Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-11-16 18:07:32 +00:00
parent acee9d6fb5
commit 6ba7c824e9
124 changed files with 928 additions and 1248 deletions

View File

@ -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

View File

@ -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:

View File

@ -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";

View File

@ -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]

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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">

View File

@ -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"

View File

@ -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')">

View File

@ -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"

View File

@ -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 }} &#x2192;

View File

@ -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"

View File

@ -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">

View File

@ -53,7 +53,6 @@ export default {
<template>
<gl-alert
data-qa-selector="flash_alert"
variant="danger"
:dismissible="canDismiss"
:primary-button-text="message.actionText"

View File

@ -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" />

View File

@ -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') }}

View 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>

View File

@ -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)"

View File

@ -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

View File

@ -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>

View File

@ -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 />

View File

@ -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>

View File

@ -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"

View File

@ -82,7 +82,6 @@ export default {
type="file"
class="hidden"
multiple
data-qa-selector="file_upload_field"
@change="openFile"
/>
</li>

View File

@ -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}`"

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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 }">

View File

@ -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>

View File

@ -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'"

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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? },

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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`,

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
b34d536dadff793f35eb1d7321fd07639c89bff40a2edf5dd51bb5db627b912c

View File

@ -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);

View File

@ -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. |

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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 |

View File

@ -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)

View File

@ -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. | |

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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: >-

View File

@ -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)})

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: {

View File

@ -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

View File

@ -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

View File

@ -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