diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-08 15:09:53 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-08 15:09:53 +0300 |
commit | 148b75b329294f6b6ae409bbf8d70590e63c6bc9 (patch) | |
tree | 30918a97e353067ff9c99e04fb7e296305d130b7 /app | |
parent | 707742e59ca57d1f2ea00d65fa35a7b9a5ded398 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
18 files changed, 145 insertions, 403 deletions
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index ee319990290..8c72971682d 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -185,11 +185,11 @@ export const fetchDiffFilesMeta = ({ commit, state }) => { .get(mergeUrlParams(urlParams, state.endpointMetadata)) .then(({ data }) => { const strippedData = { ...data }; - delete strippedData.diff_files; + commit(types.SET_LOADING, false); commit(types.SET_MERGE_REQUEST_DIFFS, data.merge_request_diffs || []); - commit(types.SET_DIFF_DATA, strippedData); + commit(types.SET_DIFF_METADATA, strippedData); worker.postMessage(prepareDiffData(data, state.diffFiles)); diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js index 25184028799..30097239aaa 100644 --- a/app/assets/javascripts/diffs/store/mutation_types.js +++ b/app/assets/javascripts/diffs/store/mutation_types.js @@ -3,7 +3,7 @@ export const SET_LOADING = 'SET_LOADING'; export const SET_BATCH_LOADING = 'SET_BATCH_LOADING'; export const SET_RETRIEVING_BATCHES = 'SET_RETRIEVING_BATCHES'; -export const SET_DIFF_DATA = 'SET_DIFF_DATA'; +export const SET_DIFF_METADATA = 'SET_DIFF_METADATA'; export const SET_DIFF_DATA_BATCH = 'SET_DIFF_DATA_BATCH'; export const SET_DIFF_FILES = 'SET_DIFF_FILES'; diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 69ae3f705e3..90940d82226 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -66,17 +66,10 @@ export default { updateDiffFilesInState(state, files); }, - [types.SET_DIFF_DATA](state, data) { - let files = state.diffFiles; - - if (window.location.search.indexOf('diff_id') !== -1 && data.diff_files) { - files = prepareDiffData(data, files); - } - + [types.SET_DIFF_METADATA](state, data) { Object.assign(state, { ...convertObjectPropsToCamelCase(data), }); - updateDiffFilesInState(state, files); }, [types.SET_DIFF_DATA_BATCH](state, data) { diff --git a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue index e236834d8e1..ac6a0871153 100644 --- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue +++ b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue @@ -2,16 +2,16 @@ import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; import { isEqual, get, isEmpty } from 'lodash'; import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.graphql'; -import { FETCH_SETTINGS_ERROR_MESSAGE } from '../../shared/constants'; - -import SettingsForm from './settings_form.vue'; import { + FETCH_SETTINGS_ERROR_MESSAGE, UNAVAILABLE_FEATURE_TITLE, UNAVAILABLE_FEATURE_INTRO_TEXT, UNAVAILABLE_USER_FEATURE_TEXT, UNAVAILABLE_ADMIN_FEATURE_TEXT, } from '../constants'; +import SettingsForm from './settings_form.vue'; + export default { components: { SettingsForm, diff --git a/app/assets/javascripts/registry/settings/components/settings_form.vue b/app/assets/javascripts/registry/settings/components/settings_form.vue index 3eab7e6d038..c46c633d274 100644 --- a/app/assets/javascripts/registry/settings/components/settings_form.vue +++ b/app/assets/javascripts/registry/settings/components/settings_form.vue @@ -4,8 +4,6 @@ import Tracking from '~/tracking'; import { UPDATE_SETTINGS_ERROR_MESSAGE, UPDATE_SETTINGS_SUCCESS_MESSAGE, -} from '~/registry/shared/constants'; -import { SET_CLEANUP_POLICY_BUTTON, KEEP_HEADER_TEXT, KEEP_INFO_TEXT, @@ -21,7 +19,7 @@ import { CADENCE_LABEL, EXPIRATION_POLICY_FOOTER_NOTE, } from '~/registry/settings/constants'; -import { formOptionsGenerator } from '~/registry/shared/utils'; +import { formOptionsGenerator } from '~/registry/settings/utils'; import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.graphql'; import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update'; import ExpirationDropdown from './expiration_dropdown.vue'; diff --git a/app/assets/javascripts/registry/settings/constants.js b/app/assets/javascripts/registry/settings/constants.js index 1dd533ce665..21c54299632 100644 --- a/app/assets/javascripts/registry/settings/constants.js +++ b/app/assets/javascripts/registry/settings/constants.js @@ -52,4 +52,40 @@ export const EXPIRATION_POLICY_FOOTER_NOTE = s__( 'ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time', ); +export const KEEP_N_OPTIONS = [ + { key: 'ONE_TAG', variable: 1, default: false }, + { key: 'FIVE_TAGS', variable: 5, default: false }, + { key: 'TEN_TAGS', variable: 10, default: true }, + { key: 'TWENTY_FIVE_TAGS', variable: 25, default: false }, + { key: 'FIFTY_TAGS', variable: 50, default: false }, + { key: 'ONE_HUNDRED_TAGS', variable: 100, default: false }, +]; + +export const CADENCE_OPTIONS = [ + { key: 'EVERY_DAY', label: __('Every day'), default: true }, + { key: 'EVERY_WEEK', label: __('Every week'), default: false }, + { key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false }, + { key: 'EVERY_MONTH', label: __('Every month'), default: false }, + { key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false }, +]; + +export const OLDER_THAN_OPTIONS = [ + { key: 'SEVEN_DAYS', variable: 7, default: false }, + { key: 'FOURTEEN_DAYS', variable: 14, default: false }, + { key: 'THIRTY_DAYS', variable: 30, default: false }, + { key: 'NINETY_DAYS', variable: 90, default: true }, +]; + +export const FETCH_SETTINGS_ERROR_MESSAGE = s__( + 'ContainerRegistry|Something went wrong while fetching the cleanup policy.', +); + +export const UPDATE_SETTINGS_ERROR_MESSAGE = s__( + 'ContainerRegistry|Something went wrong while updating the cleanup policy.', +); + +export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__( + 'ContainerRegistry|Cleanup policy successfully saved.', +); + export const NAME_REGEX_LENGTH = 255; diff --git a/app/assets/javascripts/registry/shared/utils.js b/app/assets/javascripts/registry/settings/utils.js index 5c8c505f835..51b4fb6bdb8 100644 --- a/app/assets/javascripts/registry/shared/utils.js +++ b/app/assets/javascripts/registry/settings/utils.js @@ -6,21 +6,6 @@ export const findDefaultOption = options => { return item ? item.key : null; }; -export const mapComputedToEvent = (list, root) => { - const result = {}; - list.forEach(e => { - result[e] = { - get() { - return this[root][e]; - }, - set(value) { - this.$emit('input', { newValue: { ...this[root], [e]: value }, modified: e }); - }, - }; - }); - return result; -}; - export const olderThanTranslationGenerator = variable => n__('%d day', '%d days', variable); export const keepNTranslationGenerator = variable => diff --git a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue b/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue deleted file mode 100644 index 2b8e9f6ff64..00000000000 --- a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue +++ /dev/null @@ -1,258 +0,0 @@ -<script> -import { uniqueId } from 'lodash'; -import { GlFormGroup, GlToggle, GlFormSelect, GlFormTextarea, GlSprintf } from '@gitlab/ui'; -import { - NAME_REGEX_LENGTH, - ENABLED_TEXT, - DISABLED_TEXT, - TEXT_AREA_INVALID_FEEDBACK, - EXPIRATION_INTERVAL_LABEL, - EXPIRATION_SCHEDULE_LABEL, - KEEP_N_LABEL, - NAME_REGEX_LABEL, - NAME_REGEX_PLACEHOLDER, - NAME_REGEX_DESCRIPTION, - NAME_REGEX_KEEP_LABEL, - NAME_REGEX_KEEP_PLACEHOLDER, - NAME_REGEX_KEEP_DESCRIPTION, - ENABLE_TOGGLE_LABEL, - ENABLE_TOGGLE_DESCRIPTION, -} from '../constants'; -import { mapComputedToEvent } from '../utils'; - -export default { - components: { - GlFormGroup, - GlToggle, - GlFormSelect, - GlFormTextarea, - GlSprintf, - }, - props: { - formOptions: { - type: Object, - required: false, - default: () => ({}), - }, - apiErrors: { - type: Object, - required: false, - default: null, - }, - isLoading: { - type: Boolean, - required: false, - default: false, - }, - value: { - type: Object, - required: false, - default: () => ({}), - }, - labelCols: { - type: [Number, String], - required: false, - default: 3, - }, - labelAlign: { - type: String, - required: false, - default: 'right', - }, - }, - i18n: { - ENABLE_TOGGLE_LABEL, - ENABLE_TOGGLE_DESCRIPTION, - }, - selectList: [ - { - name: 'expiration-policy-interval', - label: EXPIRATION_INTERVAL_LABEL, - model: 'olderThan', - }, - { - name: 'expiration-policy-schedule', - label: EXPIRATION_SCHEDULE_LABEL, - model: 'cadence', - }, - { - name: 'expiration-policy-latest', - label: KEEP_N_LABEL, - model: 'keepN', - }, - ], - textAreaList: [ - { - name: 'expiration-policy-name-matching', - label: NAME_REGEX_LABEL, - model: 'nameRegex', - placeholder: NAME_REGEX_PLACEHOLDER, - description: NAME_REGEX_DESCRIPTION, - }, - { - name: 'expiration-policy-keep-name', - label: NAME_REGEX_KEEP_LABEL, - model: 'nameRegexKeep', - placeholder: NAME_REGEX_KEEP_PLACEHOLDER, - description: NAME_REGEX_KEEP_DESCRIPTION, - }, - ], - data() { - return { - uniqueId: uniqueId(), - }; - }, - computed: { - ...mapComputedToEvent( - ['enabled', 'cadence', 'olderThan', 'keepN', 'nameRegex', 'nameRegexKeep'], - 'value', - ), - policyEnabledText() { - return this.enabled ? ENABLED_TEXT : DISABLED_TEXT; - }, - textAreaValidation() { - const nameRegexErrors = this.apiErrors?.nameRegex || this.validateRegexLength(this.nameRegex); - const nameKeepRegexErrors = - this.apiErrors?.nameRegexKeep || this.validateRegexLength(this.nameRegexKeep); - - return { - /* - * The state has this form: - * null: gray border, no message - * true: green border, no message ( because none is configured) - * false: red border, error message - * So in this function we keep null if the are no message otherwise we 'invert' the error message - */ - nameRegex: { - state: nameRegexErrors === null ? null : !nameRegexErrors, - message: nameRegexErrors, - }, - nameRegexKeep: { - state: nameKeepRegexErrors === null ? null : !nameKeepRegexErrors, - message: nameKeepRegexErrors, - }, - }; - }, - fieldsValidity() { - return ( - this.textAreaValidation.nameRegex.state !== false && - this.textAreaValidation.nameRegexKeep.state !== false - ); - }, - isFormElementDisabled() { - return !this.enabled || this.isLoading; - }, - }, - watch: { - fieldsValidity: { - immediate: true, - handler(valid) { - if (valid) { - this.$emit('validated'); - } else { - this.$emit('invalidated'); - } - }, - }, - }, - methods: { - validateRegexLength(value) { - if (!value) { - return null; - } - return value.length <= NAME_REGEX_LENGTH ? '' : TEXT_AREA_INVALID_FEEDBACK; - }, - idGenerator(id) { - return `${id}_${this.uniqueId}`; - }, - updateModel(value, key) { - this[key] = value; - }, - }, -}; -</script> - -<template> - <div ref="form-elements" class="gl-line-height-20"> - <gl-form-group - :id="idGenerator('expiration-policy-toggle-group')" - :label-cols="labelCols" - :label-align="labelAlign" - :label-for="idGenerator('expiration-policy-toggle')" - :label="$options.i18n.ENABLE_TOGGLE_LABEL" - > - <div class="gl-display-flex"> - <gl-toggle - :id="idGenerator('expiration-policy-toggle')" - v-model="enabled" - :disabled="isLoading" - /> - <span class="gl-mb-3 gl-ml-3 gl-line-height-20"> - <gl-sprintf :message="$options.i18n.ENABLE_TOGGLE_DESCRIPTION"> - <template #toggleStatus> - <strong>{{ policyEnabledText }}</strong> - </template> - </gl-sprintf> - </span> - </div> - </gl-form-group> - - <gl-form-group - v-for="select in $options.selectList" - :id="idGenerator(`${select.name}-group`)" - :key="select.name" - :label-cols="labelCols" - :label-align="labelAlign" - :label-for="idGenerator(select.name)" - :label="select.label" - > - <gl-form-select - :id="idGenerator(select.name)" - :value="value[select.model]" - :disabled="isFormElementDisabled" - @input="updateModel($event, select.model)" - > - <option v-for="option in formOptions[select.model]" :key="option.key" :value="option.key"> - {{ option.label }} - </option> - </gl-form-select> - </gl-form-group> - - <gl-form-group - v-for="textarea in $options.textAreaList" - :id="idGenerator(`${textarea.name}-group`)" - :key="textarea.name" - :label-cols="labelCols" - :label-align="labelAlign" - :label-for="idGenerator(textarea.name)" - :state="textAreaValidation[textarea.model].state" - :invalid-feedback="textAreaValidation[textarea.model].message" - > - <template #label> - <gl-sprintf :message="textarea.label"> - <template #italic="{content}"> - <i>{{ content }}</i> - </template> - </gl-sprintf> - </template> - <gl-form-textarea - :id="idGenerator(textarea.name)" - :value="value[textarea.model]" - :placeholder="textarea.placeholder" - :state="textAreaValidation[textarea.model].state" - :disabled="isFormElementDisabled" - trim - @input="updateModel($event, textarea.model)" - /> - <template #description> - <span ref="regex-description"> - <gl-sprintf :message="textarea.description"> - <template #code="{content}"> - <code>{{ content }}</code> - </template> - </gl-sprintf> - </span> - </template> - </gl-form-group> - </div> -</template> diff --git a/app/assets/javascripts/registry/shared/constants.js b/app/assets/javascripts/registry/shared/constants.js deleted file mode 100644 index d1e3d93938b..00000000000 --- a/app/assets/javascripts/registry/shared/constants.js +++ /dev/null @@ -1,69 +0,0 @@ -import { s__, __ } from '~/locale'; - -export const FETCH_SETTINGS_ERROR_MESSAGE = s__( - 'ContainerRegistry|Something went wrong while fetching the cleanup policy.', -); - -export const UPDATE_SETTINGS_ERROR_MESSAGE = s__( - 'ContainerRegistry|Something went wrong while updating the cleanup policy.', -); - -export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__( - 'ContainerRegistry|Cleanup policy successfully saved.', -); - -export const NAME_REGEX_LENGTH = 255; - -export const ENABLED_TEXT = __('Enabled'); -export const DISABLED_TEXT = __('Disabled'); - -export const ENABLE_TOGGLE_LABEL = s__('ContainerRegistry|Cleanup policy:'); -export const ENABLE_TOGGLE_DESCRIPTION = s__( - 'ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion', -); - -export const TEXT_AREA_INVALID_FEEDBACK = s__( - 'ContainerRegistry|The value of this input should be less than 256 characters', -); - -export const EXPIRATION_INTERVAL_LABEL = s__('ContainerRegistry|Expiration interval:'); -export const EXPIRATION_SCHEDULE_LABEL = s__('ContainerRegistry|Expiration schedule:'); -export const KEEP_N_LABEL = s__('ContainerRegistry|Number of tags to retain:'); -export const NAME_REGEX_LABEL = s__( - 'ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}', -); -export const NAME_REGEX_PLACEHOLDER = ''; -export const NAME_REGEX_DESCRIPTION = s__( - 'ContainerRegistry|Wildcards such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}', -); -export const NAME_REGEX_KEEP_LABEL = s__( - 'ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}', -); -export const NAME_REGEX_KEEP_PLACEHOLDER = ''; -export const NAME_REGEX_KEEP_DESCRIPTION = s__( - 'ContainerRegistry|Wildcards such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported', -); - -export const KEEP_N_OPTIONS = [ - { variable: 1, key: 'ONE_TAG', default: false }, - { variable: 5, key: 'FIVE_TAGS', default: false }, - { variable: 10, key: 'TEN_TAGS', default: true }, - { variable: 25, key: 'TWENTY_FIVE_TAGS', default: false }, - { variable: 50, key: 'FIFTY_TAGS', default: false }, - { variable: 100, key: 'ONE_HUNDRED_TAGS', default: false }, -]; - -export const CADENCE_OPTIONS = [ - { key: 'EVERY_DAY', label: __('Every day'), default: true }, - { key: 'EVERY_WEEK', label: __('Every week'), default: false }, - { key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false }, - { key: 'EVERY_MONTH', label: __('Every month'), default: false }, - { key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false }, -]; - -export const OLDER_THAN_OPTIONS = [ - { key: 'SEVEN_DAYS', variable: 7, default: false }, - { key: 'FOURTEEN_DAYS', variable: 14, default: false }, - { key: 'THIRTY_DAYS', variable: 30, default: false }, - { key: 'NINETY_DAYS', variable: 90, default: true }, -]; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 433dcf2e219..d121daf0aa7 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -464,7 +464,7 @@ export default { <security-reports-app v-if="shouldRenderSecurityReport" :pipeline-id="mr.pipeline.id" - :project-id="mr.targetProjectId" + :project-id="mr.sourceProjectId" :security-reports-docs-path="mr.securityReportsDocsPath" /> diff --git a/app/assets/stylesheets/fontawesome_custom.scss b/app/assets/stylesheets/fontawesome_custom.scss index a5e357c8fda..693632c3af1 100644 --- a/app/assets/stylesheets/fontawesome_custom.scss +++ b/app/assets/stylesheets/fontawesome_custom.scss @@ -27,10 +27,6 @@ font-size: 2em; } -.fa-caret-down::before { - content: '\f0d7'; -} - .fa-exclamation-triangle::before { content: '\f071'; } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 1e051b3bcf0..db41d28575f 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -231,16 +231,6 @@ color: $gray-700; } - .fa-caret-down { - margin-left: 5px; - } - - &.dropdown-toggle { - .fa-caret-down { - margin-left: 3px; - } - } - &.btn-text-field { width: 100%; text-align: left; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 51870ace23b..1caf62067a6 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -201,14 +201,6 @@ ul.related-merge-requests > li { } } } - - .create-merge-request-dropdown-toggle { - .fa-caret-down { - pointer-events: none; - color: inherit; - margin-left: 0; - } - } } .discussion-reply-holder { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index e5a9e99b2fb..974f74a4368 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -148,15 +148,6 @@ fill: $layout-link-gray; } - .fa-caret-down { - margin-left: 3px; - line-height: 0; - - &.dropdown-btn-icon { - margin-left: 0; - } - } - .notifications-icon { top: 1px; margin-right: 0; diff --git a/app/models/ci/build_dependencies.rb b/app/models/ci/build_dependencies.rb index 7e690dc413c..2c6a9a63bdb 100644 --- a/app/models/ci/build_dependencies.rb +++ b/app/models/ci/build_dependencies.rb @@ -2,6 +2,8 @@ module Ci class BuildDependencies + include ::Gitlab::Utils::StrongMemoize + attr_reader :processable def initialize(processable) @@ -9,7 +11,7 @@ module Ci end def all - (local + cross_project).uniq + (local + cross_pipeline + cross_project).uniq end # Dependencies local to the given pipeline @@ -23,6 +25,14 @@ module Ci deps end + # Dependencies from the same parent-pipeline hierarchy excluding + # the current job's pipeline + def cross_pipeline + strong_memoize(:cross_pipeline) do + fetch_dependencies_in_hierarchy + end + end + # Dependencies that are defined by project and ref def cross_project [] @@ -33,7 +43,7 @@ module Ci end def valid? - valid_local? && valid_cross_project? + valid_local? && valid_cross_pipeline? && valid_cross_project? end private @@ -44,6 +54,54 @@ module Ci ::Ci::Build end + def fetch_dependencies_in_hierarchy + deps_specifications = specified_cross_pipeline_dependencies + return [] if deps_specifications.empty? + + deps_specifications = expand_variables_and_validate(deps_specifications) + jobs_in_pipeline_hierarchy(deps_specifications) + end + + def jobs_in_pipeline_hierarchy(deps_specifications) + all_pipeline_ids = [] + all_job_names = [] + + deps_specifications.each do |spec| + all_pipeline_ids << spec[:pipeline] + all_job_names << spec[:job] + end + + model_class.latest.success + .in_pipelines(processable.pipeline.same_family_pipeline_ids) + .in_pipelines(all_pipeline_ids.uniq) + .by_name(all_job_names.uniq) + .select do |dependency| + # the query may not return exact matches pipeline-job, so we filter + # them separately. + deps_specifications.find do |spec| + spec[:pipeline] == dependency.pipeline_id && + spec[:job] == dependency.name + end + end + end + + def expand_variables_and_validate(specifications) + specifications.map do |spec| + pipeline = ExpandVariables.expand(spec[:pipeline].to_s, processable_variables).to_i + # current pipeline is not allowed because local dependencies + # should be used instead. + next if pipeline == processable.pipeline_id + + job = ExpandVariables.expand(spec[:job], processable_variables) + + { job: job, pipeline: pipeline } + end.compact + end + + def valid_cross_pipeline? + cross_pipeline.size == specified_cross_pipeline_dependencies.size + end + def valid_local? return true if Feature.enabled?(:ci_disable_validates_dependencies) @@ -78,6 +136,22 @@ module Ci scope.where(name: processable.options[:dependencies]) end + + def processable_variables + -> { processable.simple_variables_without_dependencies } + end + + def specified_cross_pipeline_dependencies + strong_memoize(:specified_cross_pipeline_dependencies) do + next [] unless Feature.enabled?(:ci_cross_pipeline_artifacts_download, processable.project, default_enabled: false) + + specified_cross_dependencies.select { |dep| dep[:pipeline] && dep[:artifacts] } + end + end + + def specified_cross_dependencies + Array(processable.options[:cross_dependencies]) + end end end diff --git a/app/models/identity.rb b/app/models/identity.rb index 40d9f856abf..fc97c68b756 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -18,6 +18,9 @@ class Identity < ApplicationRecord scope :with_extern_uid, ->(provider, extern_uid) do iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider) end + scope :with_any_extern_uid, ->(provider) do + where.not(extern_uid: nil).with_provider(provider) + end def ldap? Gitlab::Auth::OAuth::Provider.ldap_provider?(provider) diff --git a/app/models/user.rb b/app/models/user.rb index 083117ef8ec..4acb287b51c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1045,7 +1045,7 @@ class User < ApplicationRecord end def require_personal_access_token_creation_for_git_auth? - return false if allow_password_authentication_for_git? || ldap_user? + return false if allow_password_authentication_for_git? || password_based_omniauth_user? PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none? end @@ -1063,7 +1063,7 @@ class User < ApplicationRecord end def allow_password_authentication_for_git? - Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user? + Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !password_based_omniauth_user? end def can_change_username? @@ -1143,6 +1143,18 @@ class User < ApplicationRecord namespace.find_fork_of(project) end + def password_based_omniauth_user? + ldap_user? || crowd_user? + end + + def crowd_user? + if identities.loaded? + identities.find { |identity| identity.provider == 'crowd' && identity.extern_uid.present? } + else + identities.with_any_extern_uid('crowd').exists? + end + end + def ldap_user? if identities.loaded? identities.find { |identity| Gitlab::Auth::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? } diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb index 8ad4f59373d..1f90263ba4a 100644 --- a/app/services/projects/prometheus/alerts/notify_service.rb +++ b/app/services/projects/prometheus/alerts/notify_service.rb @@ -17,10 +17,10 @@ module Projects SUPPORTED_VERSION = '4' - def execute(token, _integration = nil) + def execute(token, integration = nil) return bad_request unless valid_payload_size? return unprocessable_entity unless self.class.processable?(params) - return unauthorized unless valid_alert_manager_token?(token) + return unauthorized unless valid_alert_manager_token?(token, integration) process_prometheus_alerts @@ -53,9 +53,9 @@ module Projects params['alerts'] end - def valid_alert_manager_token?(token) + def valid_alert_manager_token?(token, integration) valid_for_manual?(token) || - valid_for_alerts_endpoint?(token) || + valid_for_alerts_endpoint?(token, integration) || valid_for_managed?(token) end @@ -70,11 +70,10 @@ module Projects end end - def valid_for_alerts_endpoint?(token) - return false unless project.alerts_service_activated? + def valid_for_alerts_endpoint?(token, integration) + return false unless integration&.active? - # Here we are enforcing the existence of the token - compare_token(token, project.alerts_service.token) + compare_token(token, integration.token) end def valid_for_managed?(token) |