Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
abdb550f69
commit
3ca896b640
|
|
@ -279,7 +279,7 @@ Dangerfile
|
|||
/ee/app/assets/javascripts/vue_shared/components/customizable_dashboard/panels_base.vue
|
||||
/ee/app/assets/javascripts/product_analytics/
|
||||
|
||||
^[Analytics Instrumentation] @gitlab-org/analytics-section/product-intelligence/engineers
|
||||
^[Analytics Instrumentation] @gitlab-org/analytics-section/analytics-instrumentation/engineers
|
||||
/ee/lib/gitlab/usage_data_counters/
|
||||
/ee/lib/ee/gitlab/usage_data.rb
|
||||
/lib/gitlab/usage_data.rb
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ workflow:
|
|||
|
||||
include:
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 5.2.2
|
||||
ref: 5.3.0
|
||||
file:
|
||||
- /ci/base.gitlab-ci.yml
|
||||
- /ci/allure-report.yml
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
include:
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 5.1.1
|
||||
file:
|
||||
- /ci/base.gitlab-ci.yml
|
||||
- /ci/allure-report.yml
|
||||
- /ci/knapsack-report.yml
|
||||
- local: .gitlab/ci/qa-common/main.gitlab-ci.yml
|
||||
- template: Verify/Browser-Performance.gitlab-ci.yml
|
||||
|
||||
.test-variables:
|
||||
|
|
@ -21,7 +16,7 @@ include:
|
|||
.bundle-base:
|
||||
extends:
|
||||
- .qa-cache
|
||||
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}:bundler-2.3
|
||||
- .ruby-image
|
||||
before_script:
|
||||
- cd qa && bundle install
|
||||
|
||||
|
|
@ -75,8 +70,6 @@ review-qa-smoke:
|
|||
- .rules:qa-smoke
|
||||
variables:
|
||||
QA_SCENARIO: Test::Instance::Smoke
|
||||
QA_RUN_TYPE: review-qa-smoke
|
||||
retry: 1
|
||||
|
||||
review-qa-blocking:
|
||||
extends:
|
||||
|
|
@ -84,7 +77,6 @@ review-qa-blocking:
|
|||
- .rules:qa-blocking
|
||||
variables:
|
||||
QA_SCENARIO: Test::Instance::ReviewBlocking
|
||||
QA_RUN_TYPE: review-qa-blocking
|
||||
retry: 1
|
||||
review-qa-blocking-parallel:
|
||||
extends:
|
||||
|
|
@ -98,7 +90,6 @@ review-qa-non-blocking:
|
|||
- .rules:qa-non-blocking
|
||||
variables:
|
||||
QA_SCENARIO: Test::Instance::ReviewNonBlocking
|
||||
QA_RUN_TYPE: review-qa-non-blocking
|
||||
when: manual
|
||||
allow_failure: true
|
||||
review-qa-non-blocking-parallel:
|
||||
|
|
@ -118,18 +109,12 @@ browser_performance:
|
|||
|
||||
e2e-test-report:
|
||||
extends:
|
||||
- .generate-allure-report-base
|
||||
- .e2e-test-report
|
||||
- .rules:prepare-report
|
||||
stage: post-qa
|
||||
variables:
|
||||
ALLURE_JOB_NAME: e2e-review-qa
|
||||
ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
|
||||
ALLURE_RESULTS_GLOB: qa/tmp/allure-results
|
||||
ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
|
||||
GITLAB_AUTH_TOKEN: $PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE
|
||||
GIT_STRATEGY: none
|
||||
allow_failure: true
|
||||
when: always
|
||||
ALLURE_JOB_NAME: $QA_RUN_TYPE
|
||||
|
||||
upload-knapsack-report:
|
||||
extends:
|
||||
|
|
@ -145,38 +130,29 @@ delete-test-resources:
|
|||
- .rules:prepare-report
|
||||
stage: post-qa
|
||||
variables:
|
||||
QA_TEST_RESOURCES_FILE_PATTERN: $CI_PROJECT_DIR/qa/tmp/test-resources-*.json
|
||||
GITLAB_QA_ACCESS_TOKEN: $REVIEW_APPS_ROOT_TOKEN
|
||||
script:
|
||||
- export GITLAB_ADDRESS="$QA_GITLAB_URL"
|
||||
- bundle exec rake "test_resources:delete[$QA_TEST_RESOURCES_FILE_PATTERN]"
|
||||
- bundle exec rake "test_resources:delete[$CI_PROJECT_DIR/qa/tmp/test-resources-*.json]"
|
||||
allow_failure: true
|
||||
when: always
|
||||
|
||||
notify-slack:
|
||||
extends:
|
||||
- .notify-slack-qa
|
||||
- .qa-cache
|
||||
- .notify-slack
|
||||
- .rules:main-run
|
||||
stage: post-qa
|
||||
variables:
|
||||
QA_RSPEC_XML_FILE_PATTERN: ${CI_PROJECT_DIR}/qa/tmp/rspec-*.xml
|
||||
RUN_WITH_BUNDLE: "true"
|
||||
QA_PATH: qa
|
||||
ALLURE_JOB_NAME: e2e-review-qa
|
||||
SLACK_ICON_EMOJI: ci_failing
|
||||
STATUS_SYM: ☠️
|
||||
STATUS: failed
|
||||
TYPE: "(review-app) "
|
||||
ALLURE_JOB_NAME: $QA_RUN_TYPE
|
||||
when: on_failure
|
||||
script:
|
||||
- bundle exec prepare-stage-reports --input-files "${CI_PROJECT_DIR}/qa/tmp/rspec-*.xml"
|
||||
- !reference [.notify-slack-qa, script]
|
||||
|
||||
export-test-metrics:
|
||||
extends:
|
||||
- .export-test-metrics
|
||||
- .bundle-base
|
||||
- .rules:main-run
|
||||
stage: post-qa
|
||||
variables:
|
||||
QA_METRICS_REPORT_FILE_PATTERN: tmp/test-metrics-*.json
|
||||
when: always
|
||||
script:
|
||||
- bundle exec rake "ci:export_test_metrics[tmp/test-metrics-*.json]"
|
||||
|
|
|
|||
|
|
@ -101,36 +101,33 @@ start-review-app-pipeline:
|
|||
SCHEDULE_TYPE: $SCHEDULE_TYPE
|
||||
DAST_RUN: $DAST_RUN
|
||||
SKIP_MESSAGE: Skipping review-app due to mr containing only quarantine changes!
|
||||
QA_RUN_TYPE: e2e-review-qa
|
||||
trigger:
|
||||
strategy: depend
|
||||
include:
|
||||
- artifact: review-app-pipeline.yml
|
||||
job: e2e-test-pipeline-generate
|
||||
|
||||
include:
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 5.2.2
|
||||
file:
|
||||
- /ci/danger-review.yml
|
||||
|
||||
danger-review:
|
||||
extends:
|
||||
- .default-retry
|
||||
- .ruby-node-cache
|
||||
- .review:rules:danger
|
||||
stage: test
|
||||
needs: []
|
||||
image: "${DEFAULT_CI_IMAGE}"
|
||||
before_script:
|
||||
- source scripts/utils.sh
|
||||
- bundle_install_script "--with danger"
|
||||
- yarn_install_script
|
||||
script:
|
||||
# ${DANGER_DANGERFILE} is used by Jihulab for customizing danger support: https://jihulab.com/gitlab-cn/gitlab/-/blob/main-jh/jh/.gitlab-ci.yml
|
||||
- >
|
||||
if [ -z "$DANGER_GITLAB_API_TOKEN" ]; then
|
||||
run_timed_command danger_as_local
|
||||
else
|
||||
danger_id=$(echo -n ${DANGER_GITLAB_API_TOKEN} | md5sum | awk '{print $1}' | cut -c5-10)
|
||||
run_timed_command "bundle exec danger --fail-on-errors=true --verbose --danger_id=\"${danger_id}\" --dangerfile=\"${DANGER_DANGERFILE:-Dangerfile}\""
|
||||
fi
|
||||
|
||||
danger-review-local:
|
||||
extends:
|
||||
- danger-review
|
||||
- .review:rules:danger-local
|
||||
script:
|
||||
- run_timed_command danger_as_local
|
||||
extends: danger-review
|
||||
before_script:
|
||||
- !reference ["danger-review", "before_script"]
|
||||
# We unset DANGER_GITLAB_API_TOKEN so that Danger will run as local from `danger-review:script`
|
||||
- unset DANGER_GITLAB_API_TOKEN
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!-- This issue template is used by https://about.gitlab.com/handbook/engineering/development/analytics-section/product-intelligence/ for tracking effort around Service Ping reporting for GitLab.com -->
|
||||
<!-- This issue template is used by https://about.gitlab.com/handbook/engineering/development/analytics-section/analytics-instrumentation/ for tracking effort around Service Ping reporting for GitLab.com -->
|
||||
|
||||
The [Product Intelligence group](https://about.gitlab.com/handbook/engineering/development/analytics/product-intelligence/) runs manual reporting of ServicePing for GitLab.com on a weekly basis. This issue:
|
||||
The [Analytics Instrumentation group](https://about.gitlab.com/handbook/engineering/development/analytics/analytics-instrumentation/) runs manual reporting of ServicePing for GitLab.com on a weekly basis. This issue:
|
||||
|
||||
- Captures the work required to complete the reporting process,.
|
||||
- Captures the follow-up tasks that are focused on metrics performance verification.
|
||||
|
|
@ -123,7 +123,7 @@ If you get mentioned, check the failing metric and open an optimization issue.
|
|||
<!-- Do not edit below this line -->
|
||||
|
||||
/confidential
|
||||
/label ~"group::product intelligence" ~"devops::analytics" ~backend ~"section::analytics" ~"Category:Service Ping"
|
||||
/label ~"group::analytics instrumentation" ~"devops::analytics" ~backend ~"section::analytics" ~"Category:Service Ping"
|
||||
/epic https://gitlab.com/groups/gitlab-org/-/epics/6000
|
||||
/weight 5
|
||||
/title Monitor and Generate GitLab.com Service Ping
|
||||
|
|
|
|||
|
|
@ -528,18 +528,6 @@ Layout/ArgumentAlignment:
|
|||
- 'app/models/pages_domain_acme_order.rb'
|
||||
- 'app/models/project.rb'
|
||||
- 'app/models/project_feature.rb'
|
||||
- 'app/models/project_import_data.rb'
|
||||
- 'app/models/project_team.rb'
|
||||
- 'app/models/prometheus_alert.rb'
|
||||
- 'app/models/release.rb'
|
||||
- 'app/models/releases/source.rb'
|
||||
- 'app/models/remote_mirror.rb'
|
||||
- 'app/models/repository.rb'
|
||||
- 'app/models/resource_timebox_event.rb'
|
||||
- 'app/models/service_desk_setting.rb'
|
||||
- 'app/models/terraform/state.rb'
|
||||
- 'app/models/time_tracking/timelog_category.rb'
|
||||
- 'app/models/u2f_registration.rb'
|
||||
- 'app/services/compare_service.rb'
|
||||
- 'app/services/concerns/rate_limited_service.rb'
|
||||
- 'app/services/design_management/copy_design_collection/copy_service.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
14.20.0
|
||||
14.21.0
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -362,7 +362,7 @@ gem 'snowplow-tracker', '~> 0.8.0'
|
|||
|
||||
# Metrics
|
||||
gem 'webrick', '~> 1.8.1', require: false
|
||||
gem 'prometheus-client-mmap', '~> 0.23', require: 'prometheus/client'
|
||||
gem 'prometheus-client-mmap', '~> 0.24', require: 'prometheus/client'
|
||||
|
||||
gem 'warning', '~> 1.3.0'
|
||||
|
||||
|
|
|
|||
|
|
@ -452,11 +452,11 @@
|
|||
{"name":"premailer","version":"1.16.0","platform":"ruby","checksum":"03e4402c448e6bae13fb5f6301a8bde4f3508e1bff90ae7c0972c7be94694786"},
|
||||
{"name":"premailer-rails","version":"1.10.3","platform":"ruby","checksum":"7cdcb97027866f7a81c490c6d15ada7f39666b5f6375f0821b7e97e0483b112f"},
|
||||
{"name":"proc_to_ast","version":"0.1.0","platform":"ruby","checksum":"92a73fa66e2250a83f8589f818b0751bcf227c68f85916202df7af85082f8691"},
|
||||
{"name":"prometheus-client-mmap","version":"0.23.1","platform":"aarch64-linux","checksum":"4091121090d1d44747b3d09f2dbd5fdd61e274d557b8ed98b06c65cdd006d174"},
|
||||
{"name":"prometheus-client-mmap","version":"0.23.1","platform":"arm64-darwin","checksum":"fa54f230631852392b38cba1ad396c0472cb9f088eef563d0c381b19b1333855"},
|
||||
{"name":"prometheus-client-mmap","version":"0.23.1","platform":"ruby","checksum":"48545f23217a5e85ca79fa8c2563711e319debdae46ddbd6348ff37f48029c40"},
|
||||
{"name":"prometheus-client-mmap","version":"0.23.1","platform":"x86_64-darwin","checksum":"99b56f4017f0a1a062914da253c613b9957bfabf5b38af5012e3d8515ed49555"},
|
||||
{"name":"prometheus-client-mmap","version":"0.23.1","platform":"x86_64-linux","checksum":"624da747dbb97e0d88be1f2ba5ae5253941fc85dea875845f5b4c7a2c95ee032"},
|
||||
{"name":"prometheus-client-mmap","version":"0.24.3","platform":"aarch64-linux","checksum":"e9e05922724a1caa2788dd32567ec9e3e95607fb32f8eb95bb95884061a3042e"},
|
||||
{"name":"prometheus-client-mmap","version":"0.24.3","platform":"arm64-darwin","checksum":"6b618db676f7286de80c1ffdc52d931fc474e470c34d652e38d74b46953955b8"},
|
||||
{"name":"prometheus-client-mmap","version":"0.24.3","platform":"ruby","checksum":"014542ebeb7b9187427c0d431fe5bcab9b07d293a25e61182a92e79cc95dde34"},
|
||||
{"name":"prometheus-client-mmap","version":"0.24.3","platform":"x86_64-darwin","checksum":"4fde644acbd01e149edda9c47746dca0dc9610951fc793a22989a069670cfe5c"},
|
||||
{"name":"prometheus-client-mmap","version":"0.24.3","platform":"x86_64-linux","checksum":"981dbaadfccb8652feac7fce7a129464274140dbb7603f5e5060b8536387eaa1"},
|
||||
{"name":"pry","version":"0.14.2","platform":"java","checksum":"fd780670977ba04ff7ee32dabd4d02fe4bf02e977afe8809832d5dca1412862e"},
|
||||
{"name":"pry","version":"0.14.2","platform":"ruby","checksum":"c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d"},
|
||||
{"name":"pry-byebug","version":"3.10.1","platform":"ruby","checksum":"c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8"},
|
||||
|
|
|
|||
|
|
@ -1145,7 +1145,7 @@ GEM
|
|||
coderay
|
||||
parser
|
||||
unparser
|
||||
prometheus-client-mmap (0.23.1)
|
||||
prometheus-client-mmap (0.24.3)
|
||||
rb_sys (~> 0.9)
|
||||
pry (0.14.2)
|
||||
coderay (~> 1.1)
|
||||
|
|
@ -1859,7 +1859,7 @@ DEPENDENCIES
|
|||
pg_query (~> 2.2, >= 2.2.1)
|
||||
png_quantizator (~> 0.2.1)
|
||||
premailer-rails (~> 1.10.3)
|
||||
prometheus-client-mmap (~> 0.23)
|
||||
prometheus-client-mmap (~> 0.24)
|
||||
pry-byebug
|
||||
pry-rails (~> 0.3.9)
|
||||
pry-shell (~> 0.6.1)
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$refs.disclosureDropdown?.close();
|
||||
},
|
||||
generateHeaders() {
|
||||
const BASE_PADDING = 16;
|
||||
const headers = [...this.blobViewer.querySelectorAll('h1,h2,h3,h4,h5,h6')];
|
||||
|
|
@ -72,10 +69,8 @@ export default {
|
|||
<template>
|
||||
<gl-disclosure-dropdown
|
||||
v-if="!isHidden && items.length"
|
||||
ref="disclosureDropdown"
|
||||
icon="list-bulleted"
|
||||
class="gl-mr-2"
|
||||
:items="items"
|
||||
@action="close"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -258,9 +258,6 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...mapActions(['updateList', 'setActiveId', 'toggleListCollapsed']),
|
||||
closeListActions() {
|
||||
this.$refs.headerListActions?.close();
|
||||
},
|
||||
openSidebarSettings() {
|
||||
if (this.activeId === inactiveId) {
|
||||
sidebarEventHub.$emit('sidebar.closeAll');
|
||||
|
|
@ -277,8 +274,6 @@ export default {
|
|||
}
|
||||
|
||||
this.track('click_button', { label: 'list_settings' });
|
||||
|
||||
this.closeListActions();
|
||||
},
|
||||
showScopedLabels(label) {
|
||||
return this.scopedLabelsAvailable && isScopedLabel(label);
|
||||
|
|
@ -292,13 +287,9 @@ export default {
|
|||
} else {
|
||||
eventHub.$emit(`${toggleFormEventPrefix.issue}${this.list.id}`);
|
||||
}
|
||||
|
||||
this.closeListActions();
|
||||
},
|
||||
showNewEpicForm() {
|
||||
eventHub.$emit(`${toggleFormEventPrefix.epic}${this.list.id}`);
|
||||
|
||||
this.closeListActions();
|
||||
},
|
||||
toggleExpanded() {
|
||||
const collapsed = !this.list.collapsed;
|
||||
|
|
@ -532,7 +523,6 @@ export default {
|
|||
</div>
|
||||
<gl-disclosure-dropdown
|
||||
v-if="showListHeaderActions"
|
||||
ref="headerListActions"
|
||||
v-gl-tooltip.hover.top="{
|
||||
title: $options.i18n.listActions,
|
||||
boundary: 'viewport',
|
||||
|
|
|
|||
|
|
@ -58,8 +58,6 @@ export default {
|
|||
selectAgent(agent) {
|
||||
this.$emit('agentSelected', agent);
|
||||
this.selectedAgent = agent;
|
||||
|
||||
this.$refs.dropdown.closeAndFocus();
|
||||
},
|
||||
onKeyEnter() {
|
||||
if (!this.searchTerm?.length) {
|
||||
|
|
@ -76,7 +74,6 @@ export default {
|
|||
<template>
|
||||
<div @keydown.enter.stop.prevent="onKeyEnter">
|
||||
<gl-collapsible-listbox
|
||||
ref="dropdown"
|
||||
v-model="selectedAgent"
|
||||
class="gl-w-full"
|
||||
toggle-class="select-agent-dropdown"
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export default {
|
|||
*/
|
||||
this.$nextTick(() => {
|
||||
if (!this.noteUpdateDirty) {
|
||||
this.autosaveDiscussion.reset();
|
||||
this.autosaveDiscussion?.reset();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ export default {
|
|||
'file.file_hash': {
|
||||
handler: function hashChangeWatch(newHash, oldHash) {
|
||||
if (
|
||||
this.viewDiffsFileByFile &&
|
||||
!this.isCollapsed &&
|
||||
!this.glFeatures.singleFileFileByFile &&
|
||||
newHash &&
|
||||
oldHash &&
|
||||
!this.hasDiff &&
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ export default {
|
|||
:show-suggest-popover="showSuggestPopover"
|
||||
:save-button-title="__('Comment')"
|
||||
:autosave-key="autosaveKey"
|
||||
:autofocus="false"
|
||||
class="diff-comment-form gl-mt-3"
|
||||
@handleFormUpdateAddToReview="addToReview"
|
||||
@cancelForm="handleCancelCommentForm"
|
||||
|
|
|
|||
|
|
@ -461,6 +461,26 @@ export const setParallelDiffViewType = ({ commit }) => {
|
|||
|
||||
export const showCommentForm = ({ commit }, { lineCode, fileHash }) => {
|
||||
commit(types.TOGGLE_LINE_HAS_FORM, { lineCode, fileHash, hasForm: true });
|
||||
|
||||
// The comment form for diffs gets focussed differently due to the way the virtual scroller
|
||||
// works. If we focus the comment form on mount and the comment form gets removed and then
|
||||
// added again the page will scroll in unexpected ways
|
||||
setTimeout(() => {
|
||||
const el = document.querySelector(`[data-line-code="${lineCode}"] textarea`);
|
||||
|
||||
if (!el) return;
|
||||
|
||||
const { bottom } = el.getBoundingClientRect();
|
||||
const overflowBottom = bottom - window.innerHeight;
|
||||
|
||||
// Prevent the browser scrolling for us
|
||||
// We handle the scrolling to not break the diffs virtual scroller
|
||||
el.focus({ preventScroll: true });
|
||||
|
||||
if (overflowBottom > 0) {
|
||||
window.scrollBy(0, Math.floor(Math.abs(overflowBottom)) + 150);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const cancelCommentForm = ({ commit }, { lineCode, fileHash }) => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import { GlLoadingIcon } from '@gitlab/ui';
|
|||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import getEnvironment from '../graphql/queries/environment.query.graphql';
|
||||
import updateEnvironment from '../graphql/mutations/update_environment.mutation.graphql';
|
||||
import EnvironmentForm from './environment_form.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -11,6 +13,7 @@ export default {
|
|||
GlLoadingIcon,
|
||||
EnvironmentForm,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
inject: ['projectEnvironmentsPath', 'updateEnvironmentPath', 'projectPath', 'environmentName'],
|
||||
apollo: {
|
||||
environment: {
|
||||
|
|
@ -42,6 +45,44 @@ export default {
|
|||
this.formEnvironment = environment;
|
||||
},
|
||||
onSubmit() {
|
||||
if (this.glFeatures?.environmentSettingsToGraphql) {
|
||||
this.updateWithGraphql();
|
||||
} else {
|
||||
this.updateWithAxios();
|
||||
}
|
||||
},
|
||||
async updateWithGraphql() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: updateEnvironment,
|
||||
variables: {
|
||||
input: {
|
||||
id: this.formEnvironment.id,
|
||||
externalUrl: this.formEnvironment.externalUrl,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { errors } = data.environmentUpdate;
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(errors[0]?.message ?? errors[0]);
|
||||
}
|
||||
|
||||
const { path } = data.environmentUpdate.environment;
|
||||
|
||||
if (path) {
|
||||
visitUrl(path);
|
||||
}
|
||||
} catch (error) {
|
||||
const { message } = error;
|
||||
createAlert({ message });
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
updateWithAxios() {
|
||||
this.loading = true;
|
||||
axios
|
||||
.put(this.updateEnvironmentPath, {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
mutation updateEnvironment($input: EnvironmentUpdateInput!) {
|
||||
environmentUpdate(input: $input) {
|
||||
environment {
|
||||
id
|
||||
path
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
<script>
|
||||
import {
|
||||
GlButton,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormCheckbox,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlFormCheckbox,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
helpUrl: helpPagePath('operations/metrics/embed_grafana', {
|
||||
anchor: 'use-integration-with-grafana-api',
|
||||
}),
|
||||
placeholderUrl: 'https://my-grafana.example.com/',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['operationsSettingsEndpoint', 'grafanaToken', 'grafanaUrl', 'grafanaEnabled']),
|
||||
integrationEnabled: {
|
||||
get() {
|
||||
return this.grafanaEnabled;
|
||||
},
|
||||
set(grafanaEnabled) {
|
||||
this.setGrafanaEnabled(grafanaEnabled);
|
||||
},
|
||||
},
|
||||
localGrafanaToken: {
|
||||
get() {
|
||||
return this.grafanaToken;
|
||||
},
|
||||
set(token) {
|
||||
this.setGrafanaToken(token);
|
||||
},
|
||||
},
|
||||
localGrafanaUrl: {
|
||||
get() {
|
||||
return this.grafanaUrl;
|
||||
},
|
||||
set(url) {
|
||||
this.setGrafanaUrl(url);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions([
|
||||
'setGrafanaUrl',
|
||||
'setGrafanaToken',
|
||||
'setGrafanaEnabled',
|
||||
'updateGrafanaIntegration',
|
||||
]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="grafana" class="settings no-animate js-grafana-integration">
|
||||
<div class="settings-header">
|
||||
<h4
|
||||
class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
|
||||
>
|
||||
{{ s__('GrafanaIntegration|Grafana authentication') }}
|
||||
</h4>
|
||||
<gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button>
|
||||
<p class="js-section-sub-header">
|
||||
{{
|
||||
s__(
|
||||
'GrafanaIntegration|Set up Grafana authentication to embed Grafana panels in GitLab Flavored Markdown.',
|
||||
)
|
||||
}}
|
||||
<gl-link :href="helpUrl">{{ __('Learn more.') }}</gl-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<form>
|
||||
<gl-form-group :label="__('Enable authentication')" label-for="grafana-integration-enabled">
|
||||
<gl-form-checkbox id="grafana-integration-enabled" v-model="integrationEnabled">
|
||||
{{ s__('GrafanaIntegration|Active') }}
|
||||
</gl-form-checkbox>
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
:label="s__('GrafanaIntegration|Grafana URL')"
|
||||
label-for="grafana-url"
|
||||
:description="s__('GrafanaIntegration|Enter the base URL of the Grafana instance.')"
|
||||
>
|
||||
<gl-form-input id="grafana-url" v-model="localGrafanaUrl" :placeholder="placeholderUrl" />
|
||||
</gl-form-group>
|
||||
<gl-form-group :label="s__('GrafanaIntegration|API token')" label-for="grafana-token">
|
||||
<gl-form-input id="grafana-token" v-model="localGrafanaToken" />
|
||||
<p class="form-text text-muted">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__('GrafanaIntegration|Enter the %{docLinkStart}Grafana API token%{docLinkEnd}.')
|
||||
"
|
||||
>
|
||||
<template #docLink="{ content }">
|
||||
<gl-link
|
||||
href="https://grafana.com/docs/http_api/auth/#create-api-token"
|
||||
target="_blank"
|
||||
>{{ content }} <gl-icon name="external-link" class="gl-vertical-align-middle"
|
||||
/></gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</gl-form-group>
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="primary"
|
||||
data-testid="save-grafana-settings-button"
|
||||
@click="updateGrafanaIntegration"
|
||||
>
|
||||
{{ __('Save changes') }}
|
||||
</gl-button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import GrafanaIntegration from './components/grafana_integration.vue';
|
||||
import store from './store';
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-grafana-integration');
|
||||
|
||||
if (!el) return false;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
store: store(el.dataset),
|
||||
render(createElement) {
|
||||
return createElement(GrafanaIntegration);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import * as mutationTypes from './mutation_types';
|
||||
|
||||
export const setGrafanaUrl = ({ commit }, url) => commit(mutationTypes.SET_GRAFANA_URL, url);
|
||||
|
||||
export const setGrafanaToken = ({ commit }, token) =>
|
||||
commit(mutationTypes.SET_GRAFANA_TOKEN, token);
|
||||
|
||||
export const setGrafanaEnabled = ({ commit }, enabled) =>
|
||||
commit(mutationTypes.SET_GRAFANA_ENABLED, enabled);
|
||||
|
||||
export const updateGrafanaIntegration = ({ state, dispatch }) =>
|
||||
axios
|
||||
.patch(state.operationsSettingsEndpoint, {
|
||||
project: {
|
||||
grafana_integration_attributes: {
|
||||
grafana_url: state.grafanaUrl,
|
||||
token: state.grafanaToken,
|
||||
enabled: state.grafanaEnabled,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => dispatch('receiveGrafanaIntegrationUpdateSuccess'))
|
||||
.catch((error) => dispatch('receiveGrafanaIntegrationUpdateError', error));
|
||||
|
||||
export const receiveGrafanaIntegrationUpdateSuccess = () => {
|
||||
/**
|
||||
* The operations_controller currently handles successful requests
|
||||
* by creating an alert banner message to notify the user.
|
||||
*/
|
||||
refreshCurrentPage();
|
||||
};
|
||||
|
||||
export const receiveGrafanaIntegrationUpdateError = (_, error) => {
|
||||
const { response } = error;
|
||||
const message = response.data && response.data.message ? response.data.message : '';
|
||||
|
||||
createAlert({
|
||||
message: `${__('There was an error saving your changes.')} ${message}`,
|
||||
});
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import * as actions from './actions';
|
||||
import mutations from './mutations';
|
||||
import createState from './state';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export const createStore = (initialState) =>
|
||||
new Vuex.Store({
|
||||
state: createState(initialState),
|
||||
actions,
|
||||
mutations,
|
||||
});
|
||||
|
||||
export default createStore;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export const SET_GRAFANA_URL = 'SET_GRAFANA_URL';
|
||||
export const SET_GRAFANA_TOKEN = 'SET_GRAFANA_TOKEN';
|
||||
export const SET_GRAFANA_ENABLED = 'SET_GRAFANA_ENABLED';
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.SET_GRAFANA_URL](state, url) {
|
||||
state.grafanaUrl = url;
|
||||
},
|
||||
[types.SET_GRAFANA_TOKEN](state, token) {
|
||||
state.grafanaToken = token;
|
||||
},
|
||||
[types.SET_GRAFANA_ENABLED](state, enabled) {
|
||||
state.grafanaEnabled = enabled;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
|
||||
export default (initialState = {}) => ({
|
||||
operationsSettingsEndpoint: initialState.operationsSettingsEndpoint,
|
||||
grafanaToken: initialState.grafanaIntegrationToken || '',
|
||||
grafanaUrl: initialState.grafanaIntegrationUrl || '',
|
||||
grafanaEnabled: parseBoolean(initialState.grafanaIntegrationEnabled) || false,
|
||||
});
|
||||
|
|
@ -17,14 +17,9 @@ export default {
|
|||
methods: {
|
||||
convertToTask() {
|
||||
eventHub.$emit('convert-task-list-item', this.$el.closest('li').dataset.sourcepos);
|
||||
this.closeDropdown();
|
||||
},
|
||||
deleteTaskListItem() {
|
||||
eventHub.$emit('delete-task-list-item', this.$el.closest('li').dataset.sourcepos);
|
||||
this.closeDropdown();
|
||||
},
|
||||
closeDropdown() {
|
||||
this.$refs.dropdown.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -33,7 +28,6 @@ export default {
|
|||
<template>
|
||||
<gl-disclosure-dropdown
|
||||
v-if="canUpdate"
|
||||
ref="dropdown"
|
||||
class="task-list-item-actions-wrapper"
|
||||
category="tertiary"
|
||||
icon="ellipsis_v"
|
||||
|
|
|
|||
|
|
@ -214,22 +214,18 @@ export default {
|
|||
methods: {
|
||||
...mapActions(['toggleAwardRequest', 'promoteCommentToTimelineEvent']),
|
||||
onEdit() {
|
||||
this.closeMoreActionsDropdown();
|
||||
this.$emit('handleEdit');
|
||||
},
|
||||
onDelete() {
|
||||
this.closeMoreActionsDropdown();
|
||||
this.$emit('handleDelete');
|
||||
},
|
||||
onResolve() {
|
||||
this.$emit('handleResolve');
|
||||
},
|
||||
onAbuse() {
|
||||
this.closeMoreActionsDropdown();
|
||||
this.toggleReportAbuseDrawer(true);
|
||||
},
|
||||
onCopyUrl() {
|
||||
this.closeMoreActionsDropdown();
|
||||
this.$toast.show(__('Link copied to clipboard.'));
|
||||
},
|
||||
handleAssigneeUpdate(assignees) {
|
||||
|
|
@ -241,8 +237,6 @@ export default {
|
|||
let { assignees } = this;
|
||||
const { project_id, iid } = this.getNoteableData;
|
||||
|
||||
this.closeMoreActionsDropdown();
|
||||
|
||||
if (this.isUserAssigned) {
|
||||
assignees = assignees.filter((assignee) => assignee.id !== this.author.id);
|
||||
} else {
|
||||
|
|
@ -271,11 +265,6 @@ export default {
|
|||
toggleReportAbuseDrawer(isOpen) {
|
||||
this.isReportAbuseDrawerOpen = isOpen;
|
||||
},
|
||||
closeMoreActionsDropdown() {
|
||||
if (this.shouldShowActionsDropdown && this.$refs.moreActionsDropdown) {
|
||||
this.$refs.moreActionsDropdown.close();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -374,7 +363,6 @@ export default {
|
|||
/>
|
||||
<div v-else-if="shouldShowActionsDropdown" class="more-actions dropdown">
|
||||
<gl-disclosure-dropdown
|
||||
ref="moreActionsDropdown"
|
||||
v-gl-tooltip
|
||||
:title="$options.i18n.moreActionsLabel"
|
||||
:aria-label="$options.i18n.moreActionsLabel"
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -359,7 +364,7 @@ export default {
|
|||
:autocomplete-data-sources="autocompleteDataSources"
|
||||
:disabled="isSubmitting"
|
||||
supports-quick-actions
|
||||
autofocus
|
||||
:autofocus="autofocus"
|
||||
@keydown.meta.enter="handleKeySubmit()"
|
||||
@keydown.ctrl.enter="handleKeySubmit()"
|
||||
@keydown.exact.up="editMyLastNote()"
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
<script>
|
||||
import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { s__ } from '~/locale';
|
||||
import { timezones } from '~/monitoring/format_date';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['dashboardTimezone']),
|
||||
dashboardTimezoneModel: {
|
||||
get() {
|
||||
return this.dashboardTimezone.selected;
|
||||
},
|
||||
set(selected) {
|
||||
this.setDashboardTimezone(selected);
|
||||
},
|
||||
},
|
||||
options() {
|
||||
return [
|
||||
{
|
||||
value: timezones.LOCAL,
|
||||
text: s__("MetricsSettings|User's local timezone"),
|
||||
},
|
||||
{
|
||||
value: timezones.UTC,
|
||||
text: s__('MetricsSettings|UTC (Coordinated Universal Time)'),
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setDashboardTimezone']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form-group
|
||||
:label="s__('MetricsSettings|Dashboard timezone')"
|
||||
label-for="dashboard-timezone-setting"
|
||||
>
|
||||
<template #description>
|
||||
{{
|
||||
s__(
|
||||
"MetricsSettings|Choose whether to display dashboard metrics in UTC or the user's local timezone.",
|
||||
)
|
||||
}}
|
||||
</template>
|
||||
|
||||
<gl-form-select
|
||||
id="dashboard-timezone-setting"
|
||||
v-model="dashboardTimezoneModel"
|
||||
:options="options"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</template>
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<script>
|
||||
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['externalDashboard']),
|
||||
userDashboardUrl: {
|
||||
get() {
|
||||
return this.externalDashboard.url;
|
||||
},
|
||||
set(url) {
|
||||
this.setExternalDashboardUrl(url);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setExternalDashboardUrl']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form-group
|
||||
:label="s__('MetricsSettings|External dashboard URL')"
|
||||
label-for="external-dashboard-url"
|
||||
>
|
||||
<template #description>
|
||||
{{
|
||||
s__(
|
||||
'MetricsSettings|Add a button to the metrics dashboard linking directly to your existing external dashboard.',
|
||||
)
|
||||
}}
|
||||
</template>
|
||||
<!-- placeholder with a url is a false positive -->
|
||||
<!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
|
||||
<gl-form-input
|
||||
id="external-dashboard-url"
|
||||
v-model="userDashboardUrl"
|
||||
placeholder="https://my-org.gitlab.io/my-dashboards"
|
||||
/>
|
||||
<!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
|
||||
</gl-form-group>
|
||||
</template>
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
<script>
|
||||
import { GlButton, GlLink } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import DashboardTimezone from './form_group/dashboard_timezone.vue';
|
||||
import ExternalDashboard from './form_group/external_dashboard.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlLink,
|
||||
ExternalDashboard,
|
||||
DashboardTimezone,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['helpPage']),
|
||||
userDashboardUrl: {
|
||||
get() {
|
||||
return this.externalDashboard.url;
|
||||
},
|
||||
set(url) {
|
||||
this.setExternalDashboardUrl(url);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['saveChanges']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="settings no-animate">
|
||||
<div class="settings-header">
|
||||
<h4
|
||||
class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
|
||||
>
|
||||
{{ s__('MetricsSettings|Metrics') }}
|
||||
</h4>
|
||||
<gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button>
|
||||
<p class="js-section-sub-header">
|
||||
{{ s__('MetricsSettings|Manage metrics dashboard settings.') }}
|
||||
<gl-link :href="helpPage">{{ __('Learn more.') }}</gl-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<form>
|
||||
<dashboard-timezone />
|
||||
<external-dashboard />
|
||||
<gl-button variant="confirm" category="primary" @click="saveChanges">
|
||||
{{ __('Save Changes') }}
|
||||
</gl-button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import MetricsSettingsForm from './components/metrics_settings.vue';
|
||||
import store from './store';
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-operation-settings');
|
||||
|
||||
if (!el) return false;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
store: store(el.dataset),
|
||||
render(createElement) {
|
||||
return createElement(MetricsSettingsForm);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import * as mutationTypes from './mutation_types';
|
||||
|
||||
export const setExternalDashboardUrl = ({ commit }, url) =>
|
||||
commit(mutationTypes.SET_EXTERNAL_DASHBOARD_URL, url);
|
||||
|
||||
export const setDashboardTimezone = ({ commit }, selected) =>
|
||||
commit(mutationTypes.SET_DASHBOARD_TIMEZONE, selected);
|
||||
|
||||
export const saveChanges = ({ state, dispatch }) =>
|
||||
axios
|
||||
.patch(state.operationsSettingsEndpoint, {
|
||||
project: {
|
||||
metrics_setting_attributes: {
|
||||
dashboard_timezone: state.dashboardTimezone.selected,
|
||||
external_dashboard_url: state.externalDashboard.url,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => dispatch('receiveSaveChangesSuccess'))
|
||||
.catch((error) => dispatch('receiveSaveChangesError', error));
|
||||
|
||||
export const receiveSaveChangesSuccess = () => {
|
||||
/**
|
||||
* The operations_controller currently handles successful requests
|
||||
* by creating an alert banner message to notify the user.
|
||||
*/
|
||||
refreshCurrentPage();
|
||||
};
|
||||
|
||||
export const receiveSaveChangesError = (_, error) => {
|
||||
const { response = {} } = error;
|
||||
const message = response.data && response.data.message ? response.data.message : '';
|
||||
|
||||
createAlert({
|
||||
message: `${__('There was an error saving your changes.')} ${message}`,
|
||||
});
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import * as actions from './actions';
|
||||
import mutations from './mutations';
|
||||
import createState from './state';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export const createStore = (initialState) =>
|
||||
new Vuex.Store({
|
||||
state: createState(initialState),
|
||||
actions,
|
||||
mutations,
|
||||
});
|
||||
|
||||
export default createStore;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export const SET_EXTERNAL_DASHBOARD_URL = 'SET_EXTERNAL_DASHBOARD_URL';
|
||||
export const SET_DASHBOARD_TIMEZONE = 'SET_DASHBOARD_TIMEZONE';
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.SET_EXTERNAL_DASHBOARD_URL](state, url) {
|
||||
state.externalDashboard.url = url;
|
||||
},
|
||||
[types.SET_DASHBOARD_TIMEZONE](state, selected) {
|
||||
state.dashboardTimezone.selected = selected;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
export default (initialState = {}) => ({
|
||||
operationsSettingsEndpoint: initialState.operationsSettingsEndpoint,
|
||||
helpPage: initialState.helpPage,
|
||||
externalDashboard: {
|
||||
url: initialState.externalDashboardUrl,
|
||||
},
|
||||
dashboardTimezone: {
|
||||
selected: initialState.dashboardTimezoneSetting,
|
||||
},
|
||||
});
|
||||
|
|
@ -1,14 +1,10 @@
|
|||
import mountAlertsSettings from '~/alerts_settings';
|
||||
import mountErrorTrackingForm from '~/error_tracking_settings';
|
||||
import mountGrafanaIntegration from '~/grafana_integration';
|
||||
import initIncidentsSettings from '~/incidents_settings';
|
||||
import mountOperationSettings from '~/operation_settings';
|
||||
import initSettingsPanels from '~/settings_panels';
|
||||
|
||||
initIncidentsSettings();
|
||||
mountErrorTrackingForm();
|
||||
mountOperationSettings();
|
||||
mountGrafanaIntegration();
|
||||
if (!IS_EE) {
|
||||
initSettingsPanels();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ export default {
|
|||
isInvitedMembers(groupItem) {
|
||||
return groupItem.component === TOP_NAV_INVITE_MEMBERS_COMPONENT;
|
||||
},
|
||||
closeAndFocus() {
|
||||
this.$refs.dropdown.closeAndFocus();
|
||||
},
|
||||
},
|
||||
toggleId: 'create-menu-toggle',
|
||||
popperOptions: {
|
||||
|
|
@ -64,7 +61,6 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<gl-disclosure-dropdown
|
||||
ref="dropdown"
|
||||
category="tertiary"
|
||||
icon="plus"
|
||||
no-caret
|
||||
|
|
@ -89,7 +85,6 @@ export default {
|
|||
:key="`${groupItem.text}-trigger`"
|
||||
trigger-source="top-nav"
|
||||
:trigger-element="$options.TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN"
|
||||
@modal-opened="closeAndFocus"
|
||||
/>
|
||||
<gl-disclosure-dropdown-item v-else :key="groupItem.text" :item="groupItem" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export default {
|
|||
items: [
|
||||
{
|
||||
text: this.$options.i18n.shortcuts,
|
||||
action: this.showKeyboardShortcuts,
|
||||
action: () => {},
|
||||
extraAttrs: {
|
||||
class: 'js-shortcuts-modal-trigger',
|
||||
'data-track-action': 'click_button',
|
||||
|
|
@ -172,18 +172,11 @@ export default {
|
|||
return true;
|
||||
},
|
||||
|
||||
showKeyboardShortcuts() {
|
||||
this.$refs.dropdown.close();
|
||||
},
|
||||
|
||||
showTanukiBotChat() {
|
||||
this.$refs.dropdown.close();
|
||||
|
||||
this.helpCenterState.showTanukiBotChatDrawer = true;
|
||||
},
|
||||
|
||||
async showWhatsNew() {
|
||||
this.$refs.dropdown.close();
|
||||
this.showWhatsNewNotification = false;
|
||||
|
||||
if (!this.toggleWhatsNewDrawer) {
|
||||
|
|
@ -226,7 +219,6 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-disclosure-dropdown
|
||||
ref="dropdown"
|
||||
:popper-options="$options.popperOptions"
|
||||
@shown="trackDropdownToggle(true)"
|
||||
@hidden="trackDropdownToggle(false)"
|
||||
|
|
|
|||
|
|
@ -241,6 +241,7 @@ export default {
|
|||
:popper-options="$options.popperOptions"
|
||||
data-testid="user-dropdown"
|
||||
data-qa-selector="user_menu"
|
||||
:auto-close="false"
|
||||
@shown="onShow"
|
||||
>
|
||||
<template #toggle>
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ export default {
|
|||
:css-classes="['diff-suggest-popover']"
|
||||
placement="bottom"
|
||||
:show="suggestPopoverVisible"
|
||||
triggers=""
|
||||
>
|
||||
<strong>{{ __('New! Suggest changes directly') }}</strong>
|
||||
<p class="mb-2">
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ export default {
|
|||
currentUserId() {
|
||||
return window.gon.current_user_id;
|
||||
},
|
||||
currentUserFullName() {
|
||||
return window.gon.current_user_fullname;
|
||||
},
|
||||
/**
|
||||
* Parse and convert award emoji list to a format that AwardsList can understand
|
||||
*/
|
||||
|
|
@ -42,15 +45,18 @@ export default {
|
|||
name: emoji.name,
|
||||
user: {
|
||||
id: getIdFromGraphQLId(emoji.user.id),
|
||||
name: emoji.user.name,
|
||||
},
|
||||
}));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleAward(name) {
|
||||
// Decide action based on emoji is already present
|
||||
// Decide action based on emoji given by current user.
|
||||
const action =
|
||||
this.awards.findIndex((emoji) => emoji.name === name) > -1
|
||||
this.awards.findIndex(
|
||||
(emoji) => emoji.name === name && emoji.user.id === this.currentUserId,
|
||||
) > -1
|
||||
? EMOJI_ACTION_REMOVE
|
||||
: EMOJI_ACTION_ADD;
|
||||
const inputVariables = {
|
||||
|
|
@ -96,13 +102,19 @@ export default {
|
|||
__typename: 'AwardEmoji',
|
||||
user: {
|
||||
id: convertToGraphQLId(TYPENAME_USER, this.currentUserId),
|
||||
name: this.currentUserFullName,
|
||||
__typename: 'UserCore',
|
||||
},
|
||||
},
|
||||
];
|
||||
// Exclude the award emoji node in case of remove action
|
||||
if (action === EMOJI_ACTION_REMOVE) {
|
||||
awardEmojiNodes = [...this.awardEmoji.nodes.filter((emoji) => emoji.name !== name)];
|
||||
awardEmojiNodes = [
|
||||
...this.awardEmoji.nodes.filter(
|
||||
(emoji) =>
|
||||
!(emoji.name === name && getIdFromGraphQLId(emoji.user.id) === this.currentUserId),
|
||||
),
|
||||
];
|
||||
}
|
||||
return {
|
||||
workItemUpdate: {
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@ fragment AwardEmojiFragment on AwardEmoji {
|
|||
name
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Common
|
||||
.diff-file {
|
||||
margin-bottom: $gl-padding;
|
||||
padding-bottom: $gl-padding;
|
||||
|
||||
&.has-body {
|
||||
.file-title {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class Admin::UsersController < Admin::ApplicationController
|
|||
|
||||
log_impersonation_event
|
||||
|
||||
flash[:alert] = format(_("You are now impersonating %{username}"), username: user.username)
|
||||
flash[:notice] = format(_("You are now impersonating %{username}"), username: user.username)
|
||||
|
||||
redirect_to root_path
|
||||
else
|
||||
|
|
|
|||
|
|
@ -5,25 +5,27 @@ module WebIdeCSP
|
|||
|
||||
included do
|
||||
before_action :include_web_ide_csp
|
||||
end
|
||||
|
||||
# We want to include frames from `/assets/webpack` of the request's host to
|
||||
# support URL flexibility with the Web IDE.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118875
|
||||
def include_web_ide_csp
|
||||
return if request.content_security_policy.directives.blank?
|
||||
# We want to include frames from `/assets/webpack` of the request's host to
|
||||
# support URL flexibility with the Web IDE.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118875
|
||||
def include_web_ide_csp
|
||||
return if request.content_security_policy.directives.blank?
|
||||
|
||||
base_uri = URI(request.url)
|
||||
base_uri.path = ::Gitlab.config.gitlab.relative_url_root || '/'
|
||||
# `.path +=` handles combining `x/` and `/foo`
|
||||
base_uri.path += '/assets/webpack/'
|
||||
webpack_url = base_uri.to_s
|
||||
base_uri = URI(request.url)
|
||||
base_uri.path = ::Gitlab.config.gitlab.relative_url_root || '/'
|
||||
# `.path +=` handles combining `x/` and `/foo`
|
||||
base_uri.path += '/assets/webpack/'
|
||||
webpack_url = base_uri.to_s
|
||||
|
||||
default_src = Array(request.content_security_policy.directives['default-src'] || [])
|
||||
request.content_security_policy.directives['frame-src'] ||= default_src
|
||||
request.content_security_policy.directives['frame-src'].concat([webpack_url, 'https://*.vscode-cdn.net/'])
|
||||
default_src = Array(request.content_security_policy.directives['default-src'] || [])
|
||||
request.content_security_policy.directives['frame-src'] ||= default_src
|
||||
request.content_security_policy.directives['frame-src'].concat([webpack_url, 'https://*.vscode-cdn.net/'])
|
||||
|
||||
request.content_security_policy.directives['worker-src'] ||= default_src
|
||||
request.content_security_policy.directives['worker-src'].concat([webpack_url])
|
||||
end
|
||||
request.content_security_policy.directives['worker-src'] ||= default_src
|
||||
request.content_security_policy.directives['worker-src'].concat([webpack_url])
|
||||
end
|
||||
end
|
||||
|
||||
WebIdeCSP.prepend_mod_with('WebIdeCSP')
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:kas_user_access_project, @project)
|
||||
end
|
||||
|
||||
before_action only: [:edit, :new] do
|
||||
push_frontend_feature_flag(:environment_settings_to_graphql, @project)
|
||||
end
|
||||
|
||||
before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect]
|
||||
before_action :authorize_create_environment!, only: [:new, :create]
|
||||
before_action :authorize_stop_environment!, only: [:stop]
|
||||
|
|
|
|||
|
|
@ -12,13 +12,15 @@ class AbuseReport < ApplicationRecord
|
|||
|
||||
cache_markdown_field :message, pipeline: :single_line
|
||||
|
||||
belongs_to :reporter, class_name: 'User'
|
||||
belongs_to :user
|
||||
belongs_to :reporter, class_name: 'User', inverse_of: :reported_abuse_reports
|
||||
belongs_to :user, inverse_of: :abuse_reports
|
||||
belongs_to :resolved_by, class_name: 'User', inverse_of: :resolved_abuse_reports
|
||||
belongs_to :assignee, class_name: 'User', inverse_of: :assigned_abuse_reports
|
||||
|
||||
has_many :events, class_name: 'ResourceEvents::AbuseReportEvent', inverse_of: :abuse_report
|
||||
|
||||
validates :reporter, presence: true
|
||||
validates :user, presence: true
|
||||
validates :reporter, presence: true, on: :create
|
||||
validates :user, presence: true, on: :create
|
||||
validates :message, presence: true
|
||||
validates :category, presence: true
|
||||
validates :user_id,
|
||||
|
|
@ -27,7 +29,7 @@ class AbuseReport < ApplicationRecord
|
|||
message: ->(object, data) do
|
||||
_('You have already reported this user')
|
||||
end
|
||||
}
|
||||
}, on: :create
|
||||
|
||||
validates :reported_from_url,
|
||||
allow_blank: true,
|
||||
|
|
@ -45,6 +47,9 @@ class AbuseReport < ApplicationRecord
|
|||
message: N_("exceeds the limit of %{count} links")
|
||||
}
|
||||
|
||||
validates :mitigation_steps, length: { maximum: 1000 }, allow_blank: true
|
||||
validates :evidence, json_schema: { filename: 'abuse_report_evidence' }, allow_blank: true
|
||||
|
||||
before_validation :filter_empty_strings_from_links_to_spam
|
||||
validate :links_to_spam_contains_valid_urls
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ class ProjectImportData < ApplicationRecord
|
|||
|
||||
belongs_to :project, inverse_of: :import_data
|
||||
attr_encrypted :credentials,
|
||||
key: Settings.attr_encrypted_db_key_base,
|
||||
marshal: true,
|
||||
encode: true,
|
||||
mode: :per_attribute_iv_and_salt,
|
||||
insecure_mode: true,
|
||||
algorithm: 'aes-256-cbc'
|
||||
key: Settings.attr_encrypted_db_key_base,
|
||||
marshal: true,
|
||||
encode: true,
|
||||
mode: :per_attribute_iv_and_salt,
|
||||
insecure_mode: true,
|
||||
algorithm: 'aes-256-cbc'
|
||||
|
||||
# NOTE
|
||||
# We are serializing a project as `data` in an "unsafe" way here
|
||||
|
|
|
|||
|
|
@ -179,9 +179,11 @@ class ProjectTeam
|
|||
#
|
||||
# Returns a Hash mapping user ID -> maximum access level.
|
||||
def max_member_access_for_user_ids(user_ids)
|
||||
Gitlab::SafeRequestLoader.execute(resource_key: project.max_member_access_for_resource_key(User),
|
||||
resource_ids: user_ids,
|
||||
default_value: Gitlab::Access::NO_ACCESS) do |user_ids|
|
||||
Gitlab::SafeRequestLoader.execute(
|
||||
resource_key: project.max_member_access_for_resource_key(User),
|
||||
resource_ids: user_ids,
|
||||
default_value: Gitlab::Access::NO_ACCESS
|
||||
) do |user_ids|
|
||||
project.project_authorizations
|
||||
.where(user: user_ids)
|
||||
.group(:user_id)
|
||||
|
|
@ -202,9 +204,11 @@ class ProjectTeam
|
|||
end
|
||||
|
||||
def contribution_check_for_user_ids(user_ids)
|
||||
Gitlab::SafeRequestLoader.execute(resource_key: "contribution_check_for_users:#{project.id}",
|
||||
resource_ids: user_ids,
|
||||
default_value: false) do |user_ids|
|
||||
Gitlab::SafeRequestLoader.execute(
|
||||
resource_key: "contribution_check_for_users:#{project.id}",
|
||||
resource_ids: user_ids,
|
||||
default_value: false
|
||||
) do |user_ids|
|
||||
project.merge_requests
|
||||
.merged
|
||||
.where(author_id: user_ids, target_branch: project.default_branch.to_s)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class PrometheusAlert < ApplicationRecord
|
|||
|
||||
validates :environment, :project, :prometheus_metric, :threshold, :operator, presence: true
|
||||
validates :runbook_url, length: { maximum: 255 }, allow_blank: true,
|
||||
addressable_url: { enforce_sanitization: true, ascii_only: true }
|
||||
addressable_url: { enforce_sanitization: true, ascii_only: true }
|
||||
validate :require_valid_environment_project!
|
||||
validate :require_valid_metric_project!
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@ class Release < ApplicationRecord
|
|||
|
||||
scope :sorted, -> { order(released_at: :desc) }
|
||||
scope :preloaded, -> {
|
||||
includes(:author, :evidences, :milestones, :links, :sorted_links,
|
||||
project: [:project_feature, :route, { namespace: :route }])
|
||||
includes(
|
||||
:author, :evidences, :milestones, :links, :sorted_links,
|
||||
project: [:project_feature, :route, { namespace: :route }]
|
||||
)
|
||||
}
|
||||
scope :with_milestones, -> { joins(:milestone_releases) }
|
||||
scope :with_group_milestones, -> { joins(:milestones).where.not(milestones: { group_id: nil }) }
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ module Releases
|
|||
class << self
|
||||
def all(project, tag_name)
|
||||
Gitlab::Workhorse::ARCHIVE_FORMATS.map do |format|
|
||||
Releases::Source.new(project: project,
|
||||
tag_name: tag_name,
|
||||
format: format)
|
||||
Releases::Source.new(project: project, tag_name: tag_name, format: format)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -19,9 +17,7 @@ module Releases
|
|||
def url
|
||||
Gitlab::Routing
|
||||
.url_helpers
|
||||
.project_archive_url(project,
|
||||
id: File.join(tag_name, archive_prefix),
|
||||
format: format)
|
||||
.project_archive_url(project, id: File.join(tag_name, archive_prefix), format: format)
|
||||
end
|
||||
|
||||
def hook_attrs
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ class RemoteMirror < ApplicationRecord
|
|||
UNPROTECTED_BACKOFF_DELAY = 5.minutes
|
||||
|
||||
attr_encrypted :credentials,
|
||||
key: Settings.attr_encrypted_db_key_base,
|
||||
marshal: true,
|
||||
encode: true,
|
||||
mode: :per_attribute_iv_and_salt,
|
||||
insecure_mode: true,
|
||||
algorithm: 'aes-256-cbc'
|
||||
key: Settings.attr_encrypted_db_key_base,
|
||||
marshal: true,
|
||||
encode: true,
|
||||
mode: :per_attribute_iv_and_salt,
|
||||
insecure_mode: true,
|
||||
algorithm: 'aes-256-cbc'
|
||||
|
||||
belongs_to :project, inverse_of: :remote_mirrors
|
||||
|
||||
|
|
@ -31,10 +31,8 @@ class RemoteMirror < ApplicationRecord
|
|||
|
||||
scope :stuck, -> do
|
||||
started
|
||||
.where('(last_update_started_at < ? AND last_update_at IS NOT NULL)',
|
||||
MAX_INCREMENTAL_RUNTIME.ago)
|
||||
.or(where('(last_update_started_at < ? AND last_update_at IS NULL)',
|
||||
MAX_FIRST_RUNTIME.ago))
|
||||
.where('(last_update_started_at < ? AND last_update_at IS NOT NULL)', MAX_INCREMENTAL_RUNTIME.ago)
|
||||
.or(where('(last_update_started_at < ? AND last_update_at IS NULL)', MAX_FIRST_RUNTIME.ago))
|
||||
end
|
||||
|
||||
state_machine :update_status, initial: :none do
|
||||
|
|
|
|||
|
|
@ -880,10 +880,12 @@ class Repository
|
|||
end
|
||||
|
||||
def merge(user, source_sha, merge_request, message)
|
||||
merge_to_branch(user,
|
||||
source_sha: source_sha,
|
||||
target_branch: merge_request.target_branch,
|
||||
message: message) do |commit_id|
|
||||
merge_to_branch(
|
||||
user,
|
||||
source_sha: source_sha,
|
||||
target_branch: merge_request.target_branch,
|
||||
message: message
|
||||
) do |commit_id|
|
||||
merge_request.update_and_mark_in_progress_merge_commit_sha(commit_id)
|
||||
nil # Return value does not matter.
|
||||
end
|
||||
|
|
@ -1136,10 +1138,13 @@ class Repository
|
|||
end
|
||||
|
||||
def squash(user, merge_request, message)
|
||||
raw.squash(user, start_sha: merge_request.diff_start_sha,
|
||||
end_sha: merge_request.diff_head_sha,
|
||||
author: merge_request.author,
|
||||
message: message)
|
||||
raw.squash(
|
||||
user,
|
||||
start_sha: merge_request.diff_start_sha,
|
||||
end_sha: merge_request.diff_head_sha,
|
||||
author: merge_request.author,
|
||||
message: message
|
||||
)
|
||||
end
|
||||
|
||||
def submodule_links
|
||||
|
|
@ -1271,11 +1276,13 @@ class Repository
|
|||
end
|
||||
|
||||
def initialize_raw_repository
|
||||
Gitlab::Git::Repository.new(shard,
|
||||
disk_path + '.git',
|
||||
repo_type.identifier_for_container(container),
|
||||
container.full_path,
|
||||
container: container)
|
||||
Gitlab::Git::Repository.new(
|
||||
shard,
|
||||
disk_path + '.git',
|
||||
repo_type.identifier_for_container(container),
|
||||
container.full_path,
|
||||
container: container
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,9 @@ class ResourceTimeboxEvent < ResourceEvent
|
|||
|
||||
case self
|
||||
when ResourceMilestoneEvent
|
||||
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(author: user,
|
||||
project: issue.project)
|
||||
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(
|
||||
author: user, project: issue.project
|
||||
)
|
||||
else
|
||||
# no-op
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ module Terraform
|
|||
|
||||
validates :project_id, :name, presence: true
|
||||
validates :uuid, presence: true, uniqueness: true, length: { is: UUID_LENGTH },
|
||||
format: { with: HEX_REGEXP, message: 'only allows hex characters' }
|
||||
format: { with: HEX_REGEXP, message: 'only allows hex characters' }
|
||||
|
||||
attribute :uuid, default: -> { SecureRandom.hex(UUID_LENGTH / 2) }
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ module TimeTracking
|
|||
validates :description, length: { maximum: 1024 }
|
||||
validates :color, color: true, allow_blank: false, length: { maximum: 7 }
|
||||
validates :billing_rate,
|
||||
if: :billable?,
|
||||
presence: true,
|
||||
numericality: { greater_than: 0 }
|
||||
if: :billable?,
|
||||
presence: true,
|
||||
numericality: { greater_than: 0 }
|
||||
|
||||
DEFAULT_COLOR = ::Gitlab::Color.of('#6699cc')
|
||||
|
||||
|
|
|
|||
|
|
@ -216,8 +216,10 @@ class User < ApplicationRecord
|
|||
has_many :releases, dependent: :nullify, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :subscriptions, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :abuse_reports, dependent: :destroy, foreign_key: :user_id # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :abuse_reports, dependent: :nullify, foreign_key: :user_id, inverse_of: :user # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :reported_abuse_reports, dependent: :nullify, foreign_key: :reporter_id, class_name: "AbuseReport", inverse_of: :reporter # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :assigned_abuse_reports, foreign_key: :assignee_id, class_name: "AbuseReport", inverse_of: :assignee
|
||||
has_many :resolved_abuse_reports, foreign_key: :resolved_by_id, class_name: "AbuseReport", inverse_of: :resolved_by
|
||||
has_many :spam_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :abuse_trust_scores, class_name: 'Abuse::TrustScore', foreign_key: :user_id
|
||||
has_many :builds, class_name: 'Ci::Build'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Evidence to support an abuse report",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"title",
|
||||
"description"
|
||||
]
|
||||
}
|
||||
},
|
||||
"snippets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"login_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"account_age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"spam_score": {
|
||||
"type": "number"
|
||||
},
|
||||
"telesign_score": {
|
||||
"type": "number"
|
||||
},
|
||||
"arkos_score": {
|
||||
"type": "number"
|
||||
},
|
||||
"pvs_score": {
|
||||
"type": "number"
|
||||
},
|
||||
"product_coverage": {
|
||||
"type": "number"
|
||||
},
|
||||
"virus_total_score": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"login_count",
|
||||
"account_age",
|
||||
"spam_score",
|
||||
"telesign_score",
|
||||
"arkos_score",
|
||||
"pvs_score",
|
||||
"product_coverage",
|
||||
"virus_total_score"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"user"
|
||||
]
|
||||
}
|
||||
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
= render Pajamas::AlertComponent.new(variant: :warning,
|
||||
alert_options: { class: 'hidden js-cluster-api-unreachable' }) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= s_('ClusterIntegration|Your cluster API is unreachable. Please ensure your API URL is correct.')
|
||||
|
||||
= render Pajamas::AlertComponent.new(variant: :warning,
|
||||
alert_options: { class: 'hidden js-cluster-authentication-failure js-cluster-api-unreachable' }) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= s_('ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid.')
|
||||
|
||||
.hidden.js-cluster-success.bs-callout.bs-callout-success{ role: 'alert' }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mt-6 gl-mb-3' }) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
|
||||
- issue_link_start = link_start % { url: 'https://gitlab.com/gitlab-org/configure/general/-/issues/199' }
|
||||
- docs_link_start = link_start % { url: help_page_path('user/clusters/agent/index.md') }
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
alert_options: { class: 'gcp-signup-offer',
|
||||
data: { feature_id: Users::CalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: callouts_path }},
|
||||
close_button_options: { data: { track_action: 'click_dismiss', track_label: 'gcp_signup_offer_banner' }}) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
|
||||
= c.actions do
|
||||
- c.with_actions do
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', button_options: { rel: 'noopener noreferrer', data: { track_action: 'click_button', track_label: 'gcp_signup_offer_banner' } }) do
|
||||
= s_("ClusterIntegration|Apply for credit")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
= _('Projects are where you store your code, access issues, wiki and other features of GitLab.')
|
||||
- else
|
||||
= render Pajamas::AlertComponent.new(variant: :info, alert_options: { class: 'gl-mb-5 gl-w-full' }) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= _("You see projects here when you're added to a group or project.").html_safe
|
||||
|
||||
- if current_user.can_create_group?
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
- if render_remember_me
|
||||
.gl-px-5
|
||||
= render Pajamas::CheckboxTagComponent.new(name: 'remember_me') do |c|
|
||||
= c.label do
|
||||
- c.with_label do
|
||||
= _('Remember me')
|
||||
|
||||
.submit-container.move-submit-down.gl-px-5.gl-pb-5
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
variant: :danger,
|
||||
dismissible: false,
|
||||
alert_options: { id: 'error_explanation', class: 'gl-mb-3'}) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
%ul.gl-pl-4
|
||||
- resource.errors.full_messages.each do |message|
|
||||
%li= message
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@
|
|||
= label_for_provider(provider)
|
||||
- if render_remember_me
|
||||
= render Pajamas::CheckboxTagComponent.new(name: 'remember_me_omniauth', value: nil) do |c|
|
||||
= c.label do
|
||||
- c.with_label do
|
||||
= _('Remember me')
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
|
||||
- if current_user
|
||||
- unless has_label
|
||||
%span.gl-float-left= _("Visibility:")
|
||||
%span.gl-float-left.gl-white-space-nowrap= _("Visibility:")
|
||||
= gl_redirect_listbox_tag(projects_filter_items, selected, class: 'gl-ml-3', data: { placement: 'right' })
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
= render Pajamas::AlertComponent.new(variant: :danger,
|
||||
dismissible: false,
|
||||
alert_options: { class: 'gl-mb-5' }) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
- @errors.each do |error|
|
||||
= error
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT,
|
||||
dismiss_endpoint: callouts_path }},
|
||||
close_button_options: { data: { testid: 'close-registration-enabled-callout' }}) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= _("Your GitLab instance allows anyone to register for an account, which is a security risk on public-facing GitLab instances. You should deactivate new sign ups if public users aren't expected to register for an account.")
|
||||
= c.actions do
|
||||
- c.with_actions do
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, href: general_admin_application_settings_path(anchor: 'js-signup-settings')) do
|
||||
= _('Deactivate')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-close gl-ml-3'}) do
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
%div{ class: "#{container_class} limit-container-width" }
|
||||
.content{ id: "content-body" }
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
= c.header do
|
||||
- c.with_header do
|
||||
= brand_header_logo({add_gitlab_black_text: true})
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
- if header_link?(:user_dropdown)
|
||||
.navbar-collapse
|
||||
%ul.nav.navbar-nav
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
- if current_user.ldap_user?
|
||||
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-my-5' },
|
||||
dismissible: false) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= s_('Profiles|Some options are unavailable for LDAP accounts')
|
||||
|
||||
- if params[:two_factor_auth_enabled_successfully]
|
||||
= render Pajamas::AlertComponent.new(variant: :success,
|
||||
alert_options: { class: 'gl-my-5' },
|
||||
close_button_options: { class: 'js-close-2fa-enabled-success-alert' }) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= html_escape(_('You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can %{anchorOpen}use that key to generate additional recovery codes%{anchorClose}.')) % { anchorOpen: '<a href="%{href}">'.html_safe % { href: help_page_path('user/profile/account/two_factor_authentication', anchor: 'generate-new-recovery-codes-using-ssh') }, anchorClose: '</a>'.html_safe }
|
||||
|
||||
.row.gl-mt-3.js-search-settings-section
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@
|
|||
.gl-mb-3
|
||||
|
||||
= render Pajamas::CardComponent.new(card_options: { class: 'gl-border-0' }, body_options: { class: 'gl-p-0' }) do |c|
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
%ul.list-group.list-group-flush
|
||||
= render partial: 'profiles/active_sessions/active_session', collection: @sessions
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
%main{ role: 'main' }
|
||||
.gl-max-w-80.gl-mx-auto.gl-mt-6
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.header do
|
||||
- c.with_header do
|
||||
%h4.gl-m-0= sprintf(s_('Integrations|Authorize %{integration_name} (%{user}) to use your account?'), { user: @chat_name_params[:chat_name], integration_name: @integration_name })
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
%p
|
||||
= sprintf(s_('Integrations|An application called %{integration_name} is requesting access to your GitLab account. This application was created by GitLab Inc.'), { integration_name: @integration_name })
|
||||
%p
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
%li= s_('SlackIntegration|Run ChatOps jobs.')
|
||||
%p.gl-mb-0
|
||||
= s_("SlackIntegration|You don't have to reauthorize this application if the permission scope changes in future releases.")
|
||||
- c.footer do
|
||||
- c.with_footer do
|
||||
.gl-display-flex
|
||||
= form_tag profile_chat_names_path, method: :post do
|
||||
= hidden_field_tag :token, @chat_name_token.token
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
.row.gl-mt-3
|
||||
.col-md-4
|
||||
= render Pajamas::CardComponent.new(body_options: { class: 'gl-py-0'}) do |c|
|
||||
- c.header do
|
||||
- c.with_header do
|
||||
= _('SSH Key')
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
%ul.content-list
|
||||
%li
|
||||
%span.light= _('Title:')
|
||||
|
|
@ -27,9 +27,9 @@
|
|||
%pre.well-pre
|
||||
= @key.key
|
||||
= render Pajamas::CardComponent.new(body_options: { class: 'gl-py-0'}) do |c|
|
||||
- c.header do
|
||||
- c.with_header do
|
||||
= _('Fingerprints')
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
%ul.content-list
|
||||
%li
|
||||
%span.light= 'MD5:'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
%div
|
||||
- if @user.errors.any?
|
||||
= render Pajamas::AlertComponent.new(variant: :danger) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
%ul
|
||||
- @user.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
= raw @qr_code
|
||||
.col-md-8
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
%p.gl-mt-0.gl-mb-3.gl-font-weight-bold
|
||||
= _("Can't scan the code?")
|
||||
%p.gl-mt-0.gl-mb-3
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
variant: :danger,
|
||||
alert_options: { class: 'gl-mb-3' },
|
||||
dismissible: false) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
- if current_password_required?
|
||||
|
|
@ -130,7 +130,7 @@
|
|||
variant: :danger,
|
||||
alert_options: { class: 'gl-mb-3' },
|
||||
dismissible: false) do |c|
|
||||
= c.body do
|
||||
- c.with_body do
|
||||
= link_to _('Try the troubleshooting steps here.'), help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.js-manage-two-factor-form{ data: { current_password_required: current_password_required?.to_s, profile_two_factor_auth_path: profile_two_factor_auth_path, profile_two_factor_auth_method: 'delete', codes_profile_two_factor_auth_path: codes_profile_two_factor_auth_path, codes_profile_two_factor_auth_method: 'post' } }
|
||||
- else
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
.form-check.gl-mb-3
|
||||
= check_box_tag :only_protected_branches, '1', false, class: 'js-mirror-protected form-check-input'
|
||||
= label_tag :only_protected_branches, _('Mirror only protected branches'), class: 'form-check-label'
|
||||
.form-text.text-muted
|
||||
= _('If enabled, only protected branches will be mirrored.')
|
||||
= link_to _('Learn more.'), help_page_path('user/project/repository/mirror/index.md', anchor: 'mirror-only-protected-branches'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.form-group
|
||||
= render Pajamas::CheckboxTagComponent.new(name: :only_protected_branches,
|
||||
checkbox_options: { class: 'js-mirror-protected' },
|
||||
label_options: { class: 'gl-mb-0!' }) do |c|
|
||||
= c.label do
|
||||
= _('Mirror only protected branches')
|
||||
= c.help_text do
|
||||
= _('If enabled, only protected branches will be mirrored.')
|
||||
= link_to _('Learn more.'), help_page_path('user/project/repository/mirror/index.md', anchor: 'mirror-only-protected-branches'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
.js-grafana-integration{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
|
||||
grafana_integration: { url: grafana_integration_url, token: grafana_integration_masked_token, enabled: grafana_integration_enabled?.to_s } } }
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
.js-operation-settings{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
|
||||
help_page: help_page_path('operations/metrics/dashboards/settings'),
|
||||
external_dashboard: { url: metrics_external_dashboard_url,
|
||||
help_page: help_page_path('operations/metrics/dashboards/settings') },
|
||||
dashboard_timezone: { setting: metrics_dashboard_timezone.upcase } } }
|
||||
|
|
@ -2,14 +2,7 @@
|
|||
- breadcrumb_title _('Monitor Settings')
|
||||
- @force_desktop_expanded_sidebar = true
|
||||
|
||||
- if Feature.disabled?(:remove_monitor_metrics)
|
||||
= render 'projects/settings/operations/metrics_dashboard'
|
||||
|
||||
= render 'projects/settings/operations/error_tracking'
|
||||
= render 'projects/settings/operations/alert_management'
|
||||
= render 'projects/settings/operations/incidents'
|
||||
|
||||
- if Feature.disabled?(:remove_monitor_metrics)
|
||||
= render 'projects/settings/operations/grafana_integration'
|
||||
|
||||
= render_if_exists 'projects/settings/operations/status_page'
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
= gitlab_ui_form_for [protected_branch_entity, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
|
||||
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
|
||||
= render Pajamas::CardComponent.new(card_options: { class: "gl-mb-5" }) do |c|
|
||||
- c.header do
|
||||
- c.with_header do
|
||||
= s_("ProtectedBranch|Protect a branch")
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
= form_errors(@protected_branch)
|
||||
.form-group.row
|
||||
= f.label :name, s_('ProtectedBranch|Branch:'), class: 'col-sm-12'
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
- force_push_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: force_push_docs_url }
|
||||
= (s_("ProtectedBranch|Allow all users with push access to %{tag_start}force push%{tag_end}.") % { tag_start: force_push_link_start, tag_end: '</a>' }).html_safe
|
||||
= render_if_exists 'protected_branches/ee/code_owner_approval_form', f: f, protected_branch_entity: protected_branch_entity
|
||||
- c.footer do
|
||||
- c.with_footer do
|
||||
= f.submit s_('ProtectedBranch|Protect'), disabled: true, data: { qa_selector: 'protect_button' }, pajamas_button: true
|
||||
|
||||
.js-alert-protected-branch-created-container.gl-mb-5
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
'aria-live' => 'assertive',
|
||||
data: { testid: 'welcome-form' } }) do |f|
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.body do
|
||||
- c.with_body do
|
||||
.devise-errors
|
||||
= render 'devise/shared/error_messages', resource: current_user
|
||||
.row
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: fix_dora_deployment_frequency_calculation
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116644
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/404565
|
||||
milestone: '15.11'
|
||||
name: add_validation_for_push_rules
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121030
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/411901
|
||||
milestone: '16.1'
|
||||
type: development
|
||||
group: group::optimize
|
||||
default_enabled: true
|
||||
group: group::source code
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: environment_settings_to_graphql
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121091
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/412332
|
||||
milestone: '16.1'
|
||||
type: development
|
||||
group: group::environments
|
||||
default_enabled: false
|
||||
|
|
@ -55,7 +55,7 @@ end
|
|||
OPTIONAL_REVIEW_TEMPLATE = '%{role} review is optional for %{category}'
|
||||
NOT_AVAILABLE_TEMPLATES = {
|
||||
default: 'No %{role} available',
|
||||
product_intelligence: group_not_available_template('#g_analyze_analytics_instrumentation', '@gitlab-org/analytics-section/product-intelligence/engineers'),
|
||||
analytics_instrumentation: group_not_available_template('#g_analyze_analytics_instrumentation', '@gitlab-org/analytics-section/analytics-instrumentation/engineers'),
|
||||
import_integrate_be: group_not_available_template('#g_manage_import_and_integrate', '@gitlab-org/manage/import-and-integrate'),
|
||||
import_integrate_fe: group_not_available_template('#g_manage_import_and_integrate', '@gitlab-org/manage/import-and-integrate')
|
||||
}.freeze
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddFieldsToAbuseReports < Gitlab::Database::Migration[2.1]
|
||||
RESOLVED_BY_INDEX = 'index_abuse_reports_on_resolved_by_id'
|
||||
ASSIGNEE_INDEX = 'index_abuse_reports_on_assignee_id'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :abuse_reports, :resolved_by_id, :int, null: true
|
||||
add_column :abuse_reports, :assignee_id, :int, null: true
|
||||
add_column :abuse_reports, :mitigation_steps, :text, null: true
|
||||
add_column :abuse_reports, :evidence, :jsonb, null: true
|
||||
end
|
||||
|
||||
add_text_limit :abuse_reports, :mitigation_steps, 1000
|
||||
add_concurrent_index :abuse_reports, :resolved_by_id, name: RESOLVED_BY_INDEX
|
||||
add_concurrent_index :abuse_reports, :assignee_id, name: ASSIGNEE_INDEX
|
||||
end
|
||||
|
||||
def down
|
||||
change_table :abuse_reports do |t|
|
||||
t.remove :resolved_by_id
|
||||
t.remove :assignee_id
|
||||
t.remove :mitigation_steps
|
||||
t.remove :evidence
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddForeignKeyConstraintsToAbuseReports < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
return if foreign_key_exists?(:abuse_reports, column: :resolved_by_id)
|
||||
|
||||
add_concurrent_foreign_key :abuse_reports, :users,
|
||||
column: :resolved_by_id,
|
||||
null: true,
|
||||
on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key_if_exists :abuse_reports, column: :resolved_by_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddForeignKeyToAbuseReportsForAssignee < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
return if foreign_key_exists?(:abuse_reports, column: :assignee_id)
|
||||
|
||||
add_concurrent_foreign_key :abuse_reports, :users,
|
||||
column: :assignee_id,
|
||||
null: true,
|
||||
on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key_if_exists :abuse_reports, column: :assignee_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNewAsyncIndexTableNameLengthConstraint < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
CONSTRAINT_NAME = 'check_schema_and_name_length'
|
||||
|
||||
def up
|
||||
add_text_limit :postgres_async_indexes, :table_name, 127, constraint_name: CONSTRAINT_NAME, validate: false
|
||||
end
|
||||
|
||||
def down
|
||||
remove_text_limit :postgres_async_indexes, :table_name, constraint_name: CONSTRAINT_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ValidateAsyncIndexTableNameLengthConstraint < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
validate_text_limit :postgres_async_indexes, :table_name, constraint_name: 'check_schema_and_name_length'
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op because we cannot invalidate a constraint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveOldAsyncIndexTableNameLengthConstraint < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
OLD_LENGTH_LIMIT = 63
|
||||
|
||||
def up
|
||||
remove_text_limit :postgres_async_indexes, :table_name
|
||||
end
|
||||
|
||||
def down
|
||||
# Remove items that might break the old length validation. (unlikely to happen)
|
||||
define_batchable_model('postgres_async_indexes').each_batch do |indexes|
|
||||
indexes.where('CHAR_LENGTH(table_name) > ?', OLD_LENGTH_LIMIT).delete_all
|
||||
end
|
||||
|
||||
add_text_limit :postgres_async_indexes, :table_name, OLD_LENGTH_LIMIT
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
8cd0b7369ad654e72fb12b9ae0d54cb38ada4b24a20358541dc5ab18ae60605c
|
||||
|
|
@ -0,0 +1 @@
|
|||
7a34b874349603c65c5ad8d71033a8ec23e7253c51bef20314a5c45d8da9903f
|
||||
|
|
@ -0,0 +1 @@
|
|||
7169d207128db503be38e1106f63a7c2e752ceacbfba93142a731267ce0fa0fe
|
||||
|
|
@ -0,0 +1 @@
|
|||
45f116be064d575bba2acab71458a89dbe5332c3cfb4544eb51638bf86081123
|
||||
|
|
@ -0,0 +1 @@
|
|||
a9408102327280918366975b599985f3e382ebd493267fd010ad2dcbe6140eb3
|
||||
|
|
@ -0,0 +1 @@
|
|||
662b78b3f796cec08f325ec71389bdaf9efa0695b6bfe224b18295175f1383bf
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue