Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-05-23 18:08:27 +00:00
parent abdb550f69
commit 3ca896b640
160 changed files with 1176 additions and 1639 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1 @@
14.20.0
14.21.0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -105,7 +105,7 @@ export default {
*/
this.$nextTick(() => {
if (!this.noteUpdateDirty) {
this.autosaveDiscussion.reset();
this.autosaveDiscussion?.reset();
}
});
},

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,9 @@
mutation updateEnvironment($input: EnvironmentUpdateInput!) {
environmentUpdate(input: $input) {
environment {
id
path
}
errors
}
}

View File

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

View File

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

View File

@ -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}`,
});
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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}`,
});
};

View File

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

View File

@ -1,2 +0,0 @@
export const SET_EXTERNAL_DASHBOARD_URL = 'SET_EXTERNAL_DASHBOARD_URL';
export const SET_DASHBOARD_TIMEZONE = 'SET_DASHBOARD_TIMEZONE';

View File

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

View File

@ -1,10 +0,0 @@
export default (initialState = {}) => ({
operationsSettingsEndpoint: initialState.operationsSettingsEndpoint,
helpPage: initialState.helpPage,
externalDashboard: {
url: initialState.externalDashboardUrl,
},
dashboardTimezone: {
selected: initialState.dashboardTimezoneSetting,
},
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,5 +2,6 @@ fragment AwardEmojiFragment on AwardEmoji {
name
user {
id
name
}
}

View File

@ -1,6 +1,6 @@
// Common
.diff-file {
margin-bottom: $gl-padding;
padding-bottom: $gl-padding;
&.has-body {
.file-title {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
8cd0b7369ad654e72fb12b9ae0d54cb38ada4b24a20358541dc5ab18ae60605c

View File

@ -0,0 +1 @@
7a34b874349603c65c5ad8d71033a8ec23e7253c51bef20314a5c45d8da9903f

View File

@ -0,0 +1 @@
7169d207128db503be38e1106f63a7c2e752ceacbfba93142a731267ce0fa0fe

View File

@ -0,0 +1 @@
45f116be064d575bba2acab71458a89dbe5332c3cfb4544eb51638bf86081123

View File

@ -0,0 +1 @@
a9408102327280918366975b599985f3e382ebd493267fd010ad2dcbe6140eb3

View File

@ -0,0 +1 @@
662b78b3f796cec08f325ec71389bdaf9efa0695b6bfe224b18295175f1383bf

Some files were not shown because too many files have changed in this diff Show More