Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									315243f877
								
							
						
					
					
						commit
						64f7eb2b37
					
				|  | @ -112,6 +112,7 @@ | |||
| .workhorse-patterns: &workhorse-patterns | ||||
|   - "GITLAB_WORKHORSE_VERSION" | ||||
|   - "workhorse/**/*" | ||||
|   - ".gitlab/ci/workhorse.gitlab-ci.yml" | ||||
| 
 | ||||
| .yaml-lint-patterns: &yaml-lint-patterns | ||||
|   - ".gitlab-ci.yml" | ||||
|  |  | |||
|  | @ -8,3 +8,39 @@ workhorse: | |||
|     - git checkout . | ||||
|     - scripts/update-workhorse check | ||||
|     - make -C workhorse | ||||
| 
 | ||||
| workhorse:verify: | ||||
|   extends: .workhorse:rules:workhorse | ||||
|   image: ${GITLAB_DEPENDENCY_PROXY}golang:1.15 | ||||
|   stage: test | ||||
|   needs: [] | ||||
|   script: | ||||
|     - make -C workhorse verify | ||||
| 
 | ||||
| .workhorse:test: | ||||
|   extends: .workhorse:rules:workhorse | ||||
|   services: | ||||
|     - name: registry.gitlab.com/gitlab-org/build/cng/gitaly:latest | ||||
|       # Disable the hooks so we don't have to stub the GitLab API | ||||
|       command: ["/usr/bin/env", "GITALY_TESTING_NO_GIT_HOOKS=1", "/scripts/process-wrapper"] | ||||
|       alias: gitaly | ||||
|   variables: | ||||
|     GITALY_ADDRESS: "tcp://gitaly:8075" | ||||
|   stage: test | ||||
|   needs: [] | ||||
|   script: | ||||
|     - go version | ||||
|     - apt-get update && apt-get -y install libimage-exiftool-perl | ||||
|     - make -C workhorse test | ||||
| 
 | ||||
| workhorse:test using go 1.13: | ||||
|   extends: .workhorse:test | ||||
|   image: ${GITLAB_DEPENDENCY_PROXY}golang:1.13 | ||||
| 
 | ||||
| workhorse:test using go 1.14: | ||||
|   extends: .workhorse:test | ||||
|   image: ${GITLAB_DEPENDENCY_PROXY}golang:1.14 | ||||
| 
 | ||||
| workhorse:test using go 1.15: | ||||
|   extends: .workhorse:test | ||||
|   image: ${GITLAB_DEPENDENCY_PROXY}golang:1.15 | ||||
|  |  | |||
|  | @ -1,20 +1,20 @@ | |||
| <!-- Actionable insights must recommend an action that needs to take place. An actionable insight both defines the _insight_ and clearly calls out _action_ or next step required to improve based on the result of the research observation or data. Actionable insights are tracked over time and will include follow-up. Learn more in the handbook here: https://about.gitlab.com/handbook/engineering/ux/ux-research-training/research-insights/#actionable-insights --> | ||||
| <!-- Actionable insights must recommend an action that needs to take place. An actionable insight both defines the insight and clearly calls out action or next step required to improve based on the result of the research observation or data. Actionable insights are tracked over time and will include follow-up. Learn more in the handbook here: https://about.gitlab.com/handbook/engineering/ux/ux-research-training/research-insights/#actionable-insights --> | ||||
| 
 | ||||
| ### Insight | ||||
| <!-- Describe the insight itself: often the problem, finding, or observation. _What_ is currently happening? --> | ||||
| <!-- Describe the insight itself: often the problem, finding, or observation. --> | ||||
| 
 | ||||
| ### Supporting evidence | ||||
| <!-- Describe _why_ the problem is happening, or more details behind the finding or observation. Try to include quotes or specific data collected. Feel free to link the Actionable insight from Dovetail here if applicable instead of retyping details. --> | ||||
| <!-- Describe why the problem is happening, or more details behind the finding or observation. Try to include quotes or specific data collected. Feel free to link the Actionable insight from Dovetail here if applicable instead of retyping details. --> | ||||
| 
 | ||||
| ### Action | ||||
| <!--Describe the next step or action that needs to take place as a result of the research. The action should be clearly defined, achievable, and directly tied back to the insight. Make sure to use directive terminology, such as: conduct, explore, redesign, etc. _How_ do we take a step toward improving the experience? --> | ||||
| <!--Describe the next step or action that needs to take place as a result of the research. The action should be clearly defined, achievable, and directly tied back to the insight. Make sure to use directive terminology, such as: conduct, explore, redesign, etc. --> | ||||
| 
 | ||||
| ### Resources | ||||
|  <!--Add resources as links below or as related issues. --> | ||||
| 
 | ||||
| - **Dovetail link:** [{Paste URL here}](url) | ||||
| - **Research issue link:** [{Paste URL here}](url) | ||||
| - **Follow-up issue:** [{Paste URL here}](url) | ||||
| - :dove: [Dovetail project](Paste URL for Dovetail project here) | ||||
| - :mag: [Research issue](Paste URL for research issue here) | ||||
| - :footprints: [Follow-up issue or epic](Paste URL for follow-up issue or epic here) | ||||
| 
 | ||||
| ### Tasks | ||||
| - [ ] Assign this issue to the appropriate Product Manager, Product Designer, or UX Researcher. | ||||
|  |  | |||
|  | @ -2648,87 +2648,6 @@ Style/FrozenStringLiteralComment: | |||
|     - 'bin/secpick' | ||||
|     - 'danger/changes_size/Dangerfile' | ||||
|     - 'danger/metadata/Dangerfile' | ||||
|     - 'ee/lib/tasks/geo.rake' | ||||
|     - 'ee/lib/tasks/geo/git.rake' | ||||
|     - 'ee/lib/tasks/geo/replication.rake' | ||||
|     - 'ee/lib/tasks/gitlab/db.rake' | ||||
|     - 'ee/lib/tasks/gitlab/elastic.rake' | ||||
|     - 'ee/lib/tasks/gitlab/elastic/test.rake' | ||||
|     - 'ee/lib/tasks/gitlab/geo.rake' | ||||
|     - 'ee/lib/tasks/gitlab/indexer.rake' | ||||
|     - 'ee/lib/tasks/gitlab/ldap.rake' | ||||
|     - 'ee/lib/tasks/gitlab/seed/insights.rake' | ||||
|     - 'ee/lib/tasks/gitlab/seed/metrics.rake' | ||||
|     - 'ee/lib/tasks/migrate/ldap.rake' | ||||
|     - 'lib/tasks/brakeman.rake' | ||||
|     - 'lib/tasks/cache.rake' | ||||
|     - 'lib/tasks/ci/cleanup.rake' | ||||
|     - 'lib/tasks/cleanup.rake' | ||||
|     - 'lib/tasks/config_lint.rake' | ||||
|     - 'lib/tasks/db_obsolete_ignored_columns.rake' | ||||
|     - 'lib/tasks/dev.rake' | ||||
|     - 'lib/tasks/downtime_check.rake' | ||||
|     - 'lib/tasks/eslint.rake' | ||||
|     - 'lib/tasks/file_hooks.rake' | ||||
|     - 'lib/tasks/frontend.rake' | ||||
|     - 'lib/tasks/gemojione.rake' | ||||
|     - 'lib/tasks/gitlab/artifacts/check.rake' | ||||
|     - 'lib/tasks/gitlab/artifacts/migrate.rake' | ||||
|     - 'lib/tasks/gitlab/backup.rake' | ||||
|     - 'lib/tasks/gitlab/bulk_add_permission.rake' | ||||
|     - 'lib/tasks/gitlab/check.rake' | ||||
|     - 'lib/tasks/gitlab/container_registry.rake' | ||||
|     - 'lib/tasks/gitlab/db.rake' | ||||
|     - 'lib/tasks/gitlab/doctor/secrets.rake' | ||||
|     - 'lib/tasks/gitlab/exclusive_lease.rake' | ||||
|     - 'lib/tasks/gitlab/external_diffs.rake' | ||||
|     - 'lib/tasks/gitlab/features.rake' | ||||
|     - 'lib/tasks/gitlab/generate_sample_prometheus_data.rake' | ||||
|     - 'lib/tasks/gitlab/git.rake' | ||||
|     - 'lib/tasks/gitlab/gitaly.rake' | ||||
|     - 'lib/tasks/gitlab/helpers.rake' | ||||
|     - 'lib/tasks/gitlab/import.rake' | ||||
|     - 'lib/tasks/gitlab/import_export.rake' | ||||
|     - 'lib/tasks/gitlab/info.rake' | ||||
|     - 'lib/tasks/gitlab/ldap.rake' | ||||
|     - 'lib/tasks/gitlab/lfs/check.rake' | ||||
|     - 'lib/tasks/gitlab/lfs/migrate.rake' | ||||
|     - 'lib/tasks/gitlab/list_repos.rake' | ||||
|     - 'lib/tasks/gitlab/packages/events.rake' | ||||
|     - 'lib/tasks/gitlab/packages/migrate.rake' | ||||
|     - 'lib/tasks/gitlab/pages.rake' | ||||
|     - 'lib/tasks/gitlab/praefect.rake' | ||||
|     - 'lib/tasks/gitlab/seed.rake' | ||||
|     - 'lib/tasks/gitlab/setup.rake' | ||||
|     - 'lib/tasks/gitlab/shell.rake' | ||||
|     - 'lib/tasks/gitlab/storage.rake' | ||||
|     - 'lib/tasks/gitlab/tcp_check.rake' | ||||
|     - 'lib/tasks/gitlab/test.rake' | ||||
|     - 'lib/tasks/gitlab/two_factor.rake' | ||||
|     - 'lib/tasks/gitlab/update_templates.rake' | ||||
|     - 'lib/tasks/gitlab/uploads/check.rake' | ||||
|     - 'lib/tasks/gitlab/uploads/migrate.rake' | ||||
|     - 'lib/tasks/gitlab/uploads/sanitize.rake' | ||||
|     - 'lib/tasks/gitlab/usage_data.rake' | ||||
|     - 'lib/tasks/gitlab/user_management.rake' | ||||
|     - 'lib/tasks/gitlab/web_hook.rake' | ||||
|     - 'lib/tasks/gitlab/workhorse.rake' | ||||
|     - 'lib/tasks/gitlab/x509/update.rake' | ||||
|     - 'lib/tasks/gitlab_danger.rake' | ||||
|     - 'lib/tasks/grape.rake' | ||||
|     - 'lib/tasks/haml-lint.rake' | ||||
|     - 'lib/tasks/import.rake' | ||||
|     - 'lib/tasks/karma.rake' | ||||
|     - 'lib/tasks/lint.rake' | ||||
|     - 'lib/tasks/migrate/composite_primary_keys.rake' | ||||
|     - 'lib/tasks/migrate/migrate_iids.rake' | ||||
|     - 'lib/tasks/migrate/setup_postgresql.rake' | ||||
|     - 'lib/tasks/pngquant.rake' | ||||
|     - 'lib/tasks/rubocop.rake' | ||||
|     - 'lib/tasks/scss-lint.rake' | ||||
|     - 'lib/tasks/setup.rake' | ||||
|     - 'lib/tasks/test.rake' | ||||
|     - 'lib/tasks/tokens.rake' | ||||
|     - 'qa/Gemfile' | ||||
|     - 'qa/Rakefile' | ||||
|     - 'qa/bin/qa' | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import Vue from 'vue'; | |||
| import StatisticsPanelApp from './components/app.vue'; | ||||
| import createStore from './store'; | ||||
| 
 | ||||
| export default function (el) { | ||||
| export default function initStatisticsPanel(el) { | ||||
|   if (!el) { | ||||
|     return false; | ||||
|   } | ||||
|  |  | |||
|  | @ -117,7 +117,6 @@ const boardsStore = { | |||
|   }, | ||||
| 
 | ||||
|   updateNewListDropdown(listId) { | ||||
|     // eslint-disable-next-line no-unused-expressions
 | ||||
|     document | ||||
|       .querySelector(`.js-board-list-${getIdFromGraphQLId(listId)}`) | ||||
|       ?.classList.remove('is-active'); | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import App from './components/app.vue'; | |||
| 
 | ||||
| Vue.use(VueApollo); | ||||
| 
 | ||||
| export default function () { | ||||
| export default function initIssuableSuggestions() { | ||||
|   const el = document.getElementById('js-suggestions'); | ||||
|   const issueTitle = document.getElementById('issue_title'); | ||||
|   const { projectPath } = el.dataset; | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ if (gon?.disable_animations) { | |||
| // inject test utilities if necessary
 | ||||
| if (process.env.NODE_ENV !== 'production' && gon?.test_env) { | ||||
|   disableJQueryAnimations(); | ||||
|   import(/* webpackMode: "eager" */ './test_utils/'); // eslint-disable-line no-unused-expressions
 | ||||
|   import(/* webpackMode: "eager" */ './test_utils/'); | ||||
| } | ||||
| 
 | ||||
| document.addEventListener('beforeunload', () => { | ||||
|  |  | |||
|  | @ -739,9 +739,13 @@ export const updateConfidentialityOnIssuable = ( | |||
|     }) | ||||
|     .then(({ data }) => { | ||||
|       const { | ||||
|         issueSetConfidential: { issue }, | ||||
|         issueSetConfidential: { issue, errors }, | ||||
|       } = data; | ||||
| 
 | ||||
|       setConfidentiality({ commit }, issue.confidential); | ||||
|       if (errors?.length) { | ||||
|         Flash(errors[0], 'alert'); | ||||
|       } else { | ||||
|         setConfidentiality({ commit }, issue.confidential); | ||||
|       } | ||||
|     }); | ||||
| }; | ||||
|  |  | |||
|  | @ -1,13 +1,20 @@ | |||
| <script> | ||||
| import { GlSprintf, GlLink } from '@gitlab/ui'; | ||||
| import createFlash from '~/flash'; | ||||
| import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue'; | ||||
| import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue'; | ||||
| 
 | ||||
| import { | ||||
|   PACKAGE_SETTINGS_HEADER, | ||||
|   PACKAGE_SETTINGS_DESCRIPTION, | ||||
|   PACKAGES_DOCS_PATH, | ||||
| } from '../constants'; | ||||
| import getGroupPackagesSettingsQuery from '../graphql/queries/get_group_packages_settings.query.graphql'; | ||||
|   ERROR_UPDATING_SETTINGS, | ||||
|   SUCCESS_UPDATING_SETTINGS, | ||||
| } from '~/packages_and_registries/settings/group/constants'; | ||||
| import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update'; | ||||
| import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses'; | ||||
| import getGroupPackagesSettingsQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql'; | ||||
| import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'GroupSettingsApp', | ||||
|  | @ -22,18 +29,9 @@ export default { | |||
|     GlSprintf, | ||||
|     GlLink, | ||||
|     SettingsBlock, | ||||
|     MavenSettings, | ||||
|   }, | ||||
|   inject: { | ||||
|     defaultExpanded: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|       required: true, | ||||
|     }, | ||||
|     groupPath: { | ||||
|       type: String, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   inject: ['defaultExpanded', 'groupPath'], | ||||
|   apollo: { | ||||
|     packageSettings: { | ||||
|       query: getGroupPackagesSettingsQuery, | ||||
|  | @ -50,8 +48,55 @@ export default { | |||
|   data() { | ||||
|     return { | ||||
|       packageSettings: {}, | ||||
|       errors: {}, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     isLoading() { | ||||
|       return this.$apollo.queries.packageSettings.loading; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     updateSettings(payload) { | ||||
|       this.errors = {}; | ||||
|       return this.$apollo | ||||
|         .mutate({ | ||||
|           mutation: updateNamespacePackageSettings, | ||||
|           variables: { | ||||
|             input: { | ||||
|               namespacePath: this.groupPath, | ||||
|               ...payload, | ||||
|             }, | ||||
|           }, | ||||
|           update: updateGroupPackageSettings(this.groupPath), | ||||
|           optimisticResponse: updateGroupPackagesSettingsOptimisticResponse({ | ||||
|             ...this.packageSettings, | ||||
|             ...payload, | ||||
|           }), | ||||
|         }) | ||||
|         .then(({ data }) => { | ||||
|           if (data.updateNamespacePackageSettings?.errors?.length > 0) { | ||||
|             createFlash({ message: ERROR_UPDATING_SETTINGS, type: 'warning' }); | ||||
|           } else { | ||||
|             createFlash({ message: SUCCESS_UPDATING_SETTINGS, type: 'success' }); | ||||
|           } | ||||
|         }) | ||||
|         .catch((e) => { | ||||
|           if (e.graphQLErrors) { | ||||
|             e.graphQLErrors.forEach((error) => { | ||||
|               const [ | ||||
|                 { | ||||
|                   path: [key], | ||||
|                   message, | ||||
|                 }, | ||||
|               ] = error.extensions.problems; | ||||
|               this.errors = { ...this.errors, [key]: message }; | ||||
|             }); | ||||
|           } | ||||
|           createFlash({ message: ERROR_UPDATING_SETTINGS, type: 'warning' }); | ||||
|         }); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
|  | @ -70,6 +115,15 @@ export default { | |||
|           </gl-sprintf> | ||||
|         </span> | ||||
|       </template> | ||||
|       <template #default> | ||||
|         <maven-settings | ||||
|           :maven-duplicates-allowed="packageSettings.mavenDuplicatesAllowed" | ||||
|           :maven-duplicate-exception-regex="packageSettings.mavenDuplicateExceptionRegex" | ||||
|           :maven-duplicate-exception-regex-error="errors.mavenDuplicateExceptionRegex" | ||||
|           :loading="isLoading" | ||||
|           @update="updateSettings" | ||||
|         /> | ||||
|       </template> | ||||
|     </settings-block> | ||||
|   </div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -0,0 +1,113 @@ | |||
| <script> | ||||
| import { GlSprintf, GlToggle, GlFormGroup, GlFormInput } from '@gitlab/ui'; | ||||
| 
 | ||||
| import { | ||||
|   MAVEN_TITLE, | ||||
|   MAVEN_SETTINGS_SUBTITLE, | ||||
|   MAVEN_DUPLICATES_ALLOWED_DISABLED, | ||||
|   MAVEN_DUPLICATES_ALLOWED_ENABLED, | ||||
|   MAVEN_SETTING_EXCEPTION_TITLE, | ||||
|   MAVEN_SETTINGS_EXCEPTION_LEGEND, | ||||
|   MAVEN_DUPLICATES_ALLOWED, | ||||
|   MAVEN_DUPLICATE_EXCEPTION_REGEX, | ||||
| } from '~/packages_and_registries/settings/group/constants'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'MavenSettings', | ||||
|   i18n: { | ||||
|     MAVEN_TITLE, | ||||
|     MAVEN_SETTINGS_SUBTITLE, | ||||
|     MAVEN_SETTING_EXCEPTION_TITLE, | ||||
|     MAVEN_SETTINGS_EXCEPTION_LEGEND, | ||||
|   }, | ||||
|   modelNames: { | ||||
|     MAVEN_DUPLICATES_ALLOWED, | ||||
|     MAVEN_DUPLICATE_EXCEPTION_REGEX, | ||||
|   }, | ||||
|   components: { | ||||
|     GlSprintf, | ||||
|     GlToggle, | ||||
|     GlFormGroup, | ||||
|     GlFormInput, | ||||
|   }, | ||||
|   props: { | ||||
|     loading: { | ||||
|       type: Boolean, | ||||
|       required: false, | ||||
|       default: false, | ||||
|     }, | ||||
|     mavenDuplicatesAllowed: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|       required: true, | ||||
|     }, | ||||
|     mavenDuplicateExceptionRegex: { | ||||
|       type: String, | ||||
|       default: '', | ||||
|       required: true, | ||||
|     }, | ||||
|     mavenDuplicateExceptionRegexError: { | ||||
|       type: String, | ||||
|       default: '', | ||||
|       required: false, | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     enabledButtonLabel() { | ||||
|       return this.mavenDuplicatesAllowed | ||||
|         ? MAVEN_DUPLICATES_ALLOWED_ENABLED | ||||
|         : MAVEN_DUPLICATES_ALLOWED_DISABLED; | ||||
|     }, | ||||
|     isMavenDuplicateExceptionRegexValid() { | ||||
|       return !this.mavenDuplicateExceptionRegexError; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     update(type, value) { | ||||
|       this.$emit('update', { [type]: value }); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <h5 class="gl-border-b-solid gl-border-b-1 gl-border-gray-200"> | ||||
|       {{ $options.i18n.MAVEN_TITLE }} | ||||
|     </h5> | ||||
|     <p>{{ $options.i18n.MAVEN_SETTINGS_SUBTITLE }}</p> | ||||
|     <form> | ||||
|       <div class="gl-display-flex"> | ||||
|         <gl-toggle | ||||
|           :value="mavenDuplicatesAllowed" | ||||
|           @change="update($options.modelNames.MAVEN_DUPLICATES_ALLOWED, $event)" | ||||
|         /> | ||||
|         <div class="gl-ml-5"> | ||||
|           <div data-testid="toggle-label"> | ||||
|             <gl-sprintf :message="enabledButtonLabel"> | ||||
|               <template #bold="{ content }"> | ||||
|                 <strong>{{ content }}</strong> | ||||
|               </template> | ||||
|             </gl-sprintf> | ||||
|           </div> | ||||
|           <gl-form-group | ||||
|             v-if="!mavenDuplicatesAllowed" | ||||
|             class="gl-mt-4" | ||||
|             :label="$options.i18n.MAVEN_SETTING_EXCEPTION_TITLE" | ||||
|             label-size="sm" | ||||
|             :state="isMavenDuplicateExceptionRegexValid" | ||||
|             :invalid-feedback="mavenDuplicateExceptionRegexError" | ||||
|             :description="$options.i18n.MAVEN_SETTINGS_EXCEPTION_LEGEND" | ||||
|             label-for="maven-duplicated-settings-regex-input" | ||||
|           > | ||||
|             <gl-form-input | ||||
|               id="maven-duplicated-settings-regex-input" | ||||
|               :value="mavenDuplicateExceptionRegex" | ||||
|               @change="update($options.modelNames.MAVEN_DUPLICATE_EXCEPTION_REGEX, $event)" | ||||
|             /> | ||||
|           </gl-form-group> | ||||
|         </div> | ||||
|       </div> | ||||
|     </form> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -1,4 +1,4 @@ | |||
| import { s__ } from '~/locale'; | ||||
| import { s__, __ } from '~/locale'; | ||||
| import { helpPagePath } from '~/helpers/help_page_helper'; | ||||
| 
 | ||||
| export const PACKAGE_SETTINGS_HEADER = s__('PackageRegistry|Package Registry'); | ||||
|  | @ -6,4 +6,26 @@ export const PACKAGE_SETTINGS_DESCRIPTION = s__( | |||
|   'PackageRegistry|GitLab Packages allows organizations to utilize GitLab as a private repository for a variety of common package formats. %{linkStart}More Information%{linkEnd}', | ||||
| ); | ||||
| 
 | ||||
| export const MAVEN_TITLE = s__('PackageRegistry|Maven'); | ||||
| export const MAVEN_SETTINGS_SUBTITLE = s__('PackageRegistry|Settings for Maven packages'); | ||||
| export const MAVEN_DUPLICATES_ALLOWED_DISABLED = s__( | ||||
|   'PackageRegistry|%{boldStart}Do not allow duplicates%{boldEnd} - Packages with the same name and version are rejected.', | ||||
| ); | ||||
| export const MAVEN_DUPLICATES_ALLOWED_ENABLED = s__( | ||||
|   'PackageRegistry|%{boldStart}Allow duplicates%{boldEnd} - Packages with the same name and version are accepted.', | ||||
| ); | ||||
| export const MAVEN_SETTING_EXCEPTION_TITLE = __('Exceptions'); | ||||
| export const MAVEN_SETTINGS_EXCEPTION_LEGEND = s__( | ||||
|   'PackageRegistry|Packages can be published if their name or version matches this regex', | ||||
| ); | ||||
| 
 | ||||
| export const SUCCESS_UPDATING_SETTINGS = s__('PackageRegistry|Settings saved successfully'); | ||||
| export const ERROR_UPDATING_SETTINGS = s__( | ||||
|   'PackageRegistry|An error occurred while saving the settings', | ||||
| ); | ||||
| 
 | ||||
| // Parameters
 | ||||
| 
 | ||||
| export const PACKAGES_DOCS_PATH = helpPagePath('user/packages'); | ||||
| export const MAVEN_DUPLICATES_ALLOWED = 'mavenDuplicatesAllowed'; | ||||
| export const MAVEN_DUPLICATE_EXCEPTION_REGEX = 'mavenDuplicateExceptionRegex'; | ||||
|  |  | |||
|  | @ -0,0 +1,22 @@ | |||
| import { produce } from 'immer'; | ||||
| import getGroupPackagesSettingsQuery from '../queries/get_group_packages_settings.query.graphql'; | ||||
| 
 | ||||
| export const updateGroupPackageSettings = (fullPath) => (client, { data: updatedData }) => { | ||||
|   const queryAndParams = { | ||||
|     query: getGroupPackagesSettingsQuery, | ||||
|     variables: { fullPath }, | ||||
|   }; | ||||
|   const sourceData = client.readQuery(queryAndParams); | ||||
| 
 | ||||
|   const data = produce(sourceData, (draftState) => { | ||||
|     // eslint-disable-next-line no-param-reassign
 | ||||
|     draftState.group.packageSettings = { | ||||
|       ...updatedData.updateNamespacePackageSettings.packageSettings, | ||||
|     }; | ||||
|   }); | ||||
| 
 | ||||
|   client.writeQuery({ | ||||
|     ...queryAndParams, | ||||
|     data, | ||||
|   }); | ||||
| }; | ||||
|  | @ -0,0 +1,11 @@ | |||
| export const updateGroupPackagesSettingsOptimisticResponse = (changes) => ({ | ||||
|   // eslint-disable-next-line @gitlab/require-i18n-strings
 | ||||
|   __typename: 'Mutation', | ||||
|   updateNamespacePackageSettings: { | ||||
|     __typename: 'UpdateNamespacePackageSettingsPayload', | ||||
|     errors: [], | ||||
|     packageSettings: { | ||||
|       ...changes, | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|  | @ -1,7 +1,7 @@ | |||
| import ZenMode from '~/zen_mode'; | ||||
| import GLForm from '~/gl_form'; | ||||
| 
 | ||||
| export default function ($formEl) { | ||||
| export default function initProjectForm($formEl) { | ||||
|   new ZenMode(); // eslint-disable-line no-new
 | ||||
|   new GLForm($formEl); // eslint-disable-line no-new
 | ||||
| } | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ import initInviteMemberModal from '~/invite_member/init_invite_member_modal'; | |||
| 
 | ||||
| import { IssuableType } from '~/issuable_show/constants'; | ||||
| 
 | ||||
| export default function () { | ||||
| export default function initShowIssue() { | ||||
|   const initialDataEl = document.getElementById('js-issuable-app'); | ||||
|   const { issueType, ...issuableData } = parseIssuableData(initialDataEl); | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import initInviteMemberTrigger from '~/invite_member/init_invite_member_trigger' | |||
| import initInviteMemberModal from '~/invite_member/init_invite_member_modal'; | ||||
| import StatusBox from '~/merge_request/components/status_box.vue'; | ||||
| 
 | ||||
| export default function () { | ||||
| export default function initMergeRequestShow() { | ||||
|   new ZenMode(); // eslint-disable-line no-new
 | ||||
|   initIssuableSidebar(); | ||||
|   initPipelines(); | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ import { | |||
|   GlFormGroup, | ||||
|   GlFormInput, | ||||
|   GlFormSelect, | ||||
|   GlFormTextarea, | ||||
|   GlLink, | ||||
|   GlDropdown, | ||||
|   GlDropdownItem, | ||||
|  | @ -38,6 +39,9 @@ export default { | |||
|   errorTitle: __('Pipeline cannot be run.'), | ||||
|   warningTitle: __('The form contains the following warning:'), | ||||
|   maxWarningsSummary: __('%{total} warnings found: showing first %{warningsDisplayed}'), | ||||
|   // this height value is used inline on the textarea to match the input field height | ||||
|   // it's used to prevent the overwrite if 'gl-h-7' or 'gl-h-7!' were used | ||||
|   textAreaStyle: { height: '32px' }, | ||||
|   components: { | ||||
|     GlAlert, | ||||
|     GlIcon, | ||||
|  | @ -46,6 +50,7 @@ export default { | |||
|     GlFormGroup, | ||||
|     GlFormInput, | ||||
|     GlFormSelect, | ||||
|     GlFormTextarea, | ||||
|     GlLink, | ||||
|     GlDropdown, | ||||
|     GlDropdownItem, | ||||
|  | @ -426,10 +431,12 @@ export default { | |||
|             data-testid="pipeline-form-ci-variable-key" | ||||
|             @change="addEmptyVariable(refFullName)" | ||||
|           /> | ||||
|           <gl-form-input | ||||
|           <gl-form-textarea | ||||
|             v-model="variable.value" | ||||
|             :placeholder="s__('CiVariables|Input variable value')" | ||||
|             class="gl-mb-3" | ||||
|             :style="$options.textAreaStyle" | ||||
|             :no-resize="false" | ||||
|             data-testid="pipeline-form-ci-variable-value" | ||||
|           /> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,24 +1,13 @@ | |||
| <script> | ||||
| import { GlButton } from '@gitlab/ui'; | ||||
| import { isExperimentEnabled } from '~/lib/utils/experimentation'; | ||||
| import { s__ } from '~/locale'; | ||||
| import Tracking from '~/tracking'; | ||||
| 
 | ||||
| export default { | ||||
|   i18n: { | ||||
|     control: { | ||||
|       infoMessage: s__(`Pipelines|Continuous Integration can help | ||||
|         catch bugs by running your tests automatically, | ||||
|         while Continuous Deployment can help you deliver | ||||
|         code to your product environment.`), | ||||
|       buttonMessage: s__('Pipelines|Get started with Pipelines'), | ||||
|     }, | ||||
|     experiment: { | ||||
|       infoMessage: s__(`Pipelines|GitLab CI/CD can automatically build, | ||||
|     infoMessage: s__(`Pipelines|GitLab CI/CD can automatically build, | ||||
|           test, and deploy your code. Let GitLab take care of time | ||||
|           consuming tasks, so you can spend more time creating.`), | ||||
|       buttonMessage: s__('Pipelines|Get started with CI/CD'), | ||||
|     }, | ||||
|     buttonMessage: s__('Pipelines|Get started with CI/CD'), | ||||
|   }, | ||||
|   name: 'PipelinesEmptyState', | ||||
|   components: { | ||||
|  | @ -38,23 +27,6 @@ export default { | |||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.track('viewed'); | ||||
|   }, | ||||
|   methods: { | ||||
|     track(action) { | ||||
|       if (!gon.tracking_data) { | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       const { category, value, label, property } = gon.tracking_data; | ||||
| 
 | ||||
|       Tracking.event(category, action, { value, label, property }); | ||||
|     }, | ||||
|     isExperimentEnabled() { | ||||
|       return isExperimentEnabled('pipelinesEmptyState'); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|  | @ -70,11 +42,7 @@ export default { | |||
|             {{ s__('Pipelines|Build with confidence') }} | ||||
|           </h4> | ||||
|           <p data-testid="info-text"> | ||||
|             {{ | ||||
|               isExperimentEnabled() | ||||
|                 ? $options.i18n.experiment.infoMessage | ||||
|                 : $options.i18n.control.infoMessage | ||||
|             }} | ||||
|             {{ $options.i18n.infoMessage }} | ||||
|           </p> | ||||
| 
 | ||||
|           <div class="gl-text-center"> | ||||
|  | @ -83,13 +51,8 @@ export default { | |||
|               variant="info" | ||||
|               category="primary" | ||||
|               data-testid="get-started-pipelines" | ||||
|               @click="track('documentation_clicked')" | ||||
|             > | ||||
|               {{ | ||||
|                 isExperimentEnabled() | ||||
|                   ? $options.i18n.experiment.buttonMessage | ||||
|                   : $options.i18n.control.buttonMessage | ||||
|               }} | ||||
|               {{ $options.i18n.buttonMessage }} | ||||
|             </gl-button> | ||||
|           </div> | ||||
|         </template> | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ const createTestDetails = () => { | |||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export default async function () { | ||||
| export default async function initPipelineDetailsBundle() { | ||||
|   createTestDetails(); | ||||
|   createDagApp(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import Vue from 'vue'; | ||||
| import NewProjectCreationApp from './components/app.vue'; | ||||
| 
 | ||||
| export default function (el, props) { | ||||
| export default function initNewProjectCreation(el, props) { | ||||
|   return new Vue({ | ||||
|     el, | ||||
|     components: { | ||||
|  |  | |||
|  | @ -60,8 +60,8 @@ export default { | |||
|       const nextItemEl = itemEl.nextElementSibling; | ||||
| 
 | ||||
|       return { | ||||
|         beforeId: prevItemEl && parseInt(prevItemEl.dataset.orderingId, 0), | ||||
|         afterId: nextItemEl && parseInt(nextItemEl.dataset.orderingId, 0), | ||||
|         beforeId: prevItemEl && parseInt(prevItemEl.dataset.orderingId, 10), | ||||
|         afterId: nextItemEl && parseInt(nextItemEl.dataset.orderingId, 10), | ||||
|       }; | ||||
|     }, | ||||
|     reordered(event) { | ||||
|  |  | |||
|  | @ -86,7 +86,7 @@ const createValidator = (context, feedbackMap) => ({ el, reportInvalidInput = fa | |||
|  * @param {Object<string, { message: string, isValid: ?function}>} customFeedbackMap | ||||
|  * @returns {{ inserted: function, update: function }} validateDirective | ||||
|  */ | ||||
| export default function (customFeedbackMap = {}) { | ||||
| export default function initValidation(customFeedbackMap = {}) { | ||||
|   const feedbackMap = merge(defaultFeedbackMap, customFeedbackMap); | ||||
|   const elDataMap = new WeakMap(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -156,14 +156,6 @@ | |||
|     color: inherit; | ||||
|   } | ||||
| 
 | ||||
|   // TODO remove this class once we can generate a correct hover utility from `gitlab/ui`, | ||||
|   // see here: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39286#note_396767000 | ||||
|   .btn-link-hover:hover { | ||||
|     * { | ||||
|       @include gl-text-blue-800; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .issuable-header-text { | ||||
|     margin-top: 7px; | ||||
|   } | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap | |||
|   before_action :build_merge_request, except: [:create] | ||||
| 
 | ||||
|   before_action do | ||||
|     push_frontend_feature_flag(:merge_request_reviewers, @project, default_enabled: true) | ||||
|     push_frontend_feature_flag(:mr_collapsed_approval_rules, @project) | ||||
|     push_frontend_feature_flag(:reviewer_approval_rules, @project, default_enabled: :yaml) | ||||
|   end | ||||
|  |  | |||
|  | @ -52,7 +52,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo | |||
|   end | ||||
| 
 | ||||
|   before_action do | ||||
|     push_frontend_feature_flag(:merge_request_reviewers, @project, default_enabled: true) | ||||
|     push_frontend_feature_flag(:mr_collapsed_approval_rules, @project) | ||||
|     push_frontend_feature_flag(:reviewer_approval_rules, @project, default_enabled: :yaml) | ||||
|   end | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ class Projects::PipelinesController < Projects::ApplicationController | |||
|     push_frontend_feature_flag(:jira_for_vulnerabilities, project, type: :development, default_enabled: :yaml) | ||||
|   end | ||||
|   before_action :ensure_pipeline, only: [:show] | ||||
|   before_action :push_experiment_to_gon, only: :index, if: :html_request? | ||||
| 
 | ||||
|   # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 | ||||
|   before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? } | ||||
|  | @ -45,11 +44,7 @@ class Projects::PipelinesController < Projects::ApplicationController | |||
|     @pipelines_count = limited_pipelines_count(project) | ||||
| 
 | ||||
|     respond_to do |format| | ||||
|       format.html do | ||||
|         record_empty_pipeline_experiment | ||||
| 
 | ||||
|         render :index | ||||
|       end | ||||
|       format.html | ||||
|       format.json do | ||||
|         Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL) | ||||
| 
 | ||||
|  | @ -300,20 +295,6 @@ class Projects::PipelinesController < Projects::ApplicationController | |||
|   def index_params | ||||
|     params.permit(:scope, :username, :ref, :status) | ||||
|   end | ||||
| 
 | ||||
|   def record_empty_pipeline_experiment | ||||
|     return unless @pipelines_count.to_i == 0 | ||||
|     return if helpers.has_gitlab_ci?(@project) | ||||
| 
 | ||||
|     record_experiment_user(:pipelines_empty_state) | ||||
|   end | ||||
| 
 | ||||
|   def push_experiment_to_gon | ||||
|     return unless current_user | ||||
| 
 | ||||
|     push_frontend_experiment(:pipelines_empty_state, subject: current_user) | ||||
|     frontend_experimentation_tracking_data(:pipelines_empty_state, 'view', project.namespace_id, subject: current_user) | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Projects::PipelinesController.prepend_if_ee('EE::Projects::PipelinesController') | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ module Resolvers | |||
|     def only_count_is_selected_with_merged_at_filter?(args) | ||||
|       return unless lookahead | ||||
| 
 | ||||
|       argument_names = args.except(:lookahead, :sort, :merged_before, :merged_after).keys | ||||
|       argument_names = args.compact.except(:lookahead, :sort, :merged_before, :merged_after).keys | ||||
| 
 | ||||
|       # no extra filtering arguments are provided | ||||
|       return unless argument_names.empty? | ||||
|  | @ -34,7 +34,7 @@ module Resolvers | |||
|       #   totalTimeToMerge | ||||
|       # } | ||||
|       allowed_selected_fields = [:count, :total_time_to_merge] | ||||
|       selected_fields = lookahead.selections.map(&:field).map(&:original_name) | ||||
|       selected_fields = lookahead.selections.map(&:field).map(&:original_name) - [:__typename] # ignore __typename meta field | ||||
| 
 | ||||
|       # only the allowed_selected_fields are present | ||||
|       (selected_fields - allowed_selected_fields).empty? | ||||
|  |  | |||
|  | @ -243,7 +243,7 @@ module Types | |||
|     end | ||||
| 
 | ||||
|     def reviewers | ||||
|       object.reviewers if object.allows_reviewers? | ||||
|       object.reviewers | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -174,15 +174,9 @@ module MergeRequestsHelper | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def merge_request_reviewers_enabled? | ||||
|     Feature.enabled?(:merge_request_reviewers, default_enabled: :yaml) | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def review_requested_merge_requests_count | ||||
|     return 0 unless merge_request_reviewers_enabled? | ||||
| 
 | ||||
|     current_user.review_requested_open_merge_requests_count | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -996,7 +996,7 @@ module Ci | |||
|     end | ||||
| 
 | ||||
|     def has_coverage_reports? | ||||
|       pipeline_artifacts&.has_report?(:code_coverage) | ||||
|       pipeline_artifacts&.report_exists?(:code_coverage) | ||||
|     end | ||||
| 
 | ||||
|     def can_generate_coverage_reports? | ||||
|  | @ -1004,7 +1004,7 @@ module Ci | |||
|     end | ||||
| 
 | ||||
|     def has_codequality_mr_diff_report? | ||||
|       pipeline_artifacts&.has_report?(:code_quality_mr_diff) | ||||
|       pipeline_artifacts&.report_exists?(:code_quality_mr_diff) | ||||
|     end | ||||
| 
 | ||||
|     def can_generate_codequality_reports? | ||||
|  |  | |||
|  | @ -18,6 +18,11 @@ module Ci | |||
|       code_quality_mr_diff: 'code_quality_mr_diff.json' | ||||
|     }.freeze | ||||
| 
 | ||||
|     REPORT_TYPES = { | ||||
|       code_coverage: :raw, | ||||
|       code_quality_mr_diff: :raw | ||||
|     }.freeze | ||||
| 
 | ||||
|     belongs_to :project, class_name: "Project", inverse_of: :pipeline_artifacts | ||||
|     belongs_to :pipeline, class_name: "Ci::Pipeline", inverse_of: :pipeline_artifacts | ||||
| 
 | ||||
|  | @ -36,7 +41,9 @@ module Ci | |||
|     } | ||||
| 
 | ||||
|     class << self | ||||
|       def has_report?(file_type) | ||||
|       def report_exists?(file_type) | ||||
|         return false unless REPORT_TYPES.key?(file_type) | ||||
| 
 | ||||
|         where(file_type: file_type).exists? | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -1784,7 +1784,7 @@ class MergeRequest < ApplicationRecord | |||
|   end | ||||
| 
 | ||||
|   def allows_reviewers? | ||||
|     Feature.enabled?(:merge_request_reviewers, project, default_enabled: true) | ||||
|     true | ||||
|   end | ||||
| 
 | ||||
|   def allows_multiple_reviewers? | ||||
|  |  | |||
|  | @ -30,7 +30,8 @@ class JiraService < IssueTrackerService | |||
| 
 | ||||
|   # TODO: we can probably just delegate as part of | ||||
|   # https://gitlab.com/gitlab-org/gitlab/issues/29404 | ||||
|   data_field :username, :password, :url, :api_url, :jira_issue_transition_id, :project_key, :issues_enabled, :vulnerabilities_enabled, :vulnerabilities_issuetype | ||||
|   data_field :username, :password, :url, :api_url, :jira_issue_transition_id, :project_key, :issues_enabled, | ||||
|     :vulnerabilities_enabled, :vulnerabilities_issuetype, :proxy_address, :proxy_port, :proxy_username, :proxy_password | ||||
| 
 | ||||
|   before_update :reset_password | ||||
|   after_commit :update_deployment_type, on: [:create, :update], if: :update_deployment_type? | ||||
|  |  | |||
|  | @ -7,6 +7,15 @@ class JiraTrackerData < ApplicationRecord | |||
|   attr_encrypted :api_url, encryption_options | ||||
|   attr_encrypted :username, encryption_options | ||||
|   attr_encrypted :password, encryption_options | ||||
|   attr_encrypted :proxy_address, encryption_options | ||||
|   attr_encrypted :proxy_port, encryption_options | ||||
|   attr_encrypted :proxy_username, encryption_options | ||||
|   attr_encrypted :proxy_password, encryption_options | ||||
| 
 | ||||
|   validates :proxy_address, length: { maximum: 2048 } | ||||
|   validates :proxy_port, length: { maximum: 5 } | ||||
|   validates :proxy_username, length: { maximum: 255 } | ||||
|   validates :proxy_password, length: { maximum: 255 } | ||||
| 
 | ||||
|   enum deployment_type: { unknown: 0, server: 1, cloud: 2 }, _prefix: :deployment | ||||
| end | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ class MergeRequestBasicEntity < Grape::Entity | |||
|   expose :milestone, using: API::Entities::Milestone | ||||
|   expose :labels, using: LabelEntity | ||||
|   expose :assignees, using: API::Entities::UserBasic | ||||
|   expose :reviewers, if: -> (m) { m.allows_reviewers? }, using: API::Entities::UserBasic | ||||
|   expose :reviewers, using: API::Entities::UserBasic | ||||
|   expose :task_status, :task_status_short | ||||
|   expose :lock_version, :lock_version | ||||
| end | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ class MergeRequestSidebarExtrasEntity < IssuableSidebarExtrasEntity | |||
|     MergeRequestUserEntity.represent(merge_request.assignees, options.merge(merge_request: merge_request)) | ||||
|   end | ||||
| 
 | ||||
|   expose :reviewers, if: -> (m) { m.allows_reviewers? } do |merge_request, options| | ||||
|   expose :reviewers do |merge_request, options| | ||||
|     MergeRequestUserEntity.represent(merge_request.reviewers, options.merge(merge_request: merge_request)) | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -123,7 +123,6 @@ module Ci | |||
| 
 | ||||
|     def record_conversion_event | ||||
|       Experiments::RecordConversionEventWorker.perform_async(:ci_syntax_templates, current_user.id) | ||||
|       Experiments::RecordConversionEventWorker.perform_async(:pipelines_empty_state, current_user.id) | ||||
|     end | ||||
| 
 | ||||
|     def create_namespace_onboarding_action | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ module Ci | |||
|         pipeline.pipeline_artifacts.create!( | ||||
|           project_id: pipeline.project_id, | ||||
|           file_type: :code_coverage, | ||||
|           file_format: :raw, | ||||
|           file_format: Ci::PipelineArtifact::REPORT_TYPES.fetch(:code_coverage), | ||||
|           size: file["tempfile"].size, | ||||
|           file: file, | ||||
|           expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ module Ci | |||
|         pipeline.pipeline_artifacts.create!( | ||||
|           project_id: pipeline.project_id, | ||||
|           file_type: :code_quality_mr_diff, | ||||
|           file_format: :raw, | ||||
|           file_format: Ci::PipelineArtifact::REPORT_TYPES.fetch(:code_quality_mr_diff), | ||||
|           size: file["tempfile"].size, | ||||
|           file: file, | ||||
|           expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ module DesignManagement | |||
|       return error("Only #{MAX_FILES} files are allowed simultaneously") if files.size > MAX_FILES | ||||
|       return error("Duplicate filenames are not allowed!") if files.map(&:original_filename).uniq.length != files.length | ||||
|       return error("Design copy is in progress") if design_collection.copy_in_progress? | ||||
|       return error("Filenames contained invalid characters and could not be saved") if files.any?(&:filename_sanitized?) | ||||
| 
 | ||||
|       uploaded_designs, version = upload_designs! | ||||
|       skipped_designs = designs - uploaded_designs | ||||
|  |  | |||
|  | @ -108,7 +108,7 @@ module MergeRequests | |||
|     def filter_reviewer(merge_request) | ||||
|       return if params[:reviewer_ids].blank? | ||||
| 
 | ||||
|       unless can_admin_issuable?(merge_request) && merge_request.allows_reviewers? | ||||
|       unless can_admin_issuable?(merge_request) | ||||
|         params.delete(:reviewer_ids) | ||||
| 
 | ||||
|         return | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
|       = f.number_field :session_expire_delay, class: 'form-control gl-form-input', title: _('Maximum duration of a session.'), data: { toggle: 'tooltip', container: 'body' } | ||||
|       %span.form-text.text-muted#session_expire_delay_help_block= _('GitLab restart is required to apply changes.') | ||||
| 
 | ||||
|     = render_if_exists 'admin/application_settings/git_two_factor_session_expiry', form: f | ||||
|     = render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f | ||||
|     = render_if_exists 'admin/application_settings/enforce_pat_expiration', form: f | ||||
|     = render_if_exists 'admin/application_settings/enforce_ssh_key_expiration', form: f | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| - breadcrumb_title _('Repository Settings') | ||||
| - page_title _('Repository') | ||||
| 
 | ||||
| - deploy_token_description = s_('DeployTokens|Group Deploy Tokens allow access to the packages, repositories, and registry images within the group.') | ||||
| - deploy_token_description = s_('DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group.') | ||||
| 
 | ||||
| = render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description | ||||
| = render "initial_branch_name", group: @group | ||||
|  |  | |||
|  | @ -47,11 +47,10 @@ | |||
|                 %span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) } | ||||
|                   = number_with_delimiter(issues_count) | ||||
|           - if header_link?(:merge_requests) | ||||
|             - reviewers_enabled = merge_request_reviewers_enabled? | ||||
|             = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter #{reviewers_enabled ? 'dropdown' : ''}" }) do | ||||
|             = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do | ||||
|               = link_to assigned_mrs_dashboard_path, class: 'dashboard-shortcuts-merge_requests', title: _('Merge requests'), aria: { label: _('Merge requests') }, | ||||
|                 data: { qa_selector: 'merge_requests_shortcut_button', | ||||
|                 toggle: reviewers_enabled ? "dropdown" : "tooltip", | ||||
|                 toggle: "dropdown", | ||||
|                 placement: 'bottom', | ||||
|                 track_label: 'main_navigation', | ||||
|                 track_event: 'click_merge_link', | ||||
|  | @ -60,23 +59,21 @@ | |||
|                 = sprite_icon('git-merge') | ||||
|                 %span.badge.badge-pill.merge-requests-count.js-merge-requests-count{ class: ('hidden' if user_merge_requests_counts[:total] == 0) } | ||||
|                   = number_with_delimiter(user_merge_requests_counts[:total]) | ||||
|                 - if reviewers_enabled | ||||
|                   = sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!') | ||||
|               - if reviewers_enabled | ||||
|                 .dropdown-menu.dropdown-menu-right | ||||
|                   %ul | ||||
|                     %li.dropdown-header | ||||
|                       = _('Merge requests') | ||||
|                     %li | ||||
|                       = link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do | ||||
|                         = _('Assigned to you') | ||||
|                         %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" } | ||||
|                           = user_merge_requests_counts[:assigned] | ||||
|                     %li | ||||
|                       = link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do | ||||
|                         = _('Review requests for you') | ||||
|                         %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" } | ||||
|                           = user_merge_requests_counts[:review_requested] | ||||
|                 = sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!') | ||||
|               .dropdown-menu.dropdown-menu-right | ||||
|                 %ul | ||||
|                   %li.dropdown-header | ||||
|                     = _('Merge requests') | ||||
|                   %li | ||||
|                     = link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do | ||||
|                       = _('Assigned to you') | ||||
|                       %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" } | ||||
|                         = user_merge_requests_counts[:assigned] | ||||
|                   %li | ||||
|                     = link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do | ||||
|                       = _('Review requests for you') | ||||
|                       %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" } | ||||
|                         = user_merge_requests_counts[:review_requested] | ||||
|           - if header_link?(:todos) | ||||
|             = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do | ||||
|               = link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos', | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ | |||
|         = s_('AccessTokens|It cannot be used to access any other data.') | ||||
|     .col-lg-8.feed-token-reset | ||||
|       = label_tag :feed_token, s_('AccessTokens|Feed token'), class: 'label-bold' | ||||
|       = text_field_tag :feed_token, current_user.feed_token, class: 'form-control js-select-on-focus', readonly: true | ||||
|       = text_field_tag :feed_token, current_user.feed_token, class: 'form-control gl-form-input js-select-on-focus', readonly: true | ||||
|       %p.form-text.text-muted | ||||
|         - reset_link = link_to s_('AccessTokens|reset it'), [:reset, :feed_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any RSS or calendar URLs currently in use will stop working.') } | ||||
|         - reset_message = s_('AccessTokens|Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. You should %{link_reset_it} if that ever happens.') % { link_reset_it: reset_link } | ||||
|  | @ -64,7 +64,7 @@ | |||
|         = s_('AccessTokens|It cannot be used to access any other data.') | ||||
|     .col-lg-8.incoming-email-token-reset | ||||
|       = label_tag :incoming_email_token, s_('AccessTokens|Incoming email token'), class: 'label-bold' | ||||
|       = text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control js-select-on-focus', readonly: true | ||||
|       = text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control gl-form-input js-select-on-focus', readonly: true | ||||
|       %p.form-text.text-muted | ||||
|         - reset_link = link_to s_('AccessTokens|reset it'), [:reset, :incoming_email_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any issue email addresses currently in use will stop working.') } | ||||
|         - reset_message = s_('AccessTokens|Keep this token secret. Anyone who gets ahold of it can create issues as if they were you. You should %{link_reset_it} if that ever happens.') % { link_reset_it: reset_link } | ||||
|  | @ -83,7 +83,7 @@ | |||
|         = s_('AccessTokens|It cannot be used to access any other data.') | ||||
|     .col-lg-8 | ||||
|       = label_tag :static_object_token, s_('AccessTokens|Static object token'), class: "label-bold" | ||||
|       = text_field_tag :static_object_token, current_user.static_object_token, class: 'form-control', readonly: true, onclick: 'this.select()' | ||||
|       = text_field_tag :static_object_token, current_user.static_object_token, class: 'form-control gl-form-input', readonly: true, onclick: 'this.select()' | ||||
|       %p.form-text.text-muted | ||||
|         - reset_link = url_for [:reset, :static_object_token, :profile] | ||||
|         - reset_link_start = '<a data-confirm="%{confirm}" rel="nofollow" data-method="put" href="%{url}">'.html_safe % { confirm: s_('AccessTokens|Are you sure?'), url: reset_link } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| %section.settings.no-animate#default-branch-settings{ class: ('expanded' if expanded) } | ||||
|   .settings-header | ||||
|     %h4= _('Default Branch') | ||||
|     %h4= _('Default branch') | ||||
|     %button.btn.js-settings-toggle | ||||
|       = expanded ? _('Collapse') : _('Expand') | ||||
|     %p | ||||
|  | @ -16,7 +16,7 @@ | |||
|             = _('A default branch cannot be chosen for an empty project.') | ||||
|         - else | ||||
|           .form-group | ||||
|             = f.label :default_branch, "Default Branch", class: 'label-bold' | ||||
|             = f.label :default_branch, "Default branch", class: 'label-bold' | ||||
|             = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'}) | ||||
| 
 | ||||
|         .form-group | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ | |||
|         - if merge_request.assignees.any? | ||||
|           %li.gl-display-flex.gl-align-items-center | ||||
|             = render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request | ||||
|         - if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && merge_request.reviewers.any? | ||||
|         - if merge_request.reviewers.any? | ||||
|           %li.gl-display-flex.issuable-reviewers | ||||
|             = render 'shared/issuable/reviewers', project: merge_request.project, issuable: merge_request | ||||
|         = render 'projects/merge_requests/approvals_count', merge_request: merge_request | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| %section.settings.no-animate#js-protected-branches-settings{ class: ('expanded' if expanded), data: { qa_selector: 'protected_branches_settings_content' } } | ||||
|   .settings-header | ||||
|     %h4 | ||||
|       Protected Branches | ||||
|       Protected branches | ||||
|     %button.btn.js-settings-toggle.qa-expand-protected-branches{ type: 'button' } | ||||
|       = expanded ? 'Collapse' : 'Expand' | ||||
|     %p | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| %section.settings.no-animate#js-protected-tags-settings{ class: ('expanded' if expanded), data: { qa_selector: 'protected_tag_settings_content' } } | ||||
|   .settings-header | ||||
|     %h4 | ||||
|       Protected Tags | ||||
|       Protected tags | ||||
|     %button.btn.js-settings-toggle{ type: 'button' } | ||||
|       = expanded ? 'Collapse' : 'Expand' | ||||
|     %p | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| - breadcrumb_title _("Repository Settings") | ||||
| - page_title _("Repository") | ||||
| - @content_class = "limit-container-width" unless fluid_layout | ||||
| - deploy_token_description = s_('DeployTokens|Deploy Tokens allow access to packages, your repository, and registry images.') | ||||
| - deploy_token_description = s_('DeployTokens|Deploy tokens allow access to packages, your repository, and registry images.') | ||||
| 
 | ||||
| = render "projects/default_branch/show" | ||||
| = render_if_exists "projects/push_rules/index" | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| - expanded = expanded_by_default? | ||||
| %section.qa-deploy-keys-settings.settings.no-animate#js-deploy-keys-settings{ class: ('expanded' if expanded), data: { qa_selector: 'deploy_keys_settings_content' } } | ||||
|   .settings-header | ||||
|     %h4= _('Deploy Keys') | ||||
|     %h4= _('Deploy keys') | ||||
|     %button.btn.js-settings-toggle{ type: 'button' } | ||||
|       = expanded ? 'Collapse' : 'Expand' | ||||
|     %p | ||||
|       - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/deploy_keys/index') } | ||||
|       = _("Add deploy keys to grant read/write access to this repository. %{link_start}What are Deploy Keys?%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe } | ||||
|       = _("Add deploy keys to grant read/write access to this repository. %{link_start}What are deploy keys?%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe } | ||||
|   .settings-content | ||||
|     %h5.gl-mt-0 | ||||
|     = render @deploy_keys.form_partial_path | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| %section.qa-deploy-tokens-settings.settings.no-animate#js-deploy-tokens{ class: ('expanded' if expanded), data: { qa_selector: 'deploy_tokens_settings_content' } } | ||||
|   .settings-header | ||||
|     %h4= s_('DeployTokens|Deploy Tokens') | ||||
|     %h4= s_('DeployTokens|Deploy tokens') | ||||
|     %button.gl-button.btn.js-settings-toggle.qa-expand-deploy-keys{ type: 'button' } | ||||
|       = expanded ? 'Collapse' : 'Expand' | ||||
|     %p | ||||
|  | @ -11,7 +11,7 @@ | |||
|     - if @new_deploy_token.persisted? | ||||
|       = render 'shared/deploy_tokens/new_deploy_token', deploy_token: @new_deploy_token | ||||
|     %h5.gl-mt-0 | ||||
|       = s_('DeployTokens|Add a Deploy Token') | ||||
|       = s_('DeployTokens|Add a deploy token') | ||||
|     = render 'shared/deploy_tokens/form', group_or_project: group_or_project, token: @new_deploy_token, presenter: @deploy_tokens | ||||
|     %hr | ||||
|     = render 'shared/deploy_tokens/table', group_or_project: group_or_project, active_tokens: @deploy_tokens | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ | |||
|       .block.assignee.qa-assignee-block | ||||
|         = render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in | ||||
| 
 | ||||
|       - if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && reviewers | ||||
|       - if reviewers | ||||
|         .block.reviewer.qa-reviewer-block | ||||
|           = render "shared/issuable/sidebar_reviewers", issuable_sidebar: issuable_sidebar, reviewers: reviewers, signed_in: signed_in | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Add Jira proxy settings columns | ||||
| merge_request: 52119 | ||||
| author: | ||||
| type: added | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Apply new GitLab UI for input field in user personal access token settings | ||||
| merge_request: 52426 | ||||
| author: Yogi (@yo) | ||||
| type: other | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: 'Designs: return error if uploading filenames with special chars' | ||||
| merge_request: 44136 | ||||
| author: Sushil Khanchi @khanchi97 | ||||
| type: fixed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Change pipeline empty state language | ||||
| merge_request: 53281 | ||||
| author: | ||||
| type: changed | ||||
|  | @ -0,0 +1,6 @@ | |||
| --- | ||||
| title: Support multi-line string variable values when running a manual pipeline in | ||||
|   the UI. | ||||
| merge_request: 53292 | ||||
| author: | ||||
| type: fixed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Remove merge_request_reviewers feature flag | ||||
| merge_request: 52468 | ||||
| author: | ||||
| type: removed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Updated UI text to be sentence case | ||||
| merge_request: 53323 | ||||
| author: | ||||
| type: other | ||||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: merge_request_reviewers | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40488 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/245190 | ||||
| milestone: '13.4' | ||||
| type: development | ||||
| group: group::code review | ||||
| default_enabled: true | ||||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: pipelines_empty_state_experiment_percentage | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47952 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/289 | ||||
| milestone: '13.8' | ||||
| type: experiment | ||||
| group: group::activation | ||||
| default_enabled: false | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddProxySettingsToJiraTrackerData < ActiveRecord::Migration[6.0] | ||||
|   DOWNTIME = false | ||||
| 
 | ||||
|   def change | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_address, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_address_iv, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_port, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_port_iv, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_username, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_username_iv, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_password, :text | ||||
|     add_column :jira_tracker_data, :encrypted_proxy_password_iv, :text | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| c8b5485f158fdec0ab6813e4014713786dfa231b901e77ea610a873d03f8f0f0 | ||||
|  | @ -13602,6 +13602,14 @@ CREATE TABLE jira_tracker_data ( | |||
|     deployment_type smallint DEFAULT 0 NOT NULL, | ||||
|     vulnerabilities_issuetype text, | ||||
|     vulnerabilities_enabled boolean DEFAULT false NOT NULL, | ||||
|     encrypted_proxy_address text, | ||||
|     encrypted_proxy_address_iv text, | ||||
|     encrypted_proxy_port text, | ||||
|     encrypted_proxy_port_iv text, | ||||
|     encrypted_proxy_username text, | ||||
|     encrypted_proxy_username_iv text, | ||||
|     encrypted_proxy_password text, | ||||
|     encrypted_proxy_password_iv text, | ||||
|     CONSTRAINT check_0bf84b76e9 CHECK ((char_length(vulnerabilities_issuetype) <= 255)), | ||||
|     CONSTRAINT check_214cf6a48b CHECK ((char_length(project_key) <= 255)) | ||||
| ); | ||||
|  |  | |||
|  | @ -454,10 +454,10 @@ Parameters: | |||
| | `author_id`                     | integer        | no       | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 9.5)_. | | ||||
| | `author_username`               | string         | no       | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 12.10)_. | | ||||
| | `assignee_id`                   | integer        | no       | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 9.5)_. | | ||||
| | `approver_ids` **(PREMIUM)**    | integer array  | no       | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | | ||||
| | `approver_ids` **(PREMIUM))**    | integer array  | no       | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | | ||||
| | `approved_by_ids` **(PREMIUM)** | integer array  | no       | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. | | ||||
| | `reviewer_id`                   | integer        | no       | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#enable-or-disable-merge-request-reviewers) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.  | | ||||
| | `reviewer_username`             | string         | no       | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#enable-or-disable-merge-request-reviewers) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | | ||||
| | `reviewer_id`                   | integer        | no       | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.  | | ||||
| | `reviewer_username`             | string         | no       | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | | ||||
| | `my_reaction_emoji`             | string         | no       | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14016) in GitLab 10.0)_. | | ||||
| | `source_branch`                 | string         | no       | Return merge requests with the given source branch.                                                                             | | ||||
| | `target_branch`                 | string         | no       | Return merge requests with the given target branch.                                                                             | | ||||
|  |  | |||
|  | @ -282,6 +282,7 @@ listed in the descriptions of the relevant settings. | |||
| | `first_day_of_week`                      | integer          | no                                   | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday, `1` for Monday, and `6` for Saturday. | | ||||
| | `geo_node_allowed_ips`                   | string           | yes                                  | **(PREMIUM)** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. | | ||||
| | `geo_status_timeout`                     | integer          | no                                   | **(PREMIUM)** The amount of seconds after which a request to get a secondary node status times out. | | ||||
| | `git_two_factor_session_expiry`          | integer          | no                                   | **(PREMIUM)** Maximum duration (in minutes) of a session for Git operations when 2FA is enabled. | | ||||
| | `gitaly_timeout_default`                 | integer          | no                                   | Default Gitaly timeout, in seconds. This timeout is not enforced for Git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. | | ||||
| | `gitaly_timeout_fast`                    | integer          | no                                   | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. | | ||||
| | `gitaly_timeout_medium`                  | integer          | no                                   | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. | | ||||
|  |  | |||
|  | @ -0,0 +1,219 @@ | |||
| --- | ||||
| stage: none | ||||
| group: unassigned | ||||
| info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments | ||||
| --- | ||||
| 
 | ||||
| # Design Anti-patterns | ||||
| 
 | ||||
| Anti-patterns may seem like good approaches at first, but it has been shown that they bring more ills than benefits. These should | ||||
| generally be avoided. | ||||
| 
 | ||||
| Throughout the GitLab codebase, there may be historic uses of these anti-patterns. Please [use descretion](https://about.gitlab.com/handbook/engineering/#balance-refactoring-and-velocity) | ||||
| when figuring out whether or not to refactor, when touching code that uses one of these legacy patterns. | ||||
| 
 | ||||
| **Please note:** For new features, anti-patterns are not necessarily prohibited, but it is **strongly suggested** to find another approach. | ||||
| 
 | ||||
| ## Shared Global Object (Anti-pattern) | ||||
| 
 | ||||
| A shared global object is an instance of something that can be accessed from anywhere and therefore has no clear owner. | ||||
| 
 | ||||
| Here's an example of this pattern applied to a Vuex Store: | ||||
| 
 | ||||
| ```javascript | ||||
| const createStore = () => new Vuex.Store({ | ||||
|   actions, | ||||
|   state, | ||||
|   mutations | ||||
| }); | ||||
| 
 | ||||
| // Notice that we are forcing all references to this module to use the same single instance of the store. | ||||
| // We are also creating the store at import-time and there is nothing which can automatically dispose of it. | ||||
| // | ||||
| // As an alternative, we should simply export the `createStore` and let the client manage the | ||||
| // lifecycle and instance of the store. | ||||
| export default createStore(); | ||||
| ``` | ||||
| 
 | ||||
| ### What problems do Shared Global Objects cause? | ||||
| 
 | ||||
| Shared Global Objects are convenient because they can be accessed from anywhere. However, | ||||
| the convenience does not always outweigh their heavy cost: | ||||
| 
 | ||||
| - **No ownership.** There is no clear owner to these objects and therefore they assume a non-deterministic | ||||
|   and permanent lifecycle. This can be especially problematic for tests. | ||||
| - **No access control.** When Shared Global Objects manage some state, this can create some very buggy and difficult | ||||
|   coupling situations because there is no access control to this object. | ||||
| - **Possible circular references.** Shared Global Objects can also create some circular referencing situations since submodules | ||||
|   of the Shared Global Object can reference modules that reference itself (see  | ||||
|   [this MR for an example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33366)). | ||||
| 
 | ||||
| Here are some historic examples where this pattern was identified to be problematic: | ||||
| 
 | ||||
| - [Reference to global Vuex store in IDE](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36401) | ||||
| - [Docs update to discourage singleton Vuex store](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36952) | ||||
| 
 | ||||
| ### When could the Shared Global Object pattern be actually appropriate? | ||||
| 
 | ||||
| Shared Global Object's solve the problem of making something globally accessible. This pattern | ||||
| could be appropriate: | ||||
| 
 | ||||
| - When a responsibility is truly global and should be referenced across the application | ||||
|   (e.g., an application-wide Event Bus). | ||||
| 
 | ||||
| Even in these scenarios, please consider avoiding the Shared Global Object pattern because the | ||||
| side-effects can be notoriously difficult to reason with. | ||||
| 
 | ||||
| ### References | ||||
| 
 | ||||
| To read more on this topic, check out the following references: | ||||
| 
 | ||||
| - [GlobalVariablesAreBad from C2 wiki](https://wiki.c2.com/?GlobalVariablesAreBad) | ||||
| 
 | ||||
| ## Singleton (Anti-pattern) | ||||
| 
 | ||||
| The classic [Singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) is an approach to ensure that only one  | ||||
| instance of a thing exists. | ||||
| 
 | ||||
| Here's an example of this pattern: | ||||
| 
 | ||||
| ```javascript | ||||
| class MyThing { | ||||
|   constructor() { | ||||
|     // ... | ||||
|   } | ||||
| 
 | ||||
|   // ... | ||||
| } | ||||
| 
 | ||||
| MyThing.instance = null; | ||||
| 
 | ||||
| export const getThingInstance = () => { | ||||
|   if (MyThing.instance) { | ||||
|     return MyThing.instance; | ||||
|   } | ||||
| 
 | ||||
|   const instance = new MyThing(); | ||||
|   MyThing.instance = instance; | ||||
|   return instance; | ||||
| }; | ||||
| ``` | ||||
| 
 | ||||
| ### What problems do Singletons cause? | ||||
| 
 | ||||
| It is a big assumption that only one instance of a thing should exist. More often than not, | ||||
| a Singleton is misused and causes very tight coupling amongst itself and the modules that reference it. | ||||
| 
 | ||||
| Here are some historic examples where this pattern was identified to be problematic: | ||||
| 
 | ||||
| - [Test issues caused by singleton class in IDE](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30398#note_331174190) | ||||
| - [Implicit Singleton created by module's shared variables](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/merge_requests/97#note_417515776) | ||||
| - [Complexity caused by Singletons](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29461#note_324585814) | ||||
| 
 | ||||
| Here are some ills that Singletons often produce: | ||||
| 
 | ||||
| 1. **Non-deterministic tests.** Singletons encourage non-deterministic tests because the single instance is shared across | ||||
|    individual tests, often causing the state of one test to bleed into another. | ||||
| 1. **High coupling.** Under the hood, clients of a singleton class all share a single specific  | ||||
|    instance of an object, which means this pattern inherits all the [problems of Shared Global Object](#what-problems-do-shared-global-objects-cause) | ||||
|    such as no clear ownership and no access control. These leads to high coupling situations that can | ||||
|    be buggy and difficult to untangle. | ||||
| 1. **Infectious.** Singletons are infectious, especially when they manage state. Consider the component | ||||
|    [RepoEditor](https://gitlab.com/gitlab-org/gitlab/blob/27ad6cb7b76430fbcbaf850df68c338d6719ed2b/app%2Fassets%2Fjavascripts%2Fide%2Fcomponents%2Frepo_editor.vue#L0-1) | ||||
|    used in the Web IDE. This component interfaces with a Singleton [Editor](https://gitlab.com/gitlab-org/gitlab/blob/862ad57c44ec758ef3942ac2e7a2bd40a37a9c59/app%2Fassets%2Fjavascripts%2Fide%2Flib%2Feditor.js#L21) | ||||
|    which manages some state for working with Monaco. Because of the Singleton nature of the Editor class, | ||||
|    the component `RepoEditor` is now forced to be a Singleton as well. Multiple instances of this component | ||||
|    would cause production issues because no one truly owns the instance of `Editor`. | ||||
| 
 | ||||
| ### Why is the Singleton pattern popular in other languages like Java? | ||||
| 
 | ||||
| This is because of the limitations of languages like Java where everything has to be wrapped | ||||
| in a class. In JavaScript we have things like object and function literals where we can solve | ||||
| many problems with a module that simply exports utility functions. | ||||
| 
 | ||||
| ### When could the Singleton pattern be actually appropriate?** | ||||
| 
 | ||||
| Singletons solve the problem of enforcing there to be only 1 instance of a thing. It's possible | ||||
| that a Singleton could be appropriate in the following rare cases: | ||||
| 
 | ||||
| - We need to manage some resource that **MUST** have just 1 instance (i.e. some hardware restriction). | ||||
| - There is a real [cross-cutting concern](https://en.wikipedia.org/wiki/Cross-cutting_concern) (e.g., logging) and a Singleton provides the simplest API. | ||||
| 
 | ||||
| Even in these scenarios, please consider avoiding the Singleton pattern. | ||||
| 
 | ||||
| ### What alternatives are there to the Singleton pattern? | ||||
| 
 | ||||
| #### Utility Functions | ||||
| 
 | ||||
| When no state needs to be managed, we can simply export utility functions from a module without | ||||
| messing with any class instantiation. | ||||
| 
 | ||||
| ```javascript | ||||
| // bad - Singleton | ||||
| export class ThingUtils { | ||||
|   static create() { | ||||
|     if(this.instance) { | ||||
|       return this.instance; | ||||
|     } | ||||
| 
 | ||||
|     this.instance = new ThingUtils(); | ||||
|     return this.instance; | ||||
|   } | ||||
| 
 | ||||
|   bar() { /* ... */ } | ||||
| 
 | ||||
|   fuzzify(id) { /* ... */ } | ||||
| } | ||||
| 
 | ||||
| // good - Utility functions | ||||
| export const bar = () => { /* ... */ }; | ||||
| 
 | ||||
| export const fuzzify = (id) => { /* ... */ }; | ||||
| ``` | ||||
| 
 | ||||
| #### Dependency Injection | ||||
| 
 | ||||
| [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) is an approach which breaks | ||||
| coupling by declaring a module's dependencies to be injected from outside the module (e.g., through constructor parameters, a bon-a-fide Dependency Injection framework, and even Vue's `provide/inject`). | ||||
| 
 | ||||
| ```javascript | ||||
| // bad - Vue component coupled to Singleton | ||||
| export default { | ||||
|   created() { | ||||
|     this.mediator = MyFooMediator.getInstance();     | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| // good - Vue component declares dependency | ||||
| export default { | ||||
|   inject: ['mediator'] | ||||
| }; | ||||
| ``` | ||||
| 
 | ||||
| ```javascript | ||||
| // bad - We're not sure where the singleton is in it's lifecycle so we init it here. | ||||
| export class Foo { | ||||
|   constructor() { | ||||
|     Bar.getInstance().init();     | ||||
|   } | ||||
| 
 | ||||
|   stuff() { | ||||
|     return Bar.getInstance().doStuff(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // good - Lets receive this dependency as a constructor argument. | ||||
| // It's also not our responsibility to manage the lifecycle. | ||||
| export class Foo { | ||||
|   constructor(bar) { | ||||
|     this.bar = bar; | ||||
|   } | ||||
| 
 | ||||
|   stuff() { | ||||
|     return this.bar.doStuff(); | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| In this example, the lifecycle and implementation details of `mediator` are all managed | ||||
| **outside** the component (most likely the page entrypoint). | ||||
|  | @ -6,79 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w | |||
| 
 | ||||
| # Design Patterns | ||||
| 
 | ||||
| ## Singletons | ||||
| The following design patterns are suggested approaches for solving common problems. Use discretion when evaluating | ||||
| if a certain pattern makes sense in your situation. Just because it is a pattern, doesn't mean it is a good one for your problem. | ||||
| 
 | ||||
| When exactly one object is needed for a given task, prefer to define it as a | ||||
| `class` rather than as an object literal. Prefer also to explicitly restrict | ||||
| instantiation, unless flexibility is important (such as for testing). | ||||
| **Please note:** When adding a design pattern to this document, be sure to clearly state the **problem it solves**. | ||||
| 
 | ||||
| ```javascript | ||||
| // bad | ||||
| ## TBD | ||||
| 
 | ||||
| const MyThing = { | ||||
|   prop1: 'hello', | ||||
|   method1: () => {} | ||||
| }; | ||||
| 
 | ||||
| export default MyThing; | ||||
| 
 | ||||
| // good | ||||
| 
 | ||||
| class MyThing { | ||||
|   constructor() { | ||||
|     this.prop1 = 'hello'; | ||||
|   } | ||||
|   method1() {} | ||||
| } | ||||
| 
 | ||||
| export default new MyThing(); | ||||
| 
 | ||||
| // best | ||||
| 
 | ||||
| export default class MyThing { | ||||
|   constructor() { | ||||
|     if (!MyThing.prototype.singleton) { | ||||
|       this.init(); | ||||
|       MyThing.prototype.singleton = this; | ||||
|     } | ||||
|     return MyThing.prototype.singleton; | ||||
|   } | ||||
| 
 | ||||
|   init() { | ||||
|     this.prop1 = 'hello'; | ||||
|   } | ||||
| 
 | ||||
|   method1() {} | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| ## Manipulating the DOM in a JS Class | ||||
| 
 | ||||
| When writing a class that needs to manipulate the DOM guarantee a container option is provided. | ||||
| This can be used when we need that class to be instantiated more than once in the same page. | ||||
| 
 | ||||
| Bad: | ||||
| 
 | ||||
| ```javascript | ||||
| class Foo { | ||||
|   constructor() { | ||||
|     document.querySelector('.bar'); | ||||
|   } | ||||
| } | ||||
| new Foo(); | ||||
| ``` | ||||
| 
 | ||||
| Good: | ||||
| 
 | ||||
| ```javascript | ||||
| class Foo { | ||||
|   constructor(opts) { | ||||
|     document.querySelector(`${opts.container} .bar`); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| new Foo({ container: '.my-element' }); | ||||
| ``` | ||||
| 
 | ||||
| You can find an example of the above in this [class](https://gitlab.com/gitlab-org/gitlab/blob/master/app/assets/javascripts/mini_pipeline_graph_dropdown.js); | ||||
| Stay tuned! | ||||
|  |  | |||
|  | @ -56,7 +56,11 @@ Reusable components with technical and usage guidelines can be found in our | |||
| 
 | ||||
| ## Design Patterns | ||||
| 
 | ||||
| Common JavaScript [design patterns](design_patterns.md) in the GitLab codebase. | ||||
| JavaScript [design patterns](design_patterns.md) in the GitLab codebase. | ||||
| 
 | ||||
| ## Design Anti-patterns | ||||
| 
 | ||||
| JavaScript [design anti-patterns](design_anti_patterns.md) we try to avoid. | ||||
| 
 | ||||
| ## Vue.js Best Practices | ||||
| 
 | ||||
|  |  | |||
|  | @ -131,6 +131,23 @@ add the line below to `/etc/gitlab/gitlab.rb` before increasing the max attachme | |||
| nginx['client_max_body_size'] = "200m" | ||||
| ``` | ||||
| 
 | ||||
| ## Customize session duration for Git Operations when 2FA is enabled **(PREMIUM)** | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/296669) in GitLab 13.9. | ||||
| > - It's deployed behind a feature flag, disabled by default. | ||||
| > - It's disabled on GitLab.com. | ||||
| > - It's not recommended for production use. | ||||
| > - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](../../../security/two_factor_authentication.md#enable-or-disable-two-factor-authentication-2fa-for-git-operations) | ||||
| 
 | ||||
| GitLab administrators can choose to customize the session duration (in minutes) for Git operations when 2FA is enabled. The default is 15 and this can be set to a value between 1 and 10080. | ||||
| 
 | ||||
| To set a limit on how long these sessions are valid: | ||||
| 
 | ||||
| 1. Navigate to **Admin Area > Settings > General**. | ||||
| 1. Expand the **Account and limit** section. | ||||
| 1. Fill in the **Session duration for Git operations when 2FA is enabled (minutes)** field. | ||||
| 1. Click **Save changes**. | ||||
| 
 | ||||
| ## Limiting lifetime of personal access tokens **(ULTIMATE SELF)** | ||||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) in GitLab Ultimate 12.6. | ||||
|  |  | |||
|  | @ -115,16 +115,9 @@ It is also possible to manage multiple assignees: | |||
| ### Reviewer | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216054) in GitLab 13.5. | ||||
| > - It was [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default. | ||||
| > - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49787) on GitLab 13.7. | ||||
| > - It's enabled on GitLab.com. | ||||
| > - It's recommended for production use. | ||||
| > - It can be enabled or disabled for a single project. | ||||
| > - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-merge-request-reviewers). **(FREE SELF)** | ||||
| > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/245190) in GitLab 13.9. | ||||
| 
 | ||||
| WARNING: | ||||
| This feature might not be available to you. Check the **version history** note above for details. | ||||
| 
 | ||||
| Requesting a code review is an important part of contributing code. However, deciding who should review | ||||
| your code and asking for a review are no easy tasks. Using the "assignee" field for both authors and | ||||
| reviewers makes it hard for others to determine who's doing what on a merge request. | ||||
|  | @ -137,31 +130,6 @@ This makes it easy to determine the relevant roles for the users involved in the | |||
| 
 | ||||
| To request it, open the **Reviewers** drop-down box to search for the user you wish to get a review from. | ||||
| 
 | ||||
| #### Enable or disable Merge Request Reviewers **(FREE SELF)** | ||||
| 
 | ||||
| Merge Request Reviewers is under development but ready for production use. | ||||
| It is deployed behind a feature flag that is **enabled by default**. | ||||
| [GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) | ||||
| can opt to disable it. | ||||
| 
 | ||||
| To enable it: | ||||
| 
 | ||||
| ```ruby | ||||
| # For the instance | ||||
| Feature.enable(:merge_request_reviewers) | ||||
| # For a single project | ||||
| Feature.enable(:merge_request_reviewers, Project.find(<project id>)) | ||||
| ``` | ||||
| 
 | ||||
| To disable it: | ||||
| 
 | ||||
| ```ruby | ||||
| # For the instance | ||||
| Feature.disable(:merge_request_reviewers) | ||||
| # For a single project | ||||
| Feature.disable(:merge_request_reviewers, Project.find(<project id>)) | ||||
| ``` | ||||
| 
 | ||||
| #### Approval Rule information for Reviewers **(PREMIUM)** | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233736) in GitLab 13.8. | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ module API | |||
|       expose(:downvotes)        { |merge_request, options| issuable_metadata.downvotes } | ||||
| 
 | ||||
|       expose :author, :assignees, :assignee, using: Entities::UserBasic | ||||
|       expose :reviewers, if: -> (merge_request, _) { merge_request.allows_reviewers? }, using: Entities::UserBasic | ||||
|       expose :reviewers, using: Entities::UserBasic | ||||
|       expose :source_project_id, :target_project_id | ||||
|       expose :labels do |merge_request, options| | ||||
|         if options[:with_labels_details] | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ module Gitlab | |||
|     module Otp | ||||
|       class SessionEnforcer | ||||
|         OTP_SESSIONS_NAMESPACE = 'session:otp' | ||||
|         DEFAULT_EXPIRATION = 15.minutes.to_i | ||||
| 
 | ||||
|         def initialize(key) | ||||
|           @key = key | ||||
|  | @ -13,7 +12,7 @@ module Gitlab | |||
| 
 | ||||
|         def update_session | ||||
|           Gitlab::Redis::SharedState.with do |redis| | ||||
|             redis.setex(key_name, DEFAULT_EXPIRATION, true) | ||||
|             redis.setex(key_name, session_expiry_in_seconds, true) | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|  | @ -30,6 +29,10 @@ module Gitlab | |||
|         def key_name | ||||
|           @key_name ||= "#{OTP_SESSIONS_NAMESPACE}:#{key.id}" | ||||
|         end | ||||
| 
 | ||||
|         def session_expiry_in_seconds | ||||
|           Gitlab::CurrentSettings.git_two_factor_session_expiry.minutes.to_i | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -92,10 +92,6 @@ module Gitlab | |||
|         tracking_category: 'Growth::Activation::Experiment::CiSyntaxTemplates', | ||||
|         rollout_strategy: :user | ||||
|       }, | ||||
|       pipelines_empty_state: { | ||||
|         tracking_category: 'Growth::Activation::Experiment::PipelinesEmptyState', | ||||
|         rollout_strategy: :user | ||||
|       }, | ||||
|       invite_members_new_dropdown: { | ||||
|         tracking_category: 'Growth::Expansion::Experiment::InviteMembersNewDropdown' | ||||
|       }, | ||||
|  |  | |||
|  | @ -181,8 +181,7 @@ module Gitlab | |||
|         end | ||||
|         types MergeRequest | ||||
|         condition do | ||||
|           Feature.enabled?(:merge_request_reviewers, project, default_enabled: :yaml) && | ||||
|             current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) | ||||
|           current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) | ||||
|         end | ||||
|         parse_params do |reviewer_param| | ||||
|           extract_users(reviewer_param) | ||||
|  | @ -221,7 +220,6 @@ module Gitlab | |||
|         types MergeRequest | ||||
|         condition do | ||||
|           quick_action_target.persisted? && | ||||
|             Feature.enabled?(:merge_request_reviewers, project, default_enabled: :yaml) && | ||||
|             quick_action_target.reviewers.any? && | ||||
|             current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) | ||||
|         end | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| desc 'Security check via brakeman' | ||||
| task :brakeman do | ||||
|   # We get 0 warnings at level 'w3' but we would like to reach 'w2'. Merge | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :cache do | ||||
|   namespace :clear do | ||||
|     REDIS_CLEAR_BATCH_SIZE = 1000 # There seems to be no speedup when pushing beyond 1,000 | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :ci do | ||||
|   namespace :cleanup do | ||||
|     desc "GitLab | CI | Clean running builds" | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :cleanup do | ||||
|     desc "GitLab | Cleanup | Delete moved repositories" | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module ConfigLint | ||||
|   def self.run(files) | ||||
|     failures = files.reject do |file| | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| desc 'Show a list of obsolete `ignored_columns`' | ||||
| task 'db:obsolete_ignored_columns' => :environment do | ||||
|   list = Gitlab::Database::ObsoleteIgnoredColumns.new.execute | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| task dev: ["dev:setup"] | ||||
| 
 | ||||
| namespace :dev do | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| desc 'Checks if migrations in a branch require downtime' | ||||
| task downtime_check: :environment do | ||||
|   repo = if defined?(Gitlab::License) | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| unless Rails.env.production? | ||||
|   desc "GitLab | Run ESLint" | ||||
|   task eslint: ['yarn:check'] do | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :file_hooks do | ||||
|   desc 'Validate existing file hooks' | ||||
|   task validate: :environment do | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| unless Rails.env.production? | ||||
|   namespace :frontend do | ||||
|     desc 'GitLab | Frontend | Generate fixtures for JavaScript tests' | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gemojione do | ||||
|   desc 'Generates Emoji SHA256 digests' | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :artifacts do | ||||
|     desc 'GitLab | Artifacts | Check integrity of uploaded job artifacts' | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'logger' | ||||
| require 'resolv-replace' | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'active_record/fixtures' | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :import do | ||||
|     desc "GitLab | Import | Add all users to all projects (admin users are added as maintainers)" | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   desc 'GitLab | Check the configuration of GitLab and its environment' | ||||
|   task check: :gitlab_environment do | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :container_registry do | ||||
|     desc "GitLab | Container Registry | Configure" | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :db do | ||||
|     desc 'GitLab | DB | Manually insert schema migration version' | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :doctor do | ||||
|     desc "GitLab | Check if the database encrypted values can be decrypted using current secrets" | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :exclusive_lease do | ||||
|     desc 'GitLab | Exclusive Lease | Clear existing exclusive leases for specified scope (default: *)' | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :external_diffs do | ||||
|     desc "Override external diffs in file storage to be in object storage instead. This does not change the actual location of the data" | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| namespace :gitlab do | ||||
|   namespace :features do | ||||
|     desc 'GitLab | Features | Enable direct Git access via Rugged for NFS' | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue