Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
acee9d6fb5
commit
6ba7c824e9
|
|
@ -51,7 +51,7 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend
|
|||
[Clickhouse] @gitlab-org/maintainers/clickhouse
|
||||
/db/click_house/
|
||||
/ee/db/click_house/
|
||||
/**/click(_|-)?house/
|
||||
/**/click_house/
|
||||
|
||||
## We list db/ subfolders explicitly as we don't want to match Clickhouse files
|
||||
[Database] @gitlab-org/maintainers/database
|
||||
|
|
|
|||
|
|
@ -765,41 +765,46 @@ rspec system pg14-as-if-foss clusterwide-db:
|
|||
- .clusterwide-db
|
||||
- .rails:rules:clusterwide-db
|
||||
|
||||
rspec-ee unit gitlab-duo-chat pg14:
|
||||
variables:
|
||||
REAL_AI_REQUEST: "true"
|
||||
RSPEC_RETRY_RETRY_COUNT: 0
|
||||
.rspec-ee-base-gitlab-duo:
|
||||
extends:
|
||||
- .rspec-ee-base-pg14
|
||||
- .rails:rules:ee-gitlab-duo-chat-base
|
||||
parallel:
|
||||
matrix:
|
||||
- DUO_RSPEC: ["lib/gitlab/llm/chain/agents/zero_shot/executor_real_requests_spec.rb", "support_specs/helpers/chat_qa_evaluation_helpers_spec.rb"]
|
||||
variables:
|
||||
REAL_AI_REQUEST: "true"
|
||||
|
||||
rspec-ee unit gitlab-duo-chat-zeroshot pg14:
|
||||
extends:
|
||||
- .rspec-ee-base-gitlab-duo
|
||||
- .rails:rules:ee-gitlab-duo-chat-optional
|
||||
script:
|
||||
- !reference [.base-script, script]
|
||||
- bundle exec rspec -Ispec -rspec_helper --failure-exit-code 0 --tag real_ai_request --color -- ee/spec/${DUO_RSPEC}
|
||||
- rspec_paralellized_job "--tag zeroshot_executor"
|
||||
|
||||
rspec-ee unit gitlab-duo-chat-qa-fast pg14:
|
||||
extends:
|
||||
- .rspec-ee-base-gitlab-duo
|
||||
- .rails:rules:ee-gitlab-duo-chat-fast
|
||||
script:
|
||||
- !reference [.base-script, script]
|
||||
- bundle exec rspec -Ispec -rspec_helper --color --tag fast_chat_qa_evaluation -- ee/spec/lib/gitlab/llm/chain/agents/zero_shot/qa_evaluation_spec.rb
|
||||
|
||||
rspec-ee unit gitlab-duo-chat-qa pg14:
|
||||
variables:
|
||||
REAL_AI_REQUEST: "true"
|
||||
QA_EVAL_REPORT_FILENAME: "qa_evaluation_report.md"
|
||||
RSPEC_RETRY_RETRY_COUNT: 0
|
||||
extends:
|
||||
- .rspec-ee-base-pg14
|
||||
- .rails:rules:ee-gitlab-duo-chat-base
|
||||
parallel:
|
||||
matrix:
|
||||
- DUO_RSPEC: ["qa_epic_spec.rb", "qa_issue_spec.rb"]
|
||||
- .rspec-ee-base-gitlab-duo
|
||||
- .rails:rules:ee-gitlab-duo-chat-optional
|
||||
script:
|
||||
- !reference [.base-script, script]
|
||||
- source ./scripts/utils.sh
|
||||
- install_gitlab_gem
|
||||
- bundle exec rspec -Ispec -rspec_helper --failure-exit-code 0 --tag real_ai_request --color -- ee/spec/lib/gitlab/llm/chain/agents/zero_shot/${DUO_RSPEC}
|
||||
- bundle exec rspec -Ispec -rspec_helper --failure-exit-code 0 --color --tag chat_qa_evaluation -- ee/spec/lib/gitlab/llm/chain/agents/zero_shot/qa_evaluation_spec.rb
|
||||
- ./scripts/duo_chat/reporter.rb
|
||||
artifacts:
|
||||
expire_in: 5d
|
||||
paths:
|
||||
- tmp/duo_chat/qa*.json
|
||||
- "${DUO_RSPEC}.md"
|
||||
- "${QA_EVAL_REPORT_FILENAME}"
|
||||
|
||||
rspec-ee migration pg14:
|
||||
extends:
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ include:
|
|||
- rspec_paralellized_job "--fail-fast=${RSPEC_FAIL_FAST_THRESHOLD} --tag ~quarantine --tag ~level:background_migration --tag ~click_house --tag ~real_ai_request"
|
||||
after_script:
|
||||
- echo -e "\e[0Ksection_start:`date +%s`:report_results_section[collapsed=true]\r\e[0KReport results"
|
||||
- bundle exec gem list gitlab_quality-test_tooling
|
||||
- |
|
||||
if [ "$CREATE_RAILS_TEST_FAILURE_ISSUES" == "true" ]; then
|
||||
bundle exec relate-failure-issue --input-files "rspec/rspec-*.json" --system-log-files "log" --project "gitlab-org/gitlab" --token "${TEST_FAILURES_PROJECT_TOKEN}" --related-issues-file "rspec/${CI_JOB_ID}-failed-test-issues.json";
|
||||
|
|
|
|||
|
|
@ -2125,11 +2125,20 @@
|
|||
when: never
|
||||
- if: '$VERTEX_AI_CREDENTIALS == null'
|
||||
when: never
|
||||
|
||||
.rails:rules:ee-gitlab-duo-chat-optional:
|
||||
rules:
|
||||
- !reference [".rails:rules:ee-gitlab-duo-chat-base", rules]
|
||||
- <<: *if-merge-request
|
||||
changes: *ai-patterns
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
.rails:rules:ee-gitlab-duo-chat-fast:
|
||||
rules:
|
||||
- !reference [".rails:rules:ee-gitlab-duo-chat-base", rules]
|
||||
- <<: *if-merge-request
|
||||
changes: *ai-patterns
|
||||
|
||||
.rails:rules:as-if-foss-migration:
|
||||
rules:
|
||||
- !reference [".strict-ee-only-rules", rules]
|
||||
|
|
|
|||
|
|
@ -141,7 +141,6 @@ Layout/FirstHashElementIndentation:
|
|||
- 'qa/qa/resource/snippet.rb'
|
||||
- 'qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb'
|
||||
- 'qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/open_web_ide_from_diff_tab_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2823,7 +2823,6 @@ Layout/LineLength:
|
|||
- 'qa/qa/page/project/import/repo_by_url.rb'
|
||||
- 'qa/qa/page/project/registry/show.rb'
|
||||
- 'qa/qa/page/project/settings/protected_branches.rb'
|
||||
- 'qa/qa/page/project/web_ide/edit.rb'
|
||||
- 'qa/qa/resource/file.rb'
|
||||
- 'qa/qa/resource/protected_branch.rb'
|
||||
- 'qa/qa/resource/registry_repository.rb'
|
||||
|
|
@ -2887,10 +2886,6 @@ Layout/LineLength:
|
|||
- 'qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/create_first_file_in_web_ide_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/link_to_line_in_web_ide_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/open_web_ide_from_diff_tab_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/review_merge_request_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb'
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@ RSpec/InstanceVariable:
|
|||
- 'ee/spec/views/projects/security/dast_site_profiles/new.html.haml_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/add_file_template_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/link_to_line_in_web_ide_spec.rb'
|
||||
- 'qa/qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb'
|
||||
- 'qa/qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb'
|
||||
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb'
|
||||
|
|
|
|||
|
|
@ -924,7 +924,6 @@ Style/IfUnlessModifier:
|
|||
- 'qa/qa/page/mattermost/login.rb'
|
||||
- 'qa/qa/page/page_concern.rb'
|
||||
- 'qa/qa/page/project/settings/deploy_tokens.rb'
|
||||
- 'qa/qa/page/project/web_ide/edit.rb'
|
||||
- 'qa/qa/page/view.rb'
|
||||
- 'qa/qa/resource/registry_repository.rb'
|
||||
- 'qa/qa/resource/repository/push.rb'
|
||||
|
|
|
|||
|
|
@ -2801,7 +2801,6 @@ Style/InlineDisableAnnotation:
|
|||
- 'qa/qa/page/component/issuable/sidebar.rb'
|
||||
- 'qa/qa/page/component/new_snippet.rb'
|
||||
- 'qa/qa/page/component/visibility_setting.rb'
|
||||
- 'qa/qa/page/component/web_ide/web_terminal_panel.rb'
|
||||
- 'qa/qa/page/file/show.rb'
|
||||
- 'qa/qa/page/layout/banner.rb'
|
||||
- 'qa/qa/page/merge_request/show.rb'
|
||||
|
|
|
|||
|
|
@ -52,5 +52,4 @@ Style/SoleNestedConditional:
|
|||
- 'lib/mattermost/session.rb'
|
||||
- 'lib/object_storage/direct_upload.rb'
|
||||
- 'qa/qa/flow/login.rb'
|
||||
- 'qa/qa/page/project/web_ide/edit.rb'
|
||||
- 'spec/spec_helper.rb'
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ Style/SymbolProc:
|
|||
- 'qa/qa/resource/project_snippet.rb'
|
||||
- 'qa/qa/runtime/ip_address.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb'
|
||||
- 'qa/qa/specs/features/browser_ui/3_create/web_ide_old/review_merge_request_spec.rb'
|
||||
- 'qa/qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb'
|
||||
- 'qa/qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb'
|
||||
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb'
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default {
|
|||
<header
|
||||
class="gl-my-5 gl-display-flex gl-align-items-flex-start gl-flex-wrap gl-justify-content-space-between"
|
||||
>
|
||||
<h1 v-if="$scopedSlots.title" class="gl-my-0 gl-font-size-h1 header-title">
|
||||
<h1 v-if="$scopedSlots.title" class="gl-mt-0 gl-mb-3 gl-font-size-h1 header-title">
|
||||
<slot name="title"></slot>
|
||||
</h1>
|
||||
<div v-if="$scopedSlots.actions" class="gl-display-flex gl-gap-3">
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ export default {
|
|||
:aria-label="s__('IDE|Edit')"
|
||||
data-container="body"
|
||||
data-placement="right"
|
||||
data-qa-selector="edit_mode_tab"
|
||||
data-testid="edit-mode-button"
|
||||
type="button"
|
||||
class="ide-sidebar-link js-ide-edit-mode"
|
||||
|
|
@ -80,7 +79,6 @@ export default {
|
|||
:aria-label="s__('IDE|Commit')"
|
||||
data-container="body"
|
||||
data-placement="right"
|
||||
data-qa-selector="commit_mode_tab"
|
||||
data-testid="commit-mode-button"
|
||||
type="button"
|
||||
class="ide-sidebar-link js-ide-commit-mode"
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ export default {
|
|||
:value="$options.commitToCurrentBranch"
|
||||
:disabled="!canPushToBranch"
|
||||
:title="$options.currentBranchPermissionsTooltip"
|
||||
data-qa-selector="commit_to_current_branch_radio_container"
|
||||
>
|
||||
<span class="ide-option-label">
|
||||
<gl-sprintf :message="s__('IDE|Commit to %{branchName} branch')">
|
||||
|
|
|
|||
|
|
@ -159,7 +159,6 @@ export default {
|
|||
category="primary"
|
||||
variant="confirm"
|
||||
block
|
||||
data-qa-selector="begin_commit_button"
|
||||
data-testid="begin-commit-button"
|
||||
@click="beginCommit"
|
||||
>
|
||||
|
|
@ -187,7 +186,6 @@ export default {
|
|||
:disabled="commitButtonDisabled"
|
||||
:loading="submitCommitLoading"
|
||||
data-testid="commit-button"
|
||||
data-qa-selector="commit_button"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
type="submit"
|
||||
|
|
|
|||
|
|
@ -86,11 +86,7 @@ export default {
|
|||
role="button"
|
||||
@click="openFileInEditor"
|
||||
>
|
||||
<span
|
||||
class="multi-file-commit-list-file-path d-flex align-items-center"
|
||||
data-qa-selector="file_to_commit_content"
|
||||
:data-qa-file-name="file.name"
|
||||
>
|
||||
<span class="multi-file-commit-list-file-path d-flex align-items-center">
|
||||
<file-icon :file-name="file.name" class="gl-mr-3" />
|
||||
<template v-if="file.prevName && file.prevName !== file.name">
|
||||
{{ file.prevName }} →
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ export default {
|
|||
:placeholder="placeholder"
|
||||
:value="text"
|
||||
class="note-textarea ide-commit-message-textarea"
|
||||
data-qa-selector="ide_commit_message_field"
|
||||
dir="auto"
|
||||
name="commit-message"
|
||||
@scroll="handleScroll"
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ export default {
|
|||
:value="value"
|
||||
:disabled="disabled"
|
||||
name="commit-action"
|
||||
data-qa-selector="commit_type_radio"
|
||||
@change="updateCommitAction(value)"
|
||||
>
|
||||
<span v-if="label" class="ide-option-label">
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-alert
|
||||
data-qa-selector="flash_alert"
|
||||
variant="danger"
|
||||
:dismissible="canDismiss"
|
||||
:primary-button-text="message.actionText"
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ export default {
|
|||
<div
|
||||
class="gl-display-flex gl-align-items-center ide-file-templates gl-relative gl-z-index-1"
|
||||
data-testid="file-templates-bar"
|
||||
data-qa-selector="file_templates_container"
|
||||
>
|
||||
<strong class="gl-mr-3"> {{ $options.i18n.barLabel }} </strong>
|
||||
<gl-dropdown
|
||||
|
|
@ -102,12 +101,11 @@ export default {
|
|||
<gl-dropdown
|
||||
v-if="showTemplatesDropdown"
|
||||
class="gl-mr-6"
|
||||
data-qa-selector="file_template_dropdown"
|
||||
:text="$options.i18n.templateListDropdownLabel"
|
||||
@show="fetchTemplateTypes"
|
||||
>
|
||||
<template #header>
|
||||
<gl-search-box-by-type v-model.trim="search" data-qa-selector="dropdown_filter_input" />
|
||||
<gl-search-box-by-type v-model.trim="search" />
|
||||
</template>
|
||||
<div>
|
||||
<gl-loading-icon v-if="isLoading" />
|
||||
|
|
|
|||
|
|
@ -186,7 +186,6 @@ export default {
|
|||
category="primary"
|
||||
:title="__('New file')"
|
||||
:aria-label="__('New file')"
|
||||
data-qa-selector="first_file_button"
|
||||
@click="createNewFile()"
|
||||
>
|
||||
{{ __('New file') }}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,7 @@ export default {
|
|||
/>
|
||||
<span class="ide-sidebar-project-title">
|
||||
<span class="sidebar-context-title"> {{ project.name }} </span>
|
||||
<span
|
||||
class="sidebar-context-title text-secondary"
|
||||
data-qa-selector="project_path_content"
|
||||
:data-qa-project-path="project.path_with_namespace"
|
||||
>
|
||||
<span class="sidebar-context-title text-secondary">
|
||||
{{ project.path_with_namespace }}
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ export default {
|
|||
:title="tab.title"
|
||||
:aria-label="tab.title"
|
||||
:class="buttonClasses(tab)"
|
||||
:data-qa-selector="`${tab.title.toLowerCase()}_tab_button`"
|
||||
class="ide-sidebar-link"
|
||||
type="button"
|
||||
@click="clickTab($event, tab)"
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ export default {
|
|||
:href="getCommitPath(lastCommit.short_id)"
|
||||
class="commit-sha"
|
||||
data-testid="commit-sha-content"
|
||||
data-qa-selector="commit_sha_content"
|
||||
>{{ lastCommit.short_id }}</a
|
||||
>
|
||||
by
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ export default {
|
|||
:show-label="false"
|
||||
class="gl-display-flex gl-border-0 gl-p-0 gl-mr-5"
|
||||
icon="doc-new"
|
||||
data-qa-selector="new_file_button"
|
||||
@click="createNewFile()"
|
||||
/>
|
||||
<upload
|
||||
|
|
@ -75,7 +74,6 @@ export default {
|
|||
:show-label="false"
|
||||
class="gl-display-flex gl-border-0 gl-p-0"
|
||||
icon="folder-new"
|
||||
data-qa-selector="new_directory_button"
|
||||
@click="createNewFolder()"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ide-file-list" data-qa-selector="file_list_container">
|
||||
<div class="ide-file-list">
|
||||
<template v-if="showLoading">
|
||||
<div v-for="n in 3" :key="n" class="multi-file-loading-container">
|
||||
<gl-skeleton-loader />
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ export default {
|
|||
:aria-label="__('Create new file or directory')"
|
||||
type="button"
|
||||
class="rounded border-0 d-flex ide-entry-dropdown-toggle"
|
||||
data-qa-selector="dropdown_button"
|
||||
@click.stop="openDropdown()"
|
||||
>
|
||||
<gl-icon name="ellipsis_v" />
|
||||
|
|
@ -100,7 +99,6 @@ export default {
|
|||
class="d-flex"
|
||||
icon="pencil"
|
||||
icon-classes="mr-2"
|
||||
data-qa-selector="rename_move_button"
|
||||
@click="createNewItem($options.modalTypes.rename)"
|
||||
/>
|
||||
</li>
|
||||
|
|
@ -110,7 +108,6 @@ export default {
|
|||
class="d-flex"
|
||||
icon="remove"
|
||||
icon-classes="mr-2"
|
||||
data-qa-selector="delete_button"
|
||||
@click="deleteEntry(path)"
|
||||
/>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ export default {
|
|||
<gl-modal
|
||||
ref="modal"
|
||||
modal-id="ide-new-entry"
|
||||
data-qa-selector="new_file_modal"
|
||||
data-testid="ide-new-entry"
|
||||
:title="modalTitle"
|
||||
size="lg"
|
||||
|
|
@ -179,11 +178,7 @@ export default {
|
|||
:placeholder="placeholder"
|
||||
/>
|
||||
</form>
|
||||
<ul
|
||||
v-if="isCreatingNewFile"
|
||||
class="file-templates gl-mt-3 list-inline"
|
||||
data-qa-selector="template_list_content"
|
||||
>
|
||||
<ul v-if="isCreatingNewFile" class="file-templates gl-mt-3 list-inline">
|
||||
<li v-for="(template, index) in templateTypes" :key="index" class="list-inline-item">
|
||||
<gl-button
|
||||
variant="dashed"
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ export default {
|
|||
type="file"
|
||||
class="hidden"
|
||||
multiple
|
||||
data-qa-selector="file_upload_field"
|
||||
@change="openFile"
|
||||
/>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -74,11 +74,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="`ide-${side}-sidebar`"
|
||||
:data-qa-selector="`ide_${side}_sidebar`"
|
||||
class="multi-file-commit-panel ide-sidebar"
|
||||
>
|
||||
<div :class="`ide-${side}-sidebar`" class="multi-file-commit-panel ide-sidebar">
|
||||
<div
|
||||
v-show="isOpen"
|
||||
:class="`ide-${side}-sidebar-${currentView}`"
|
||||
|
|
|
|||
|
|
@ -543,7 +543,6 @@ export default {
|
|||
'is-added': file.tempFile,
|
||||
}"
|
||||
class="multi-file-editor-holder"
|
||||
data-qa-selector="editor_container"
|
||||
data-testid="editor-container"
|
||||
:data-editor-loading="isEditorLoading"
|
||||
@focusout="triggerFilesChange"
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@ export default {
|
|||
:placeholder="placeholder"
|
||||
:value="text"
|
||||
class="gl-absolute gl-w-full gl-h-full gl-z-index-2 gl-font-monospace p-0 gl-outline-0 gl-bg-transparent gl-border-0"
|
||||
data-qa-selector="ide_commit_message_field"
|
||||
dir="auto"
|
||||
name="commit-message"
|
||||
@scroll="handleScroll"
|
||||
|
|
|
|||
|
|
@ -53,13 +53,7 @@ export default {
|
|||
<template v-else>
|
||||
<p>{{ __('Run tests against your code live using the Web Terminal') }}</p>
|
||||
<p>
|
||||
<gl-button
|
||||
:disabled="!isValid"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
data-qa-selector="start_web_terminal_button"
|
||||
@click="onStart"
|
||||
>
|
||||
<gl-button :disabled="!isValid" category="primary" variant="confirm" @click="onStart">
|
||||
{{ __('Start Web Terminal') }}
|
||||
</gl-button>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export default {
|
|||
<template>
|
||||
<div class="d-flex flex-column flex-fill min-height-0 pr-3">
|
||||
<div class="top-bar d-flex border-left-0 align-items-center">
|
||||
<div v-if="loadingText" data-qa-selector="loading_container">
|
||||
<div v-if="loadingText">
|
||||
<gl-loading-icon size="sm" :inline="true" />
|
||||
<span>{{ loadingText }}</span>
|
||||
</div>
|
||||
|
|
@ -113,7 +113,6 @@ export default {
|
|||
ref="terminal"
|
||||
class="ide-terminal-trace flex-fill min-height-0 w-100"
|
||||
:data-project-path="terminalPath"
|
||||
data-qa-selector="terminal_screen"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ export default {
|
|||
<label class="gl-mb-1 gl-mr-2">
|
||||
{{ $options.i18n.ciCatalogLabel }}
|
||||
</label>
|
||||
<gl-badge size="sm" variant="info"> {{ $options.i18n.badgeText }} </gl-badge>
|
||||
<gl-badge size="sm" variant="neutral"> {{ $options.i18n.badgeText }} </gl-badge>
|
||||
</div>
|
||||
<gl-sprintf :message="$options.i18n.ciCatalogHelpText">
|
||||
<template #link="{ content }">
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ export default {
|
|||
:title="tooltipTitle"
|
||||
:class="{ 'ml-auto': isCentered }"
|
||||
class="file-changed-icon d-inline-block"
|
||||
data-qa-selector="changed_file_icon_content"
|
||||
:data-qa-title="tooltipTitle"
|
||||
>
|
||||
<gl-icon v-if="showIcon" :name="changedIcon" :size="size" :class="changedIconClass" />
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="preview-container" data-qa-selector="preview_container">
|
||||
<div class="preview-container">
|
||||
<image-viewer v-if="type === 'image'" :path="path" :file-size="fileSize" />
|
||||
<markdown-viewer
|
||||
v-if="type === 'markdown'"
|
||||
|
|
|
|||
|
|
@ -41,14 +41,7 @@ export default {
|
|||
{{ fileName }}
|
||||
<template v-if="fileSize > 0"> ({{ fileSizeReadable }}) </template>
|
||||
</p>
|
||||
<a
|
||||
:href="path"
|
||||
class="btn btn-default"
|
||||
rel="nofollow"
|
||||
:download="fileName"
|
||||
target="_blank"
|
||||
data-qa-selector="download_button"
|
||||
>
|
||||
<a :href="path" class="btn btn-default" rel="nofollow" :download="fileName" target="_blank">
|
||||
<gl-icon :size="16" name="download" class="float-left gl-mr-3" />
|
||||
{{ __('Download') }}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div data-testid="image-viewer" data-qa-selector="image_viewer_container">
|
||||
<div data-testid="image-viewer">
|
||||
<div :class="innerCssClasses" class="position-relative">
|
||||
<img ref="contentImg" :src="safePath" @load="onImgLoad" />
|
||||
<slot
|
||||
|
|
|
|||
|
|
@ -90,12 +90,6 @@ export default {
|
|||
<svg v-else-if="!folder" :key="spriteHref" :class="[iconSizeClass, cssClasses]">
|
||||
<use :href="spriteHref" />
|
||||
</svg>
|
||||
<gl-icon
|
||||
v-else
|
||||
:name="folderIconName"
|
||||
:size="size"
|
||||
class="folder-icon"
|
||||
data-qa-selector="folder_icon_content"
|
||||
/>
|
||||
<gl-icon v-else :name="folderIconName" :size="size" class="folder-icon" />
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -132,11 +132,7 @@ export default {
|
|||
@click="clickFile"
|
||||
@mouseleave="$emit('mouseleave', $event)"
|
||||
>
|
||||
<div
|
||||
class="file-row-name-container"
|
||||
data-qa-selector="file_row_container"
|
||||
:data-qa-file-name="file.name"
|
||||
>
|
||||
<div class="file-row-name-container">
|
||||
<span
|
||||
ref="textOutput"
|
||||
class="file-row-name"
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ export default {
|
|||
attributes: {
|
||||
href: this.forkPath,
|
||||
variant: 'confirm',
|
||||
'data-qa-selector': 'fork_project_button',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -94,7 +93,6 @@ export default {
|
|||
<template>
|
||||
<gl-modal
|
||||
:visible="visible"
|
||||
data-qa-selector="confirm_fork_modal"
|
||||
:modal-id="modalId"
|
||||
:title="$options.i18n.title"
|
||||
:action-primary="btnActions.primary"
|
||||
|
|
|
|||
|
|
@ -164,10 +164,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
params.delete(:domain_denylist_raw) if params[:domain_denylist]
|
||||
params.delete(:domain_allowlist_raw) if params[:domain_allowlist]
|
||||
|
||||
if params[:application_setting].key?(:user_email_lookup_limit)
|
||||
params[:application_setting][:search_rate_limit] ||= params[:application_setting][:user_email_lookup_limit]
|
||||
end
|
||||
|
||||
params[:application_setting].permit(visible_application_setting_attributes)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,12 @@ module Ci
|
|||
belongs_to :runner
|
||||
belongs_to :trigger_request
|
||||
belongs_to :erased_by, class_name: 'User'
|
||||
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :builds
|
||||
belongs_to :pipeline,
|
||||
->(build) { in_partition(build) },
|
||||
class_name: 'Ci::Pipeline',
|
||||
foreign_key: :commit_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :builds
|
||||
|
||||
RUNNER_FEATURES = {
|
||||
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? },
|
||||
|
|
|
|||
|
|
@ -14,9 +14,14 @@ module Ci
|
|||
self.table_name = 'p_ci_builds_metadata'
|
||||
self.primary_key = 'id'
|
||||
|
||||
query_constraints :id, :partition_id
|
||||
partitionable scope: :build, partitioned: true
|
||||
|
||||
belongs_to :build, class_name: 'CommitStatus'
|
||||
belongs_to :build, # rubocop: disable Rails/InverseOf -- this relation is not present on CommitStatus
|
||||
->(metadata) { in_partition(metadata) },
|
||||
partition_foreign_key: :partition_id,
|
||||
class_name: 'CommitStatus'
|
||||
|
||||
belongs_to :project
|
||||
|
||||
before_create :set_build_project
|
||||
|
|
@ -42,6 +47,10 @@ module Ci
|
|||
job_timeout_source: 4
|
||||
}
|
||||
|
||||
def self.use_partition_id_filter?
|
||||
Ci::Pipeline.use_partition_id_filter?
|
||||
end
|
||||
|
||||
def update_timeout_state
|
||||
timeout = timeout_with_highest_precedence
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ module Ci
|
|||
save!
|
||||
end
|
||||
|
||||
# Triggered in Ci::Catalog::Resources::Version and Release model callbacks.
|
||||
def update_latest_released_at!
|
||||
update!(latest_released_at: versions.latest&.released_at)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# These columns are denormalized from the `projects` table. We first sync these
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ module Ci
|
|||
|
||||
delegate :name, :description, :tag, :sha, :released_at, :author_id, to: :release
|
||||
|
||||
after_destroy :update_catalog_resource
|
||||
after_save :update_catalog_resource
|
||||
|
||||
class << self
|
||||
# In the future, we should support semantic versioning.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/427286
|
||||
|
|
@ -110,6 +113,12 @@ module Ci
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_catalog_resource
|
||||
catalog_resource.update_latest_released_at!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -80,31 +80,31 @@ module Ci
|
|||
# Ci:Job models. With that epic, we aim to replace `statuses` with `jobs`.
|
||||
#
|
||||
# DEPRECATED:
|
||||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :latest_statuses_ordered_by_stage, -> { latest.order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :latest_statuses, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :statuses_order_id_desc, -> { order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id,
|
||||
inverse_of: :pipeline
|
||||
has_many :bridges, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :generic_commit_statuses, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus'
|
||||
has_many :statuses, ->(pipeline) { in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :processables, ->(pipeline) { in_partition(pipeline) }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :latest_statuses_ordered_by_stage, -> (pipeline) { latest.in_partition(pipeline).order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :latest_statuses, ->(pipeline) { latest.in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :statuses_order_id_desc, ->(pipeline) { in_partition(pipeline).order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id,
|
||||
inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :bridges, ->(pipeline) { in_partition(pipeline) }, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :builds, ->(pipeline) { in_partition(pipeline) }, foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :generic_commit_statuses, ->(pipeline) { in_partition(pipeline) }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus', partition_foreign_key: :partition_id
|
||||
#
|
||||
# NEW:
|
||||
has_many :all_jobs, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :current_jobs, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :all_processable_jobs, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :current_processable_jobs, -> { latest }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :all_jobs, ->(pipeline) { in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :current_jobs, ->(pipeline) { latest.in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :all_processable_jobs, ->(pipeline) { in_partition(pipeline) }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
has_many :current_processable_jobs, ->(pipeline) { latest.in_partition(pipeline) }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id
|
||||
|
||||
has_many :job_artifacts, through: :builds
|
||||
has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks
|
||||
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id, inverse_of: :pipeline # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :variables, class_name: 'Ci::PipelineVariable'
|
||||
has_many :latest_builds, -> { latest.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build'
|
||||
has_many :latest_builds, ->(pipeline) { in_partition(pipeline).latest.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build'
|
||||
has_many :downloadable_artifacts, -> do
|
||||
not_expired.or(where_exists(Ci::Pipeline.artifacts_locked.where("#{Ci::Pipeline.quoted_table_name}.id = #{Ci::Build.quoted_table_name}.commit_id"))).downloadable.with_job
|
||||
end, through: :latest_builds, source: :job_artifacts
|
||||
has_many :latest_successful_jobs, -> { latest.success.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Processable'
|
||||
has_many :latest_successful_jobs, ->(pipeline) { in_partition(pipeline).latest.success.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Processable'
|
||||
|
||||
has_many :messages, class_name: 'Ci::PipelineMessage', inverse_of: :pipeline
|
||||
|
||||
|
|
@ -113,14 +113,14 @@ module Ci
|
|||
has_many :merge_requests_as_head_pipeline, foreign_key: :head_pipeline_id, class_name: 'MergeRequest',
|
||||
inverse_of: :head_pipeline
|
||||
|
||||
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
|
||||
has_many :failed_builds, -> { latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build',
|
||||
has_many :pending_builds, ->(pipeline) { in_partition(pipeline).pending }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
|
||||
has_many :failed_builds, ->(pipeline) { in_partition(pipeline).latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build',
|
||||
inverse_of: :pipeline
|
||||
has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
|
||||
has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus',
|
||||
has_many :retryable_builds, ->(pipeline) { in_partition(pipeline).latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
|
||||
has_many :cancelable_statuses, ->(pipeline) { in_partition(pipeline).cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus',
|
||||
inverse_of: :pipeline
|
||||
has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Processable', inverse_of: :pipeline
|
||||
has_many :scheduled_actions, -> { latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
|
||||
has_many :manual_actions, ->(pipeline) { in_partition(pipeline).latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Processable', inverse_of: :pipeline
|
||||
has_many :scheduled_actions, ->(pipeline) { in_partition(pipeline).latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
|
||||
|
||||
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: :auto_canceled_by_id,
|
||||
inverse_of: :auto_canceled_by
|
||||
|
|
@ -605,6 +605,12 @@ module Ci
|
|||
::Gitlab::Ci::PipelineObjectHierarchy.new(relation, options: options)
|
||||
end
|
||||
|
||||
def self.use_partition_id_filter?
|
||||
::Gitlab::SafeRequestStore.fetch(:ci_builds_partition_id_query_filter) do
|
||||
::Feature.enabled?(:ci_builds_partition_id_query_filter)
|
||||
end
|
||||
end
|
||||
|
||||
def uses_needs?
|
||||
processables.where(scheduling_type: :dag).any?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,19 +21,45 @@ module Ci
|
|||
belongs_to :project
|
||||
belongs_to :pipeline
|
||||
|
||||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id, inverse_of: :ci_stage
|
||||
has_many :latest_statuses, -> { ordered.latest },
|
||||
has_many :statuses,
|
||||
->(stage) { in_partition(stage) },
|
||||
class_name: 'CommitStatus',
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :retried_statuses, -> { ordered.retried },
|
||||
has_many :latest_statuses,
|
||||
->(stage) { in_partition(stage).ordered.latest },
|
||||
class_name: 'CommitStatus',
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :retried_statuses,
|
||||
->(stage) { in_partition(stage).ordered.retried },
|
||||
class_name: 'CommitStatus',
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :processables,
|
||||
->(stage) { in_partition(stage) },
|
||||
class_name: 'Ci::Processable',
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :builds,
|
||||
->(stage) { in_partition(stage) },
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :bridges,
|
||||
->(stage) { in_partition(stage) },
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :generic_commit_statuses,
|
||||
->(stage) { in_partition(stage) },
|
||||
foreign_key: :stage_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :ci_stage
|
||||
has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id, inverse_of: :ci_stage
|
||||
has_many :builds, foreign_key: :stage_id, inverse_of: :ci_stage
|
||||
has_many :bridges, foreign_key: :stage_id, inverse_of: :ci_stage
|
||||
has_many :generic_commit_statuses, foreign_key: :stage_id, inverse_of: :ci_stage
|
||||
|
||||
scope :ordered, -> { order(position: :asc) }
|
||||
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
|
||||
|
|
@ -107,6 +133,10 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def self.use_partition_id_filter?
|
||||
Ci::Pipeline.use_partition_id_filter?
|
||||
end
|
||||
|
||||
def set_status(new_status)
|
||||
retry_optimistic_lock(self, name: 'ci_stage_set_status') do
|
||||
case new_status
|
||||
|
|
|
|||
|
|
@ -25,11 +25,12 @@ class CommitStatus < Ci::ApplicationRecord
|
|||
self.sequence_name = :ci_builds_id_seq
|
||||
self.primary_key = :id
|
||||
|
||||
query_constraints :id, :partition_id
|
||||
partitionable scope: :pipeline, partitioned: true
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :statuses
|
||||
belongs_to :pipeline, ->(build) { in_partition(build) }, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :statuses, partition_foreign_key: :partition_id
|
||||
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline', inverse_of: :auto_canceled_jobs
|
||||
belongs_to :ci_stage, class_name: 'Ci::Stage', foreign_key: :stage_id
|
||||
|
||||
|
|
@ -233,6 +234,10 @@ class CommitStatus < Ci::ApplicationRecord
|
|||
false
|
||||
end
|
||||
|
||||
def self.use_partition_id_filter?
|
||||
Ci::Pipeline.use_partition_id_filter?
|
||||
end
|
||||
|
||||
def locking_enabled?
|
||||
will_save_change_to_status?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ module Ci
|
|||
|
||||
included do
|
||||
has_one :metadata,
|
||||
->(build) { where(partition_id: build.partition_id) },
|
||||
class_name: 'Ci::BuildMetadata',
|
||||
foreign_key: :build_id,
|
||||
partition_foreign_key: :partition_id,
|
||||
inverse_of: :build,
|
||||
autosave: true
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ module Ci
|
|||
before_validation :set_partition_id, on: :create
|
||||
validates :partition_id, presence: true
|
||||
|
||||
scope :in_partition, ->(id) do
|
||||
where(partition_id: (id.respond_to?(:partition_id) ? id.partition_id : id))
|
||||
end
|
||||
|
||||
def set_partition_id
|
||||
return if partition_id_changed? && partition_id.present?
|
||||
return unless partition_scope_value
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ class Release < ApplicationRecord
|
|||
accepts_nested_attributes_for :links, allow_destroy: true
|
||||
|
||||
before_create :set_released_at
|
||||
# TODO: Remove this callback after catalog_resource.released_at is denormalized. See https://gitlab.com/gitlab-org/gitlab/-/issues/430117.
|
||||
after_update :update_catalog_resource, if: -> { project.catalog_resource && saved_change_to_released_at? }
|
||||
after_destroy :update_catalog_resource, if: -> { project.catalog_resource }
|
||||
|
||||
validates :project, :tag, presence: true
|
||||
validates :author_id, presence: true, on: :create
|
||||
|
|
@ -168,6 +171,10 @@ class Release < ApplicationRecord
|
|||
order_created_desc
|
||||
end
|
||||
end
|
||||
|
||||
def update_catalog_resource
|
||||
project.catalog_resource.update_latest_released_at!
|
||||
end
|
||||
end
|
||||
|
||||
Release.prepend_mod_with('Release')
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ module Packages
|
|||
attr_reader :package_file
|
||||
|
||||
def valid_package_file?
|
||||
package_file &&
|
||||
package_file.package&.debian? &&
|
||||
package_file.file.size > 0 # rubocop:disable Style/ZeroLengthPredicate
|
||||
package_file && package_file.package&.debian? && !package_file.file.empty_size?
|
||||
end
|
||||
|
||||
def file_type_basic
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ module Packages
|
|||
private
|
||||
|
||||
def valid_package_file?
|
||||
@package_file &&
|
||||
@package_file.package&.helm? &&
|
||||
@package_file.file.size > 0 # rubocop:disable Style/ZeroLengthPredicate
|
||||
@package_file && @package_file.package&.helm? && !@package_file.file.empty_size?
|
||||
end
|
||||
|
||||
def metadata
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ module Packages
|
|||
attr_reader :package_file
|
||||
|
||||
def valid_package_file?
|
||||
package_file &&
|
||||
package_file.package&.nuget? &&
|
||||
package_file.file.size > 0 # rubocop:disable Style/ZeroLengthPredicate
|
||||
package_file && package_file.package&.nuget? && !package_file.file.empty_size?
|
||||
end
|
||||
|
||||
def with_zip_file(&block)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ module QuickActions
|
|||
@updates = {}
|
||||
@execution_message = {}
|
||||
|
||||
content, commands = extractor.extract_commands(content, only: only)
|
||||
content, commands = extractor.extract_commands(content, only: only, target: quick_action_target)
|
||||
extract_updates(commands)
|
||||
|
||||
[content, @updates, execution_messages_for(commands), command_names(commands)]
|
||||
|
|
@ -56,7 +56,7 @@ module QuickActions
|
|||
|
||||
@quick_action_target = quick_action_target
|
||||
|
||||
content, commands = extractor(keep_actions).extract_commands(content)
|
||||
content, commands = extractor(keep_actions).extract_commands(content, target: quick_action_target)
|
||||
commands = explain_commands(commands)
|
||||
[content, commands]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ class GitlabUploader < CarrierWave::Uploader::Base
|
|||
file.present?
|
||||
end
|
||||
|
||||
def empty_size?
|
||||
size == 0
|
||||
end
|
||||
|
||||
def cache_dir
|
||||
File.join(root, base_dir, 'tmp/cache')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_builds_partition_id_query_filter
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130073
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431361
|
||||
milestone: '16.6'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: quick_action_refactor
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130782
|
||||
rollout_issue_url:
|
||||
milestone: '16.7'
|
||||
type: development
|
||||
group: group::project management
|
||||
default_enabled: false
|
||||
|
|
@ -68,7 +68,6 @@ end
|
|||
# rubocop:disable Style/For
|
||||
# rubocop:disable Style/SlicingWithRange
|
||||
# rubocop:disable Style/GuardClause
|
||||
# rubocop:disable Style/ZeroLengthPredicate
|
||||
# rubocop:disable Cop/LineBreakAfterGuardClauses
|
||||
# rubocop:disable Layout/MultilineHashBraceLayout
|
||||
module WikiCloth
|
||||
|
|
@ -127,7 +126,7 @@ module WikiCloth
|
|||
temp = p.split("=")
|
||||
if p !~ /=/ && temp.length == 1 && p == params.last
|
||||
return p
|
||||
elsif temp.instance_of?(Array) && temp.length > 0
|
||||
elsif temp.instance_of?(Array) && !temp.empty?
|
||||
test = temp.first.strip
|
||||
default = temp[1..-1].join("=").strip if test == "#default"
|
||||
return temp[1..-1].join("=").strip if test == match || (test == "none" && match.blank?)
|
||||
|
|
@ -267,6 +266,5 @@ end
|
|||
# rubocop:enable Style/For
|
||||
# rubocop:enable Style/SlicingWithRange
|
||||
# rubocop:enable Style/GuardClause
|
||||
# rubocop:enable Style/ZeroLengthPredicate
|
||||
# rubocop:enable Cop/LineBreakAfterGuardClauses
|
||||
# rubocop:enable Layout/MultilineHashBraceLayout
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
- title: "user_email_lookup_limit API field"
|
||||
- title: "`user_email_lookup_limit` API field"
|
||||
announcement_milestone: "14.9"
|
||||
removal_milestone: "15.0"
|
||||
removal_milestone: "16.7"
|
||||
breaking_change: true
|
||||
reporter: fzimmer
|
||||
body: | # Do not modify this line, instead modify the lines below.
|
||||
The `user_email_lookup_limit` [API field](https://docs.gitlab.com/ee/api/settings.html) is deprecated and will be removed in GitLab 15.0. Until GitLab 15.0, `user_email_lookup_limit` is aliased to `search_rate_limit` and existing workflows will continue to work.
|
||||
The `user_email_lookup_limit` [API field](https://docs.gitlab.com/ee/api/settings.html) is deprecated in GitLab 14.9 and removed in GitLab 16.7. Until the feature is removed, `user_email_lookup_limit` is aliased to `search_rate_limit` and existing workflows still work.
|
||||
|
||||
Any API calls attempting to change the rate limits for `user_email_lookup_limit` should use `search_rate_limit` instead.
|
||||
Any API calls to change the rate limits for `user_email_lookup_limit` must use `search_rate_limit` instead.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
- name: Automatic claims of enterprise users
|
||||
description: | # Do not modify this line, instead modify the lines below.
|
||||
When a GitLab.com user's primary email address matches an existing verified domain, the user is automatically claimed as an enterprise user. This gives the group Owner more user management controls and visibility into the user's account. After a user becomes an enterprise user, they can only change their primary email to an email their organization owns as per its verified domains.
|
||||
stage: govern
|
||||
self-managed: false
|
||||
gitlab-com: true
|
||||
available_in: [Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/enterprise_user/#automatic-claims-of-enterprise-users
|
||||
image_url: https://about.gitlab.com/images/16_6/automatic-claims-of-enterprise-users.png
|
||||
published_at: 2023-11-16
|
||||
release: 16.6
|
||||
|
||||
- name: Minimal forking - only include the default branch
|
||||
description: | # Do not modify this line, instead modify the lines below.
|
||||
In previous versions of GitLab, when forking a repository, the fork always included all branches within the repository. Now you can create a fork with only the default branch, reducing complexity and storage space. Create minimal forks if you don't need the changes that are currently being worked on in other branches. The default method of forking will not change and continue to include all branches within the repository. The new option shows which branch is the default, so that you are aware of exactly which branch will be included in the new fork.
|
||||
stage: Create
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: [Free, Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html#create-a-fork
|
||||
image_url: https://about.gitlab.com/images/16_6/create-minimal-forking-default-branch.png
|
||||
published_at: 2023-11-16
|
||||
release: 16.6
|
||||
|
||||
- name: Improved UI for CI/CD variable management
|
||||
description: | # Do not modify this line, instead modify the lines below.
|
||||
CI/CD variables are a fundamental part of GitLab CI/CD, and we felt that we could offer a better experience for working with variables from the settings UI. So in this release we've updated the UI to use a new drawer that improves the flow of adding and editing CI/CD variables. For example, the masking validation used to only happen when you tried to save the CI/CD variable, and if it failed you'd have to restart from scratch. But now with the new drawer, you get real time validation so you can adjust on the fly without needed to redo anything! Your [feedback for this change](https://gitlab.com/gitlab-org/gitlab/-/issues/428807) is always valued and appreciated.
|
||||
stage: verify
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: [Free, Premium, Ultimate]
|
||||
documentation_link: https://docs.gitlab.com/ee/ci/variables/#define-a-cicd-variable-in-the-ui
|
||||
image_url: https://about.gitlab.com/images/16_6/ci_variables_drawer.png
|
||||
published_at: 2023-11-16
|
||||
release: 16.6
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUploadFinishedToPagesDeployments < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.6'
|
||||
milestone '16.7'
|
||||
enable_lock_retries!
|
||||
|
||||
def change
|
||||
# Existing deployments must be consider `upload_ready` For this reason,
|
||||
# Existing deployments must be considered `upload_ready` For this reason,
|
||||
# the column is created with `default: true`, and then changed to
|
||||
# `default: false` in post_migrate/20231115151449_update_pages_deployments_upload_ready_default_value.rb.
|
||||
# This way, existing deployments are set as `upload_ready: true`,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropIndexProjectTopicsOnProjectId < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
milestone '16.7'
|
||||
|
||||
INDEX_NAME = :index_project_topics_on_project_id
|
||||
TABLE_NAME = :project_topics
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index TABLE_NAME, :project_id, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UpdatePagesDeploymentsUploadReadyDefaultValue < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.6'
|
||||
milestone '16.7'
|
||||
enable_lock_retries!
|
||||
|
||||
def up
|
||||
change_column_default :pages_deployments, :upload_ready, from: true, to: false
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
b34d536dadff793f35eb1d7321fd07639c89bff40a2edf5dd51bb5db627b912c
|
||||
|
|
@ -33957,8 +33957,6 @@ CREATE INDEX index_project_statistics_on_storage_size_and_project_id ON project_
|
|||
|
||||
CREATE INDEX index_project_statistics_on_wiki_size_and_project_id ON project_statistics USING btree (wiki_size, project_id);
|
||||
|
||||
CREATE INDEX index_project_topics_on_project_id ON project_topics USING btree (project_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_project_topics_on_project_id_and_topic_id ON project_topics USING btree (project_id, topic_id);
|
||||
|
||||
CREATE INDEX index_project_topics_on_topic_id ON project_topics USING btree (topic_id);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ For information on how to control the application settings cache for an instance
|
|||
> - `delayed_project_deletion` and `delayed_group_deletion` attributes removed in GitLab 16.0.
|
||||
> - `in_product_marketing_emails_enabled` attribute [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/418137) in GitLab 16.6.
|
||||
> - `repository_storages` attribute [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/429675) in GitLab 16.6.
|
||||
> - `user_email_lookup_limit` attribute [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136886) in GitLab 16.7.
|
||||
|
||||
List the current [application settings](#list-of-settings-that-can-be-accessed-via-api-calls)
|
||||
of the GitLab instance.
|
||||
|
|
@ -160,6 +161,7 @@ these parameters:
|
|||
|
||||
> - `always_perform_delayed_deletion` feature flag [enabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332) in GitLab 15.11.
|
||||
> - `delayed_project_deletion` and `delayed_group_deletion` attributes removed in GitLab 16.0.
|
||||
> - `user_email_lookup_limit` attribute [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136886) in GitLab 16.7.
|
||||
|
||||
Use an API call to modify GitLab instance
|
||||
[application settings](#list-of-settings-that-can-be-accessed-via-api-calls).
|
||||
|
|
@ -526,7 +528,6 @@ listed in the descriptions of the relevant settings.
|
|||
| `push_event_hooks_limit` | integer | no | Maximum number of changes (branches or tags) in a single push above which webhooks and integrations are not triggered. Setting to `0` does not disable throttling. |
|
||||
| `rate_limiting_response_text` | string | no | When rate limiting is enabled via the `throttle_*` settings, send this plain text response when a rate limit is exceeded. 'Retry later' is sent if this is blank. |
|
||||
| `raw_blob_request_limit` | integer | no | Maximum number of requests per minute for each raw path (default is `300`). Set to `0` to disable throttling.|
|
||||
| `user_email_lookup_limit` | integer | no | **{warning}** **[Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80631/)** in GitLab 14.9 will be removed in 15.0. Replaced by `search_rate_limit`. Max number of requests per minute for email lookup. Default: 60. To disable throttling set to 0.|
|
||||
| `search_rate_limit` | integer | no | Max number of requests per minute for performing a search while authenticated. Default: 30. To disable throttling set to 0.|
|
||||
| `search_rate_limit_unauthenticated` | integer | no | Max number of requests per minute for performing a search while unauthenticated. Default: 10. To disable throttling set to 0.|
|
||||
| `recaptcha_enabled` | boolean | no | (**If enabled, requires:** `recaptcha_private_key` and `recaptcha_site_key`) Enable reCAPTCHA. |
|
||||
|
|
|
|||
|
|
@ -109,17 +109,28 @@ make sure a new fixture is generated and committed together with the change.
|
|||
|
||||
## Running the rspecs tagged with `real_ai_request`
|
||||
|
||||
The rspecs tagged with the metadata `real_ai_request` can be run in GitLab project's CI by triggering
|
||||
`rspec-ee unit gitlab-duo-chat`.
|
||||
The former runs with Vertex APIs enabled. The CI jobs are optional and allowed to fail to account for
|
||||
the non-deterministic nature of LLM responses.
|
||||
The following CI jobs for GitLab project run the rspecs tagged with `real_ai_request`:
|
||||
|
||||
- `rspec-ee unit gitlab-duo-chat-zeroshot`:
|
||||
the job runs `ee/spec/lib/gitlab/llm/chain/agents/zero_shot/executor_real_requests_spec.rb`.
|
||||
The job is optionally triggered and allowed to fail.
|
||||
|
||||
- `rspec-ee unit gitlab-duo-chat-qa`:
|
||||
The job runs the QA evaluation tests in
|
||||
`ee/spec/lib/gitlab/llm/chain/agents/zero_shot/qa_evaluation_spec.rb`.
|
||||
The job is optionally triggered and allowed to fail.
|
||||
|
||||
- `rspec-ee unit gitlab-duo-chat-qa-fast`:
|
||||
The job runs a single QA evaluation test from `ee/spec/lib/gitlab/llm/chain/agents/zero_shot/qa_evaluation_spec.rb`.
|
||||
The job is always run and not allowed to fail. Although there's a chance that the QA test still might fail,
|
||||
it is cheap and fast to run and intended to prevent a regression in the QA test helpers.
|
||||
|
||||
### Management of credentials and API keys for CI jobs
|
||||
|
||||
All API keys required to run the rspecs should be [masked](../../ci/variables/index.md#mask-a-cicd-variable)
|
||||
|
||||
The exception is GCP credentials as they contain characters that prevent them from being masked.
|
||||
Because `rspec-ee unit gitlab-duo-chat` needs to run on MR branches, GCP credentials cannot be added as a protected variable
|
||||
Because the CI jobs need to run on MR branches, GCP credentials cannot be added as a protected variable
|
||||
and must be added as a regular CI variable.
|
||||
For security, the GCP credentials and the associated project added to
|
||||
GitLab project's CI must not be able to access any production infrastructure and sandboxed.
|
||||
|
|
|
|||
|
|
@ -1302,6 +1302,8 @@ in the context of other subscription tiers, follow [the subscription tier](#subs
|
|||
|
||||
Use **prerequisites** when documenting the tasks that must be completed or the conditions that must be met before a user can complete a task. Do not use **requirements**.
|
||||
|
||||
**Prerequisites** must always be plural, even if the list includes only one item.
|
||||
|
||||
For more information, see [the task topic type](../topic_types/task.md).
|
||||
|
||||
For tutorial page types, use [**before you begin**](#before-you-begin) instead.
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ As a best practice, if the task requires the user to have a role other than Gues
|
|||
put the minimum role in the prerequisites. See [the Word list](../styleguide/word_list.md) for
|
||||
how to write the phrase for each role.
|
||||
|
||||
`Prerequisites` should always be plural, even if it includes only one item.
|
||||
`Prerequisites` must always be plural, even if the list includes only one item.
|
||||
|
||||
## Related topics
|
||||
|
||||
|
|
|
|||
|
|
@ -61,14 +61,14 @@ To configure your project settings in GitLab:
|
|||
- For **Jira issue regex**, [enter a regex pattern](issues.md#use-regular-expression).
|
||||
- For **Jira issue prefix**, [enter a prefix](issues.md#use-a-prefix).
|
||||
1. In the **Issues** section:
|
||||
- To [view Jira issues](issues.md#view-jira-issues) in a GitLab project, select the **Enable Jira issues** checkbox and
|
||||
- To [view Jira issues](issues.md#view-jira-issues) in GitLab, select the **Enable Jira issues** checkbox and
|
||||
enter a Jira project key. You can only view issues from a single Jira project in a GitLab project.
|
||||
|
||||
WARNING:
|
||||
When you enable this setting, all users with access to that GitLab project
|
||||
can view all issues from the Jira project you've specified.
|
||||
|
||||
- To [create Jira issues for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select the **Enable Jira issue creation from vulnerabilities** checkbox.
|
||||
- To [create Jira issues for vulnerabilities](#create-a-jira-issue-for-a-vulnerability), select the **Enable Jira issue creation from vulnerabilities** checkbox.
|
||||
|
||||
NOTE:
|
||||
You can enable this setting at the project level only.
|
||||
|
|
@ -79,6 +79,27 @@ To configure your project settings in GitLab:
|
|||
Your GitLab project can now interact with all Jira projects in your instance, and the project
|
||||
displays a Jira link that opens the Jira project.
|
||||
|
||||
## Create a Jira issue for a vulnerability **(ULTIMATE ALL)**
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Ensure the Jira issue integration is [configured](#configure-the-integration) and the
|
||||
**Enable Jira issue creation from vulnerabilities** checkbox is selected.
|
||||
- You must have a Jira user account with permission to create issues in the target project.
|
||||
|
||||
You can create a Jira issue to track any action taken to resolve or mitigate a vulnerability.
|
||||
|
||||
To create a Jira issue for a vulnerability:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Secure > Vulnerability report**.
|
||||
1. Select the vulnerability's description.
|
||||
1. Select **Create Jira issue**.
|
||||
|
||||
A Jira issue is created in the project with information from the vulnerability report.
|
||||
|
||||
To create a GitLab issue, see [Create a GitLab issue for a vulnerability](../../user/application_security/vulnerabilities/index.md#create-a-gitlab-issue-for-a-vulnerability).
|
||||
|
||||
## Create a Jira Cloud API token
|
||||
|
||||
To configure the Jira issue integration for Jira Cloud, you must have a Jira Cloud API token.
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ This table shows the capabilities available with the Jira issue integration and
|
|||
| Add time tracking to a Jira issue | **{dotted-circle}** No | **{check-circle}** Yes, time can be specified by using Jira Smart Commits |
|
||||
| Use a Git commit or merge request to transition or close a Jira issue |**{check-circle}** Yes, only a single transition type. Typically configured to close the issue by setting it to **Done** | **{check-circle}** Yes, transition to any state by using Jira Smart Commits |
|
||||
| [View a list of Jira issues](issues.md#view-jira-issues) | **{check-circle}** Yes | **{dotted-circle}** No |
|
||||
| [Create a Jira issue for a vulnerability](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability) | **{check-circle}** Yes | **{dotted-circle}** No |
|
||||
| [Create a Jira issue for a vulnerability](configure.md#create-a-jira-issue-for-a-vulnerability) | **{check-circle}** Yes | **{dotted-circle}** No |
|
||||
| Create a GitLab branch from a Jira issue | **{dotted-circle}** No | **{check-circle}** Yes, in the issue's development panel |
|
||||
| Sync GitLab deployments to Jira issues | **{dotted-circle}** No | **{check-circle}** Yes, in the issue's development panel. Mention a Jira issue ID in a GitLab merge request, branch name, or any of the last 5,000 commits made to the branch after the last successful deployment to the environment |
|
||||
|
||||
|
|
|
|||
|
|
@ -212,7 +212,3 @@ adding a comment to the Jira issue:
|
|||
|
||||
1. Refer to the [Configure GitLab](configure.md) instructions.
|
||||
1. Clear the **Enable comments** checkbox.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Create a Jira issue for a vulnerability](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability)
|
||||
|
|
|
|||
|
|
@ -16,4 +16,5 @@ issues, epics, and more.
|
|||
| [Run an agile iteration](agile_sprint/index.md) | Use group, projects, and iterations to run an agile development iteration. |
|
||||
| [Set up a single project for issue triage](issue_triage/index.md) | Use labels to set up a project for issue triage. | **{star}** |
|
||||
| [Set up issue boards for team hand-off](boards_for_teams/index.md) | Use issue boards and scoped labels to set up collaboration across many teams. | **{star}** |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Epics and Issue Boards](https://www.youtube.com/watch?v=eQUnHwbKEkY) | Find out how to use epics and issue boards for project management. | |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Epics and issue boards](https://www.youtube.com/watch?v=eQUnHwbKEkY) | Find out how to use epics and issue boards for project management. | |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Portfolio Planning - Portfolio Management](https://www.youtube.com/watch?v=d9scVJUIF4c) | Find out how manage your portfolio with requirements, issues, epics, milestones, and time tracking. | |
|
||||
|
|
|
|||
|
|
@ -1322,6 +1322,26 @@ To prepare for GitLab 15.8 and later, you should:
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="milestone-wrapper" data-milestone="16.7">
|
||||
|
||||
## GitLab 16.7
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="16.7">
|
||||
|
||||
### `user_email_lookup_limit` API field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">14.9</span>
|
||||
- Removal in GitLab <span class="milestone">16.7</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
</div>
|
||||
|
||||
The `user_email_lookup_limit` [API field](https://docs.gitlab.com/ee/api/settings.html) is deprecated in GitLab 14.9 and removed in GitLab 16.7. Until the feature is removed, `user_email_lookup_limit` is aliased to `search_rate_limit` and existing workflows still work.
|
||||
|
||||
Any API calls to change the rate limits for `user_email_lookup_limit` must use `search_rate_limit` instead.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="milestone-wrapper" data-milestone="16.6">
|
||||
|
||||
## GitLab 16.6
|
||||
|
|
@ -4569,21 +4589,6 @@ The Container Registry supports [authentication](https://gitlab.com/gitlab-org/c
|
|||
|
||||
Since it isn't used in the context of GitLab (the product), `htpasswd` authentication will be deprecated in GitLab 14.9 and removed in GitLab 15.0.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="15.0">
|
||||
|
||||
### user_email_lookup_limit API field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">14.9</span>
|
||||
- Removal in GitLab <span class="milestone">15.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
</div>
|
||||
|
||||
The `user_email_lookup_limit` [API field](https://docs.gitlab.com/ee/api/settings.html) is deprecated and will be removed in GitLab 15.0. Until GitLab 15.0, `user_email_lookup_limit` is aliased to `search_rate_limit` and existing workflows will continue to work.
|
||||
|
||||
Any API calls attempting to change the rate limits for `user_email_lookup_limit` should use `search_rate_limit` instead.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ For more information about upgrading GitLab Helm Chart, see [the release notes f
|
|||
## 16.5.0
|
||||
|
||||
- Git 2.42.0 and later is required by Gitaly. For self-compiled installations, you should use the [Git version provided by Gitaly](../../install/installation.md#git).
|
||||
- A regression may sometimes cause an [HTTP 500 error when navigating a group](https://gitlab.com/gitlab-org/gitlab/-/issues/431659). Upgrading to GitLab 16.6 or later resolves the issue.
|
||||
|
||||
### Linux package installations
|
||||
|
||||
|
|
|
|||
|
|
@ -126,17 +126,9 @@ To change a vulnerability's status from its Vulnerability Page:
|
|||
Details of the status change, including who made the change and when, are recorded in the
|
||||
vulnerability's action log.
|
||||
|
||||
## Creating an issue for a vulnerability
|
||||
## Create a GitLab issue for a vulnerability
|
||||
|
||||
From a vulnerability's page you can create an issue to track all action taken to resolve or
|
||||
mitigate it.
|
||||
|
||||
You can create either:
|
||||
|
||||
- [A GitLab issue](#create-a-gitlab-issue-for-a-vulnerability) (default).
|
||||
- [A Jira issue](#create-a-jira-issue-for-a-vulnerability).
|
||||
|
||||
### Create a GitLab issue for a vulnerability
|
||||
You can create a GitLab issue to track any action taken to resolve or mitigate a vulnerability.
|
||||
|
||||
To create a GitLab issue for a vulnerability:
|
||||
|
||||
|
|
@ -145,37 +137,14 @@ To create a GitLab issue for a vulnerability:
|
|||
1. Select the vulnerability's description.
|
||||
1. Select **Create issue**.
|
||||
|
||||
An issue is created in the project, pre-populated with information from the vulnerability report.
|
||||
The issue is then opened so you can take further action.
|
||||
A GitLab issue is created in the project with information from the vulnerability report.
|
||||
|
||||
### Create a Jira issue for a vulnerability
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [Enable Jira integration](../../../integration/jira/configure.md). The
|
||||
**Enable Jira issue creation from vulnerabilities** option must be selected as part
|
||||
of the configuration.
|
||||
- Each user must have a personal Jira user account with permission to create issues in the target
|
||||
project.
|
||||
|
||||
To create a Jira issue for a vulnerability:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Secure > Vulnerability report**.
|
||||
1. Select the vulnerability's description.
|
||||
1. Select **Create Jira issue**.
|
||||
1. If you're not already logged in to Jira, sign in.
|
||||
|
||||
The Jira issue is created and opened in a new browser tab. The **Summary** and **Description**
|
||||
fields are pre-populated from the vulnerability's details.
|
||||
|
||||
Unlike GitLab issues, the status of whether a Jira issue is open or closed does not display in the
|
||||
GitLab user interface.
|
||||
To create a Jira issue, see [Create a Jira issue for a vulnerability](../../../integration/jira/configure.md#create-a-jira-issue-for-a-vulnerability).
|
||||
|
||||
## Linking a vulnerability to GitLab and Jira issues
|
||||
|
||||
You can link a vulnerability to one or more existing [GitLab](#create-a-gitlab-issue-for-a-vulnerability)
|
||||
or [Jira](#create-a-jira-issue-for-a-vulnerability) issues. Only one linking feature is available at the same time.
|
||||
or [Jira](../../../integration/jira/configure.md#create-a-jira-issue-for-a-vulnerability) issues. Only one linking feature is available at the same time.
|
||||
Adding a link helps track the issue that resolves or mitigates a vulnerability.
|
||||
|
||||
### Link a vulnerability to existing GitLab issues
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ At the project level, the Vulnerability Report also contains:
|
|||
The **Activity** column contains icons to indicate the activity, if any, taken on the vulnerability
|
||||
in that row:
|
||||
|
||||
- Issues **{issues}**: Links to issues created for the vulnerability. For more details, read
|
||||
[Create an issue for a vulnerability](../vulnerabilities/index.md#creating-an-issue-for-a-vulnerability).
|
||||
- Issues **{issues}**: Links to issues created for the vulnerability. For more information, see
|
||||
[Create a GitLab issue for a vulnerability](../vulnerabilities/index.md#create-a-gitlab-issue-for-a-vulnerability).
|
||||
- Wrench **{admin}**: The vulnerability has been remediated.
|
||||
- False positive **{false-positive}**: The scanner determined this vulnerability to be a false
|
||||
positive.
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ GitLab in a Kubernetes cluster, you might need a different version of Kubernetes
|
|||
You can upgrade your
|
||||
Kubernetes version to a supported version at any time:
|
||||
|
||||
- 1.27 (support ends on July 22, 2024 or when 1.30 becomes supported)
|
||||
- 1.26 (support ends on March 22, 2024 or when 1.29 becomes supported)
|
||||
- 1.27 (support ends on July 18, 2024 or when 1.30 becomes supported)
|
||||
- 1.26 (support ends on March 21, 2024 or when 1.29 becomes supported)
|
||||
- 1.25 (support ends on October 22, 2023 or when 1.28 becomes supported)
|
||||
|
||||
GitLab aims to support a new minor Kubernetes version three months after its initial release. GitLab supports at least three production-ready Kubernetes minor
|
||||
|
|
|
|||
|
|
@ -77,15 +77,17 @@ example configurations for [Azure AD](../../../user/group/saml_sso/example_saml_
|
|||
|
||||
## Configure SAML Group Links
|
||||
|
||||
When SAML is enabled, users with the Maintainer or Owner role see a new menu
|
||||
item in group **Settings > SAML Group Links**. You can configure one or more
|
||||
**SAML Group Links** to map a SAML identity provider group name to a GitLab
|
||||
role. After you have configured this, members of the SAML identity provider group are added to
|
||||
the GitLab group on their next SAML sign-in. You can do this for a top-level
|
||||
group or any subgroup.
|
||||
When SAML is enabled, users with the Maintainer or Owner role
|
||||
see a new menu item in group **Settings > SAML Group Links**. You can configure one or more **SAML Group Links** to map
|
||||
a SAML identity provider group name to a GitLab role. This can be done for a top-level group or any subgroup.
|
||||
|
||||
SAML Group Sync only manages a group if that group has one or more SAML group links. If a SAML group link is created then removed, the user remains in the group until they are removed from the group in the identity provider.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Self-managed GitLab instances must have configured SAML Group Sync. GitLab.com
|
||||
instances are already configured for SAML Group Sync, and require no extra configuration.
|
||||
|
||||
To link the SAML groups:
|
||||
|
||||
1. In **SAML Group Name**, enter the value of the relevant `saml:AttributeValue`. The value entered here must exactly match the value sent in the SAML response. For some IdPs, this may be a group ID or object ID (Azure AD) instead of a friendly group name.
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ Access our [permissions](../../permissions.md) page for more information.
|
|||
When you [add a linked issue](#add-a-linked-issue), you can show that it **blocks** or
|
||||
**is blocked by** another issue.
|
||||
|
||||
Issues that block other issues have an icon (**{issue-block}**) next to their title, shown in the
|
||||
Issues blocked by other issues have an icon (**{issue-block}**) next to their title, shown in the
|
||||
issue lists and [boards](../issue_board.md).
|
||||
The icon disappears when the blocking issue is closed or their relationship is changed or
|
||||
[removed](#remove-a-linked-issue).
|
||||
|
|
|
|||
|
|
@ -322,8 +322,12 @@ An issue, merge request, or epic cannot have two scoped labels, of the form `key
|
|||
with the same `key`. If you add a new label with the same `key` but a different `value`,
|
||||
the previous `key` label is replaced with the new label.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For a video overview, see [Scoped Labels Speed Run](https://www.youtube.com/watch?v=ebyCiKMFODg).
|
||||
<div class="video-fallback">
|
||||
See the video: <a href="https://www.youtube.com/watch?v=7l7tnEva6I8">Scoped Labels - Setting up your Organization with GitLab</a>.
|
||||
</div>
|
||||
<figure class="video-container">
|
||||
<iframe src="https://www.youtube-nocookie.com/embed/7l7tnEva6I8" frameborder="0" allowfullscreen> </iframe>
|
||||
</figure>
|
||||
|
||||
### Filter by scoped labels
|
||||
|
||||
|
|
|
|||
|
|
@ -514,6 +514,7 @@ This error can happen if your merge request:
|
|||
|
||||
- Contains many diffs.
|
||||
- Is many commits behind the target branch.
|
||||
- References a Git LFS file that is locked.
|
||||
|
||||
Users in self-managed installations can request an administrator review server logs
|
||||
to determine the cause of the error. GitLab SaaS users should
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ module API
|
|||
end
|
||||
|
||||
def track_push_package_event
|
||||
if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY && params[:file].size > 0 # rubocop: disable Style/ZeroLengthPredicate
|
||||
if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY
|
||||
track_package_event('push_package', :conan, category: 'API::ConanPackages', project: project, namespace: project.namespace)
|
||||
end
|
||||
end
|
||||
|
|
@ -196,7 +196,7 @@ module API
|
|||
end
|
||||
|
||||
def create_package_file_with_type(file_type, current_package)
|
||||
unless params[:file].size == 0 # rubocop: disable Style/ZeroLengthPredicate
|
||||
unless params[:file].empty_size?
|
||||
# conan sends two upload requests, the first has no file, so we skip record creation if file.size == 0
|
||||
::Packages::Conan::CreatePackageFileService.new(
|
||||
current_package,
|
||||
|
|
@ -212,7 +212,7 @@ module API
|
|||
|
||||
current_package = find_or_create_package
|
||||
|
||||
track_push_package_event
|
||||
track_push_package_event unless params[:file].empty_size?
|
||||
|
||||
create_package_file_with_type(file_type, current_package)
|
||||
rescue ObjectStorage::RemoteStoreError => e
|
||||
|
|
|
|||
|
|
@ -23,6 +23,26 @@ module Banzai
|
|||
raise NameError, "`#{engine_from_context}` is unknown markdown engine"
|
||||
end
|
||||
|
||||
# Parses string representing a sourcepos in format
|
||||
# "start_row:start_column-end_row:end_column" into 0-based
|
||||
# attributes. For example, "1:10-14:1" becomes
|
||||
# {
|
||||
# start: { row: 0, col: 9 },
|
||||
# end: { row: 13, col: 0 }
|
||||
# }
|
||||
def parse_sourcepos(sourcepos)
|
||||
start_pos, end_pos = sourcepos&.split('-')
|
||||
start_row, start_col = start_pos&.split(':')
|
||||
end_row, end_col = end_pos&.split(':')
|
||||
|
||||
return unless start_row && start_col && end_row && end_col
|
||||
|
||||
{
|
||||
start: { row: [1, start_row.to_i].max - 1, col: [1, start_col.to_i].max - 1 },
|
||||
end: { row: [1, end_row.to_i].max - 1, col: [1, end_col.to_i].max - 1 }
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def engine(engine_from_context)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Banzai
|
||||
module Filter
|
||||
# Filter which looks for possible paragraphs with quick action lines, and allows
|
||||
# another processor to do final determination. Paragraph source position
|
||||
# is returned in `result[:quick_action_paragraphs]`.
|
||||
class QuickActionFilter < HTML::Pipeline::Filter
|
||||
def call
|
||||
result[:quick_action_paragraphs] = []
|
||||
|
||||
doc.children.xpath('self::p').each do |node|
|
||||
next unless node.attributes['data-sourcepos']
|
||||
|
||||
sourcepos = ::Banzai::Filter::MarkdownFilter.parse_sourcepos(node.attributes['data-sourcepos'].value)
|
||||
|
||||
node.children.xpath('self::text()').each do |text_node|
|
||||
next unless %r{^/}.match?(text_node.content)
|
||||
|
||||
result[:quick_action_paragraphs] <<
|
||||
{ start_line: sourcepos[:start][:row], end_line: sourcepos[:end][:row] }
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
doc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Banzai
|
||||
module Pipeline
|
||||
# Pipeline for detecting possible paragraphs with quick actions,
|
||||
# leveraging the markdown processor
|
||||
class QuickActionPipeline < BasePipeline
|
||||
def self.filters
|
||||
FilterArray[
|
||||
Filter::NormalizeSourceFilter,
|
||||
Filter::TruncateSourceFilter,
|
||||
Filter::FrontMatterFilter,
|
||||
Filter::BlockquoteFenceFilter,
|
||||
Filter::MarkdownFilter,
|
||||
Filter::QuickActionFilter
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -40,12 +40,6 @@ Metrics/BlockLength:
|
|||
Long blocks can be hard to read. Consider splitting the code into separate
|
||||
methods.
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: true
|
||||
Details: >
|
||||
Adding documentation makes it easier to figure out what a migration is
|
||||
supposed to do.
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: true
|
||||
Details: >-
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ module Gitlab
|
|||
)
|
||||
}mix
|
||||
|
||||
EXCLUSION_REGEX = %r{
|
||||
EXCLUSION_REGEX = %r{#{INLINE_CODE_REGEX} | #{HTML_BLOCK_REGEX}}mix
|
||||
EXCLUSION_REGEX_ORG = %r{
|
||||
#{CODE_REGEX} | #{INLINE_CODE_REGEX} | #{HTML_BLOCK_REGEX} | #{QUOTE_BLOCK_REGEX}
|
||||
}mix
|
||||
|
||||
|
|
@ -92,10 +93,16 @@ module Gitlab
|
|||
# commands = extractor.extract_commands(msg) #=> [['labels', '~foo ~"bar baz"']]
|
||||
# msg #=> "hello\n/labels ~foo ~"bar baz"\n\nworld"
|
||||
# ```
|
||||
def extract_commands(content, only: nil)
|
||||
# TODO: target is only needed for feature flag
|
||||
def extract_commands(content, only: nil, target: nil)
|
||||
return [content, []] unless content
|
||||
|
||||
perform_regex(content, only: only)
|
||||
actor = target&.project&.group if target.respond_to?(:project)
|
||||
if Feature.enabled?(:quick_action_refactor, actor)
|
||||
perform_regex(content, only: only)
|
||||
else
|
||||
perform_regex_org(content, only: only)
|
||||
end
|
||||
end
|
||||
|
||||
# Encloses quick action commands into code span markdown
|
||||
|
|
@ -105,7 +112,13 @@ module Gitlab
|
|||
def redact_commands(content)
|
||||
return "" unless content
|
||||
|
||||
content, _ = perform_regex(content, redact: true)
|
||||
# TODO: we don't have an actor at this point, so just check the global
|
||||
# feature flag.
|
||||
content, _ = if Feature.enabled?(:quick_action_refactor)
|
||||
perform_regex(content, redact: true)
|
||||
else
|
||||
perform_regex_org(content, redact: true)
|
||||
end
|
||||
|
||||
content
|
||||
end
|
||||
|
|
@ -119,7 +132,53 @@ module Gitlab
|
|||
content = content.dup
|
||||
content.delete!("\r")
|
||||
|
||||
content.gsub!(commands_regex(names: names, sub_names: sub_names)) do
|
||||
# use a markdown based pipeline to grab possible paragraphs that might
|
||||
# contain quick actions. This ensures they are not in HTML blocks, quote blocks,
|
||||
# or code blocks.
|
||||
pipeline = Banzai::Pipeline::QuickActionPipeline.html_pipeline
|
||||
possible_paragraphs = pipeline.call(content, {}, {})[:quick_action_paragraphs]
|
||||
|
||||
if possible_paragraphs.present?
|
||||
content_lines = content.lines
|
||||
|
||||
# Each paragraph that possibly contains quick actions must be searched. In order
|
||||
# to use the `sourcepos` information, we need to convert into individual lines,
|
||||
# and then replace the specific lines.
|
||||
possible_paragraphs.each do |possible|
|
||||
endpos = possible[:end_line]
|
||||
endpos += 1 if content_lines[endpos + 1] == "\n"
|
||||
|
||||
paragraph = content_lines[possible[:start_line]..endpos].join
|
||||
|
||||
paragraph.gsub!(commands_regex(names: names, sub_names: sub_names)) do
|
||||
command, output = if $~[:substitution]
|
||||
process_substitutions($~)
|
||||
else
|
||||
process_commands($~, redact)
|
||||
end
|
||||
|
||||
commands << command
|
||||
output
|
||||
end
|
||||
|
||||
content_lines.fill('', possible[:start_line]..endpos)
|
||||
content_lines[possible[:start_line]] = paragraph
|
||||
end
|
||||
|
||||
content = content_lines.join
|
||||
end
|
||||
|
||||
[content.rstrip, commands.reject(&:empty?)]
|
||||
end
|
||||
|
||||
def perform_regex_org(content, only: nil, redact: false)
|
||||
names = command_names(limit_to_commands: only).map(&:to_s)
|
||||
sub_names = substitution_names.map(&:to_s)
|
||||
commands = []
|
||||
content = content.dup
|
||||
content.delete!("\r")
|
||||
|
||||
content.gsub!(commands_regex(names: names, sub_names: sub_names, use_org_regex: true)) do
|
||||
command, output = if $~[:substitution]
|
||||
process_substitutions($~)
|
||||
else
|
||||
|
|
@ -176,12 +235,13 @@ module Gitlab
|
|||
# It looks something like:
|
||||
#
|
||||
# /^\/(?<cmd>close|reopen|...)(?:( |$))(?<arg>[^\/\n]*)(?:\n|$)/
|
||||
def commands_regex(names:, sub_names:)
|
||||
def commands_regex(names:, sub_names:, use_org_regex: false)
|
||||
names += ['use_org_regex'] if use_org_regex
|
||||
@commands_regex[names] ||= %r{
|
||||
#{EXCLUSION_REGEX}
|
||||
#{use_org_regex ? EXCLUSION_REGEX_ORG : EXCLUSION_REGEX}
|
||||
|
|
||||
(?:
|
||||
# Command not in a blockquote, blockcode, or HTML tag:
|
||||
# Command such as:
|
||||
# /close
|
||||
|
||||
^\/
|
||||
|
|
@ -194,7 +254,8 @@ module Gitlab
|
|||
)
|
||||
|
|
||||
(?:
|
||||
# Substitution not in a blockquote, blockcode, or HTML tag:
|
||||
# Substitution such as:
|
||||
# /shrug
|
||||
|
||||
^\/
|
||||
(?<substitution>#{Regexp.new(Regexp.union(sub_names).source, Regexp::IGNORECASE)})
|
||||
|
|
|
|||
|
|
@ -105,6 +105,10 @@ class UploadedFile
|
|||
@tempfile&.close
|
||||
end
|
||||
|
||||
def empty_size?
|
||||
size == 0
|
||||
end
|
||||
|
||||
alias_method :local_path, :path
|
||||
|
||||
def method_missing(method_name, *args, &block) #:nodoc:
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module WebIDE
|
||||
module Alert
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
view 'app/assets/javascripts/ide/components/error_message.vue' do
|
||||
element :flash_alert
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_no_alert?(message = nil)
|
||||
return has_no_element?(:flash_alert) if message.nil?
|
||||
|
||||
within_element(:flash_alert) do
|
||||
has_no_text?(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module WebIDE
|
||||
module Modal
|
||||
class CreateNewFile < Page::Base
|
||||
view 'app/assets/javascripts/ide/components/new_dropdown/modal.vue' do
|
||||
element 'file-name-field', required: true
|
||||
element :new_file_modal, required: true
|
||||
element :template_list_content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module WebIDE
|
||||
module WebTerminalPanel
|
||||
extend QA::Page::PageConcern
|
||||
|
||||
def self.prepended(base)
|
||||
super
|
||||
|
||||
base.class_eval do
|
||||
view 'app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue' do
|
||||
element :ide_right_sidebar, %q(:data-qa-selector="`ide_${side}_sidebar`") # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/ide_sidebar_nav.vue' do
|
||||
element :terminal_tab_button, %q(:data-qa-selector="`${tab.title.toLowerCase()}_tab_button`") # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/terminal/empty_state.vue' do
|
||||
element :start_web_terminal_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/terminal/terminal.vue' do
|
||||
element :loading_container
|
||||
element :terminal_screen
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_finished_loading?
|
||||
has_no_element?(:loading_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
|
||||
end
|
||||
|
||||
def has_terminal_screen?
|
||||
wait_until(reload: false) do
|
||||
within_element :terminal_screen do
|
||||
# The DOM initially just includes the :terminal_screen element
|
||||
# and then the xterm package dynamically loads when the user
|
||||
# clicks the Start Web Terminal button. If it loads succesfully
|
||||
# an element with the class `xterm` is added to the DOM.
|
||||
# The xterm is a third-party library, so we can't add a selector
|
||||
find(".xterm")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def start_web_terminal
|
||||
within_element :ide_right_sidebar do
|
||||
click_element :terminal_tab_button
|
||||
end
|
||||
|
||||
click_element :start_web_terminal_button
|
||||
|
||||
has_element? :loading_container, text: "Starting"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Project
|
||||
module WebIDE
|
||||
class Edit < Page::Base
|
||||
prepend Page::Component::WebIDE::Alert
|
||||
prepend Page::Component::WebIDE::WebTerminalPanel
|
||||
include Page::Component::DropdownFilter
|
||||
|
||||
view 'app/assets/javascripts/ide/components/activity_bar.vue' do
|
||||
element :commit_mode_tab
|
||||
element :edit_mode_tab
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/ide_status_bar.vue' do
|
||||
element :commit_sha_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/ide_tree.vue' do
|
||||
element :new_file_button, required: true
|
||||
element :new_directory_button, required: true
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/ide_tree_list.vue' do
|
||||
element :file_list_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/file_templates/bar.vue' do
|
||||
element :file_templates_container
|
||||
element :file_template_dropdown
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/file_templates/bar.vue' do
|
||||
element :dropdown_filter_input
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/commit_sidebar/actions.vue' do
|
||||
element :commit_to_current_branch_radio_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/commit_sidebar/form.vue' do
|
||||
element :begin_commit_button
|
||||
element :commit_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue' do
|
||||
element :commit_type_radio
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/repo_editor.vue' do
|
||||
element :editor_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/ide.vue' do
|
||||
element :first_file_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/file_row.vue' do
|
||||
element 'file-row-name-container'
|
||||
element :file_row_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/new_dropdown/index.vue' do
|
||||
element :dropdown_button
|
||||
element :rename_move_button
|
||||
element :delete_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/web_ide/confirm_fork_modal.vue' do
|
||||
element :fork_project_button
|
||||
element :confirm_fork_modal
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/ide_project_header.vue' do
|
||||
element :project_path_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/commit_sidebar/message_field.vue' do
|
||||
element :ide_commit_message_field
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/changed_file_icon.vue' do
|
||||
element :changed_file_icon_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/file_icon.vue' do
|
||||
element :folder_icon_content
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue' do
|
||||
element :preview_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue' do
|
||||
element :download_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue' do
|
||||
element :image_viewer_container
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/new_dropdown/upload.vue' do
|
||||
element :file_upload_field
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/ide/components/commit_sidebar/list_item.vue' do
|
||||
element :file_to_commit_content
|
||||
end
|
||||
|
||||
# Used for stablility, due to feature_caching of vscode_web_ide
|
||||
def wait_until_ide_loads
|
||||
Support::Waiter.wait_until(
|
||||
sleep_interval: 2,
|
||||
max_duration: 120,
|
||||
reload_page: page,
|
||||
retry_on_exception: true
|
||||
) do
|
||||
has_element?(:commit_mode_tab)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file?(file_name)
|
||||
within_element(:file_list_container) do
|
||||
has_element?('file-row-name-container', file_name: file_name)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_to_commit?(file_name)
|
||||
has_element?(:file_to_commit_content, file_name: file_name)
|
||||
end
|
||||
|
||||
def has_project_path?(project_path)
|
||||
has_element?(:project_path_content, project_path: project_path)
|
||||
end
|
||||
|
||||
def has_file_addition_icon?(file_name)
|
||||
within_element(:file_row_container, file_name: file_name) do
|
||||
has_element?(:changed_file_icon_content, title: 'Added')
|
||||
end
|
||||
end
|
||||
|
||||
def has_folder_icon?(file_name)
|
||||
within_element(:file_row_container, file_name: file_name) do
|
||||
has_element?(:folder_icon_content)
|
||||
end
|
||||
end
|
||||
|
||||
def has_download_button?(file_name)
|
||||
click_element(:file_row_container, file_name: file_name)
|
||||
within_element(:preview_container) do
|
||||
has_element?(:download_button)
|
||||
end
|
||||
end
|
||||
|
||||
def has_image_viewer?(file_name)
|
||||
click_element(:file_row_container, file_name: file_name)
|
||||
within_element(:preview_container) do
|
||||
has_element?(:image_viewer_container)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_content?(file_name, file_content)
|
||||
click_element(:file_row_container, file_name: file_name)
|
||||
within_element(:editor_container) do
|
||||
has_text?(file_content)
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_project
|
||||
click_element(:project_path_content, Page::Project::Show)
|
||||
end
|
||||
|
||||
def create_new_file_from_template(file_name, template)
|
||||
click_element(:new_file_button, Page::Component::WebIDE::Modal::CreateNewFile)
|
||||
|
||||
within_element(:template_list_content) do
|
||||
click_on file_name
|
||||
rescue Capybara::ElementNotFound
|
||||
raise ElementNotFound, %(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
|
||||
end
|
||||
|
||||
# Wait for the modal to fade out too
|
||||
has_no_element?(:new_file_modal)
|
||||
|
||||
wait_until(reload: false) do
|
||||
within_element(:file_templates_container) do
|
||||
click_element :file_template_dropdown
|
||||
fill_element :dropdown_filter_input, template
|
||||
|
||||
begin
|
||||
click_on template
|
||||
rescue Capybara::ElementNotFound
|
||||
raise ElementNotFound, %(Couldn't find template "#{template}" for #{file_name}. Please confirm that it exists in the list of templates.)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commit_sha
|
||||
return unless has_element?(:commit_sha_content, wait: 0)
|
||||
|
||||
find_element(:commit_sha_content).text
|
||||
end
|
||||
|
||||
def commit_changes(commit_message = nil, open_merge_request: false, wait_for_success: true)
|
||||
# Clicking :begin_commit_button switches from the
|
||||
# edit to the commit view
|
||||
click_element(:begin_commit_button)
|
||||
active_element?(:commit_mode_tab)
|
||||
|
||||
original_commit = commit_sha
|
||||
|
||||
# After clicking :begin_commit_button, there is an animation
|
||||
# that hides :begin_commit_button and shows :commit_button
|
||||
#
|
||||
# Wait for the animation to complete before clicking :commit_button
|
||||
# otherwise the click will quietly do nothing.
|
||||
wait_until(reload: false) do
|
||||
has_no_element?(:begin_commit_button) &&
|
||||
has_element?(:commit_button)
|
||||
end
|
||||
|
||||
if commit_message
|
||||
fill_element(:ide_commit_message_field, commit_message)
|
||||
end
|
||||
|
||||
if open_merge_request
|
||||
click_element(:commit_button, Page::MergeRequest::New)
|
||||
else
|
||||
# Click :commit_button and keep retrying just in case part of the
|
||||
# animation is still in process even when the buttons have the
|
||||
# expected visibility.
|
||||
commit_success = retry_until(sleep_interval: 5) do
|
||||
within_element(:commit_to_current_branch_radio_container) do
|
||||
choose_element(:commit_type_radio, true)
|
||||
end
|
||||
click_element(:commit_button) if has_element?(:commit_button)
|
||||
|
||||
break unless wait_for_success
|
||||
|
||||
# If this is the first commit, the commit SHA only appears after reloading
|
||||
wait_until(reload: true) do
|
||||
active_element?(:edit_mode_tab) && commit_sha != original_commit
|
||||
end
|
||||
end
|
||||
|
||||
if wait_for_success
|
||||
raise "The changes do not appear to have been committed successfully." unless commit_success
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_to_modified_content(content)
|
||||
finished_loading?
|
||||
modified_text_area.click
|
||||
modified_text_area.set content
|
||||
end
|
||||
|
||||
def modified_text_area
|
||||
wait_for_animated_element(:editor_container)
|
||||
within_element(:editor_container) do
|
||||
find('.modified textarea.inputarea')
|
||||
end
|
||||
end
|
||||
|
||||
def create_first_file(file_name)
|
||||
click_element(:first_file_button, Page::Component::WebIDE::Modal::CreateNewFile)
|
||||
fill_element('file-name-field', file_name)
|
||||
click_button('Create file')
|
||||
end
|
||||
|
||||
def add_file(file_name, file_text)
|
||||
click_element(:new_file_button, Page::Component::WebIDE::Modal::CreateNewFile)
|
||||
fill_element('file-name-field', file_name)
|
||||
click_button('Create file')
|
||||
wait_until(reload: false) { has_file?(file_name) }
|
||||
within_element(:editor_container) do
|
||||
find('textarea.inputarea').click.set(file_text)
|
||||
end
|
||||
end
|
||||
|
||||
def add_directory(directory_name)
|
||||
click_element(:new_directory_button, Page::Component::WebIDE::Modal::CreateNewFile)
|
||||
fill_element('file-name-field', directory_name)
|
||||
click_button('Create directory')
|
||||
wait_until(reload: false) { has_file?(directory_name) }
|
||||
end
|
||||
|
||||
def rename_file(file_name, new_file_name)
|
||||
click_element('file-row-name-container', file_name: file_name)
|
||||
click_element(:dropdown_button)
|
||||
click_element(:rename_move_button, Page::Component::WebIDE::Modal::CreateNewFile)
|
||||
fill_element('file-name-field', new_file_name)
|
||||
click_button('Rename file')
|
||||
end
|
||||
|
||||
def fork_project!
|
||||
wait_until(reload: false) do
|
||||
has_element?(:confirm_fork_modal)
|
||||
end
|
||||
click_element(:fork_project_button)
|
||||
# wait for the fork to be created
|
||||
wait_until(reload: true) do
|
||||
has_element?(:file_list_container)
|
||||
end
|
||||
end
|
||||
|
||||
def upload_file(file_path)
|
||||
within_element(:file_list_container) do
|
||||
find_element(:file_upload_field, visible: false).send_keys(file_path)
|
||||
end
|
||||
end
|
||||
|
||||
def delete_file(file_name)
|
||||
click_element('file-row-name-container', file_name: file_name)
|
||||
click_element(:dropdown_button)
|
||||
click_element(:delete_button)
|
||||
end
|
||||
|
||||
def switch_to_commit_tab
|
||||
click_element(:commit_mode_tab)
|
||||
end
|
||||
|
||||
def select_file(file_name)
|
||||
# wait for the list of files to load
|
||||
wait_until(reload: true) do
|
||||
has_element?('file-row-name-container', file_name: file_name)
|
||||
end
|
||||
click_element('file-row-name-container', file_name: file_name)
|
||||
end
|
||||
|
||||
def link_line(line_number)
|
||||
previous_url = page.current_url
|
||||
wait_for_animated_element(:editor_container)
|
||||
within_element(:editor_container) do
|
||||
find('.line-numbers', text: line_number).hover.click
|
||||
end
|
||||
wait_until(max_duration: 5, reload: false) do
|
||||
page.current_url != previous_url
|
||||
end
|
||||
page.current_url.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -208,6 +208,8 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
let(:import_status) { imported_project.project_import_status }
|
||||
|
||||
before do
|
||||
QA::Support::Helpers::ImportSource.enable('github')
|
||||
end
|
||||
|
|
@ -230,7 +232,8 @@ module QA
|
|||
status: example.exception ? "failed" : "passed",
|
||||
import_time: @import_time,
|
||||
import_finished: true,
|
||||
errors: imported_project.project_import_status[:failed_relations],
|
||||
errors: import_status[:failed_relations],
|
||||
correlation_id: import_status[:correlation_id],
|
||||
reported_stats: @stats
|
||||
}))
|
||||
end
|
||||
|
|
@ -239,7 +242,8 @@ module QA
|
|||
status: example.exception ? "failed" : "passed",
|
||||
import_time: @import_time,
|
||||
import_finished: true,
|
||||
errors: imported_project.project_import_status[:failed_relations],
|
||||
errors: import_status[:failed_relations],
|
||||
correlation_id: import_status[:correlation_id],
|
||||
reported_stats: @stats,
|
||||
source: {
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# TODO: remove this test when coverage is replaced or deemed irrelevant
|
||||
module QA
|
||||
RSpec.describe 'Create', :skip_live_env, product_group: :ide do
|
||||
before do
|
||||
skip("Skipped but kept as reference. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115741#note_1330720944")
|
||||
end
|
||||
|
||||
describe 'Web IDE file templates' do
|
||||
include Runtime::Fixtures
|
||||
|
||||
before(:all) do
|
||||
@project = create(:project,
|
||||
:with_readme,
|
||||
name: 'file-template-project',
|
||||
description: 'Add file templates via Web IDE')
|
||||
end
|
||||
|
||||
templates = [
|
||||
{
|
||||
file_name: '.gitignore',
|
||||
name: 'Android',
|
||||
api_path: 'gitignores',
|
||||
api_key: 'Android',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347752'
|
||||
},
|
||||
{
|
||||
file_name: '.gitlab-ci.yml',
|
||||
name: 'Julia',
|
||||
api_path: 'gitlab_ci_ymls',
|
||||
api_key: 'Julia',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347753'
|
||||
},
|
||||
{
|
||||
file_name: 'Dockerfile',
|
||||
name: 'Python',
|
||||
api_path: 'dockerfiles',
|
||||
api_key: 'Python',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347750'
|
||||
},
|
||||
{
|
||||
file_name: 'LICENSE',
|
||||
name: 'Mozilla Public License 2.0',
|
||||
api_path: 'licenses',
|
||||
api_key: 'mpl-2.0',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347751'
|
||||
}
|
||||
]
|
||||
|
||||
templates.each do |template|
|
||||
it "user adds #{template[:file_name]} via file template #{template[:name]}", testcase: template[:testcase] do
|
||||
content = fetch_template_from_api(template[:api_path], template[:api_key])
|
||||
|
||||
Flow::Login.sign_in
|
||||
|
||||
@project.visit!
|
||||
|
||||
Page::Project::Show.perform(&:open_web_ide!)
|
||||
Page::Project::WebIDE::Edit.perform do |ide|
|
||||
ide.wait_until_ide_loads
|
||||
ide.create_new_file_from_template template[:file_name], template[:name]
|
||||
|
||||
expect(ide.has_file?(template[:file_name])).to be_truthy
|
||||
expect(ide).to have_button('Undo')
|
||||
expect(ide).to have_normalized_ws_text(content[0..100])
|
||||
|
||||
ide.rename_file(template[:file_name], "#{SecureRandom.hex(8)}/#{template[:file_name]}")
|
||||
|
||||
ide.commit_changes
|
||||
|
||||
expect(ide).to have_content(template[:file_name])
|
||||
expect(ide).to have_normalized_ws_text(content[0..100])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# TODO: remove this test when coverage is replaced or deemed irrelevant
|
||||
module QA
|
||||
RSpec.describe 'Create', :skip_live_env, product_group: :ide do
|
||||
before do
|
||||
skip("Skipped but kept as reference. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115741#note_1330720944")
|
||||
end
|
||||
|
||||
describe 'First file using Web IDE' do
|
||||
let(:project) { create(:project, :with_readme, name: 'empty-project') }
|
||||
let(:file_name) { 'the very first file.txt' }
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
it "creates the first file in an empty project via Web IDE", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347803' do
|
||||
project.visit!
|
||||
Page::Project::Show.perform(&:create_first_new_file!)
|
||||
|
||||
Page::Project::WebIDE::Edit.perform do |ide|
|
||||
ide.wait_until_ide_loads
|
||||
ide.create_first_file(file_name)
|
||||
ide.commit_changes
|
||||
end
|
||||
|
||||
project.visit!
|
||||
|
||||
Page::Project::Show.perform do |project|
|
||||
expect(project).to have_file(file_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# TODO: remove this test when coverage is replaced or deemed irrelevant
|
||||
module QA
|
||||
RSpec.describe 'Create', :skip_live_env, product_group: :ide do
|
||||
before do
|
||||
skip("Skipped but kept as reference. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115741#note_1330720944")
|
||||
end
|
||||
|
||||
describe 'Link to line in Web IDE' do
|
||||
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
|
||||
let(:project) { create(:project, template_name: 'express') }
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
project.remove_via_api!
|
||||
end
|
||||
|
||||
it 'can link to a specific line of code in Web IDE', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347676' do
|
||||
project.visit!
|
||||
|
||||
# Open Web IDE by using a keyboard shortcut
|
||||
Page::Project::Show.perform(&:open_web_ide_via_shortcut)
|
||||
|
||||
Page::Project::WebIDE::Edit.perform do |ide|
|
||||
ide.wait_until_ide_loads
|
||||
ide.select_file('app.js')
|
||||
@link = ide.link_line('26')
|
||||
end
|
||||
|
||||
Flow::Login.sign_in(as: user)
|
||||
|
||||
page.visit(@link)
|
||||
|
||||
Page::Project::WebIDE::Edit.perform do |ide|
|
||||
expect(ide).to have_file('app.js')
|
||||
end
|
||||
|
||||
expect(page.driver.current_url).to include('app.js/#L26')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue