Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									e6779ab919
								
							
						
					
					
						commit
						38a1a6cb91
					
				|  | @ -107,3 +107,7 @@ overrides: | ||||||
|       import/no-nodejs-modules: off |       import/no-nodejs-modules: off | ||||||
|       filenames/match-regex: off |       filenames/match-regex: off | ||||||
|       no-console: off |       no-console: off | ||||||
|  |   - files: | ||||||
|  |       - '*.stories.js' | ||||||
|  |     rules: | ||||||
|  |       filenames/match-regex: off | ||||||
|  |  | ||||||
|  | @ -2,6 +2,10 @@ | ||||||
|   - source scripts/utils.sh |   - source scripts/utils.sh | ||||||
|   - run_timed_command "retry yarn install --frozen-lockfile" |   - run_timed_command "retry yarn install --frozen-lockfile" | ||||||
| 
 | 
 | ||||||
|  | .storybook-yarn-install: &storybook-yarn-install | ||||||
|  |   - source scripts/utils.sh | ||||||
|  |   - run_timed_command "retry yarn run storybook:install --frozen-lockfile" | ||||||
|  | 
 | ||||||
| .compile-assets-base: | .compile-assets-base: | ||||||
|   extends: |   extends: | ||||||
|     - .default-retry |     - .default-retry | ||||||
|  | @ -80,6 +84,15 @@ update-yarn-cache: | ||||||
|   script: |   script: | ||||||
|     - *yarn-install |     - *yarn-install | ||||||
| 
 | 
 | ||||||
|  | update-storybook-yarn-cache: | ||||||
|  |   extends: | ||||||
|  |     - .default-retry | ||||||
|  |     - .storybook-yarn-cache-push | ||||||
|  |     - .shared:rules:update-cache | ||||||
|  |   stage: prepare | ||||||
|  |   script: | ||||||
|  |     - *storybook-yarn-install | ||||||
|  | 
 | ||||||
| .frontend-fixtures-base: | .frontend-fixtures-base: | ||||||
|   extends: |   extends: | ||||||
|     - .default-retry |     - .default-retry | ||||||
|  | @ -344,3 +357,29 @@ startup-css-check as-if-foss: | ||||||
|   needs: |   needs: | ||||||
|     - job: "compile-test-assets as-if-foss" |     - job: "compile-test-assets as-if-foss" | ||||||
|     - job: "rspec frontend_fixture as-if-foss" |     - job: "rspec frontend_fixture as-if-foss" | ||||||
|  | 
 | ||||||
|  | .compile-storybook-base: | ||||||
|  |   extends: | ||||||
|  |     - .frontend-test-base | ||||||
|  |     - .storybook-yarn-cache | ||||||
|  |   script: | ||||||
|  |     - *yarn-install  # storybook depends on the global webpack config, so we must install global deps. | ||||||
|  |     - *storybook-yarn-install | ||||||
|  |     - yarn run storybook:build | ||||||
|  | 
 | ||||||
|  | compile-storybook: | ||||||
|  |   extends: | ||||||
|  |     - .compile-storybook-base | ||||||
|  |     - .frontend:rules:default-frontend-jobs | ||||||
|  |   artifacts: | ||||||
|  |     name: storybook | ||||||
|  |     expire_in: 31d | ||||||
|  |     when: always | ||||||
|  |     paths: | ||||||
|  |       - storybook/public | ||||||
|  | 
 | ||||||
|  | compile-storybook as-if-foss: | ||||||
|  |   extends: | ||||||
|  |     - .compile-storybook-base | ||||||
|  |     - .as-if-foss | ||||||
|  |     - .frontend:rules:default-frontend-jobs-as-if-foss | ||||||
|  |  | ||||||
|  | @ -88,6 +88,16 @@ | ||||||
|   <<: *assets-cache |   <<: *assets-cache | ||||||
|   policy: push  # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up. |   policy: push  # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up. | ||||||
| 
 | 
 | ||||||
|  | .storybook-node-modules-cache: &storybook-node-modules-cache | ||||||
|  |   key: "storybook-node-modules-${NODE_ENV}-v1" | ||||||
|  |   paths: | ||||||
|  |     - storybook/node_modules/ | ||||||
|  |   policy: pull | ||||||
|  | 
 | ||||||
|  | .storybook-node-modules-cache-push: &storybook-node-modules-cache-push | ||||||
|  |   <<: *storybook-node-modules-cache | ||||||
|  |   policy: push  # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up. | ||||||
|  | 
 | ||||||
| .rubocop-cache: &rubocop-cache | .rubocop-cache: &rubocop-cache | ||||||
|   key: "rubocop-v1" |   key: "rubocop-v1" | ||||||
|   paths: |   paths: | ||||||
|  | @ -181,6 +191,14 @@ | ||||||
|     - *node-modules-cache-push |     - *node-modules-cache-push | ||||||
|     - *assets-cache-push |     - *assets-cache-push | ||||||
| 
 | 
 | ||||||
|  | .storybook-yarn-cache: | ||||||
|  |   cache: | ||||||
|  |     - *storybook-node-modules-cache | ||||||
|  | 
 | ||||||
|  | .storybook-yarn-cache-push: | ||||||
|  |   cache: | ||||||
|  |     - *storybook-node-modules-cache-push | ||||||
|  | 
 | ||||||
| .use-pg11: | .use-pg11: | ||||||
|   image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.36" |   image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.36" | ||||||
|   services: |   services: | ||||||
|  |  | ||||||
|  | @ -8,12 +8,14 @@ pages: | ||||||
|     - coverage-frontend |     - coverage-frontend | ||||||
|     - karma |     - karma | ||||||
|     - compile-production-assets |     - compile-production-assets | ||||||
|  |     - compile-storybook | ||||||
|   script: |   script: | ||||||
|     - mv public/ .public/ |     - mv public/ .public/ | ||||||
|     - mkdir public/ |     - mkdir public/ | ||||||
|     - mv coverage/ public/coverage-ruby/ || true |     - mv coverage/ public/coverage-ruby/ || true | ||||||
|     - mv coverage-frontend/ public/coverage-frontend/ || true |     - mv coverage-frontend/ public/coverage-frontend/ || true | ||||||
|     - mv coverage-javascript/ public/coverage-javascript/ || true |     - mv coverage-javascript/ public/coverage-javascript/ || true | ||||||
|  |     - mv storybook/public public/storybook || true | ||||||
|     - cp .public/assets/application-*.css public/application.css || true |     - cp .public/assets/application-*.css public/application.css || true | ||||||
|     - cp .public/assets/application-*.css.gz public/application.css.gz || true |     - cp .public/assets/application-*.css.gz public/application.css.gz || true | ||||||
|   artifacts: |   artifacts: | ||||||
|  |  | ||||||
|  | @ -251,6 +251,22 @@ static-analysis as-if-foss: | ||||||
|     - .static-analysis:rules:as-if-foss |     - .static-analysis:rules:as-if-foss | ||||||
|     - .as-if-foss |     - .as-if-foss | ||||||
| 
 | 
 | ||||||
|  | zeitwerk-check: | ||||||
|  |   extends: | ||||||
|  |     - .rails-cache | ||||||
|  |     - .default-before_script | ||||||
|  |     - .rails:rules:ee-and-foss-unit | ||||||
|  |   variables: | ||||||
|  |     BUNDLE_WITHOUT: "" | ||||||
|  |     SETUP_DB: "false" | ||||||
|  |   needs: [] | ||||||
|  |   stage: test | ||||||
|  |   script: | ||||||
|  |     - sed -i -e "s/config\.autoloader = :classic/config\.autoloader = :zeitwerk/" config/application.rb | ||||||
|  |     - RAILS_ENV=test bundle exec rake zeitwerk:check | ||||||
|  |     - RAILS_ENV=development bundle exec rake zeitwerk:check | ||||||
|  |     - RAILS_ENV=production bundle exec rake zeitwerk:check | ||||||
|  | 
 | ||||||
| rspec migration pg12: | rspec migration pg12: | ||||||
|   extends: |   extends: | ||||||
|     - .rspec-base-pg12 |     - .rspec-base-pg12 | ||||||
|  |  | ||||||
|  | @ -30,7 +30,7 @@ export const fetchProjects = ({ commit, state }, search) => { | ||||||
| 
 | 
 | ||||||
|   if (groupId) { |   if (groupId) { | ||||||
|     // TODO (https://gitlab.com/gitlab-org/gitlab/-/issues/323331): For errors `createFlash` is called twice; in `callback` and in `Api.groupProjects`
 |     // TODO (https://gitlab.com/gitlab-org/gitlab/-/issues/323331): For errors `createFlash` is called twice; in `callback` and in `Api.groupProjects`
 | ||||||
|     Api.groupProjects(groupId, search, {}, callback); |     Api.groupProjects(groupId, search, { order_by: 'similarity' }, callback); | ||||||
|   } else { |   } else { | ||||||
|     // The .catch() is due to the API method not handling a rejection properly
 |     // The .catch() is due to the API method not handling a rejection properly
 | ||||||
|     Api.projects(search, { order_by: 'id' }, callback).catch(() => { |     Api.projects(search, { order_by: 'id' }, callback).catch(() => { | ||||||
|  |  | ||||||
|  | @ -22,8 +22,16 @@ export default { | ||||||
|       required: false, |       required: false, | ||||||
|       default: '', |       default: '', | ||||||
|     }, |     }, | ||||||
|  |     pronouns: { | ||||||
|  |       type: String, | ||||||
|  |       required: false, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|  |     hasPronouns() { | ||||||
|  |       return this.pronouns !== null && this.pronouns.trim() !== ''; | ||||||
|  |     }, | ||||||
|     isBusy() { |     isBusy() { | ||||||
|       return isUserBusy(this.availability); |       return isUserBusy(this.availability); | ||||||
|     }, |     }, | ||||||
|  | @ -32,9 +40,18 @@ export default { | ||||||
| </script> | </script> | ||||||
| <template> | <template> | ||||||
|   <span :class="containerClasses"> |   <span :class="containerClasses"> | ||||||
|     <gl-sprintf v-if="isBusy" :message="s__('UserAvailability|%{author} (Busy)')"> |     <gl-sprintf :message="s__('UserAvailability|%{author} %{spanStart}(Busy)%{spanEnd}')"> | ||||||
|       <template #author>{{ name }}</template> |       <template #author | ||||||
|  |         >{{ name }} | ||||||
|  |         <span v-if="hasPronouns" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal" | ||||||
|  |           >({{ pronouns }})</span | ||||||
|  |         ></template | ||||||
|  |       > | ||||||
|  |       <template #span="{ content }" | ||||||
|  |         ><span v-if="isBusy" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal">{{ | ||||||
|  |           content | ||||||
|  |         }}</span> | ||||||
|  |       </template> | ||||||
|     </gl-sprintf> |     </gl-sprintf> | ||||||
|     <template v-else>{{ name }}</template> |  | ||||||
|   </span> |   </span> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -44,6 +44,7 @@ const populateUserInfo = (user) => { | ||||||
|           bioHtml: sanitize(userData.bio_html), |           bioHtml: sanitize(userData.bio_html), | ||||||
|           workInformation: userData.work_information, |           workInformation: userData.work_information, | ||||||
|           websiteUrl: userData.website_url, |           websiteUrl: userData.website_url, | ||||||
|  |           pronouns: userData.pronouns, | ||||||
|           loaded: true, |           loaded: true, | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | /* eslint-disable @gitlab/require-i18n-strings */ | ||||||
|  | 
 | ||||||
|  | import TodoButton from './todo_button.vue'; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   component: TodoButton, | ||||||
|  |   title: 'vue_shared/components/todo_button', | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const Template = (args, { argTypes }) => ({ | ||||||
|  |   components: { TodoButton }, | ||||||
|  |   props: Object.keys(argTypes), | ||||||
|  |   template: '<todo-button v-bind="$props" v-on="$props" />', | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const Default = Template.bind({}); | ||||||
|  | Default.argTypes = { | ||||||
|  |   isTodo: { | ||||||
|  |     description: 'True if to-do is unresolved (i.e. not "done")', | ||||||
|  |     control: { type: 'boolean' }, | ||||||
|  |   }, | ||||||
|  |   click: { action: 'clicked' }, | ||||||
|  | }; | ||||||
|  | @ -72,7 +72,11 @@ export default { | ||||||
|         <template v-else> |         <template v-else> | ||||||
|           <div class="gl-mb-3"> |           <div class="gl-mb-3"> | ||||||
|             <h5 class="gl-m-0"> |             <h5 class="gl-m-0"> | ||||||
|               <user-name-with-status :name="user.name" :availability="availabilityStatus" /> |               <user-name-with-status | ||||||
|  |                 :name="user.name" | ||||||
|  |                 :availability="availabilityStatus" | ||||||
|  |                 :pronouns="user.pronouns" | ||||||
|  |               /> | ||||||
|             </h5> |             </h5> | ||||||
|             <span class="gl-text-gray-500">@{{ user.username }}</span> |             <span class="gl-text-gray-500">@{{ user.username }}</span> | ||||||
|           </div> |           </div> | ||||||
|  |  | ||||||
|  | @ -49,6 +49,7 @@ class Integration < ApplicationRecord | ||||||
|     hangouts_chat |     hangouts_chat | ||||||
|     irker |     irker | ||||||
|     packagist pipelines_email pivotaltracker pushover |     packagist pipelines_email pivotaltracker pushover | ||||||
|  |     mattermost mattermost_slash_commands microsoft_teams mock_ci mock_monitoring | ||||||
|   ].to_set.freeze |   ].to_set.freeze | ||||||
| 
 | 
 | ||||||
|   def self.renamed?(name) |   def self.renamed?(name) | ||||||
|  |  | ||||||
|  | @ -174,10 +174,11 @@ class Project < ApplicationRecord | ||||||
|   has_one :irker_integration, class_name: 'Integrations::Irker' |   has_one :irker_integration, class_name: 'Integrations::Irker' | ||||||
|   has_one :jenkins_service, class_name: 'Integrations::Jenkins' |   has_one :jenkins_service, class_name: 'Integrations::Jenkins' | ||||||
|   has_one :jira_service, class_name: 'Integrations::Jira' |   has_one :jira_service, class_name: 'Integrations::Jira' | ||||||
|   has_one :mattermost_service, class_name: 'Integrations::Mattermost' |   has_one :mattermost_integration, class_name: 'Integrations::Mattermost' | ||||||
|   has_one :mattermost_slash_commands_service, class_name: 'Integrations::MattermostSlashCommands' |   has_one :mattermost_slash_commands_integration, class_name: 'Integrations::MattermostSlashCommands' | ||||||
|   has_one :microsoft_teams_service, class_name: 'Integrations::MicrosoftTeams' |   has_one :microsoft_teams_integration, class_name: 'Integrations::MicrosoftTeams' | ||||||
|   has_one :mock_ci_service, class_name: 'Integrations::MockCi' |   has_one :mock_ci_integration, class_name: 'Integrations::MockCi' | ||||||
|  |   has_one :mock_monitoring_integration, class_name: 'MockMonitoringService' | ||||||
|   has_one :packagist_integration, class_name: 'Integrations::Packagist' |   has_one :packagist_integration, class_name: 'Integrations::Packagist' | ||||||
|   has_one :pipelines_email_integration, class_name: 'Integrations::PipelinesEmail' |   has_one :pipelines_email_integration, class_name: 'Integrations::PipelinesEmail' | ||||||
|   has_one :pivotaltracker_integration, class_name: 'Integrations::Pivotaltracker' |   has_one :pivotaltracker_integration, class_name: 'Integrations::Pivotaltracker' | ||||||
|  | @ -190,7 +191,6 @@ class Project < ApplicationRecord | ||||||
|   has_one :webex_teams_service, class_name: 'Integrations::WebexTeams' |   has_one :webex_teams_service, class_name: 'Integrations::WebexTeams' | ||||||
|   has_one :youtrack_service, class_name: 'Integrations::Youtrack' |   has_one :youtrack_service, class_name: 'Integrations::Youtrack' | ||||||
|   has_one :prometheus_service, inverse_of: :project |   has_one :prometheus_service, inverse_of: :project | ||||||
|   has_one :mock_monitoring_service |  | ||||||
| 
 | 
 | ||||||
|   has_one :root_of_fork_network, |   has_one :root_of_fork_network, | ||||||
|           foreign_key: 'root_project_id', |           foreign_key: 'root_project_id', | ||||||
|  |  | ||||||
|  | @ -10,6 +10,10 @@ module Ci | ||||||
|          resource_group scheduling_type].freeze |          resource_group scheduling_type].freeze | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     def self.extra_accessors | ||||||
|  |       [] | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     def execute(build) |     def execute(build) | ||||||
|       build.ensure_scheduling_type! |       build.ensure_scheduling_type! | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
|     = form.label :only_allow_merge_if_pipeline_succeeds, class: 'form-check-label' do |     = form.label :only_allow_merge_if_pipeline_succeeds, class: 'form-check-label' do | ||||||
|       = s_('ProjectSettings|Pipelines must succeed') |       = s_('ProjectSettings|Pipelines must succeed') | ||||||
|       .text-secondary |       .text-secondary | ||||||
|         - configuring_pipelines_for_merge_requests_help_link_url = help_page_path('ci/merge_request_pipelines/index.md', anchor: 'configuring-pipelines-for-merge-requests') |         - configuring_pipelines_for_merge_requests_help_link_url = help_page_path('ci/merge_request_pipelines/index.md', anchor: 'configure-pipelines-for-merge-requests') | ||||||
|         - configuring_pipelines_for_merge_requests_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: configuring_pipelines_for_merge_requests_help_link_url } |         - configuring_pipelines_for_merge_requests_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: configuring_pipelines_for_merge_requests_help_link_url } | ||||||
|         = s_('ProjectSettings|To enable this feature, configure pipelines. %{link_start}How to configure pipelines for merge requests?%{link_end}').html_safe % { link_start: configuring_pipelines_for_merge_requests_help_link_start, link_end: '</a>'.html_safe } |         = s_('ProjectSettings|To enable this feature, configure pipelines. %{link_start}How to configure pipelines for merge requests?%{link_end}').html_safe % { link_start: configuring_pipelines_for_merge_requests_help_link_start, link_end: '</a>'.html_safe } | ||||||
|   .form-check.mb-2 |   .form-check.mb-2 | ||||||
|  |  | ||||||
|  | @ -32,6 +32,8 @@ module Gitlab | ||||||
|     require_dependency Rails.root.join('lib/gitlab/middleware/rack_multipart_tempfile_factory') |     require_dependency Rails.root.join('lib/gitlab/middleware/rack_multipart_tempfile_factory') | ||||||
|     require_dependency Rails.root.join('lib/gitlab/runtime') |     require_dependency Rails.root.join('lib/gitlab/runtime') | ||||||
| 
 | 
 | ||||||
|  |     config.autoloader = :classic | ||||||
|  | 
 | ||||||
|     # Settings in config/environments/* take precedence over those specified here. |     # Settings in config/environments/* take precedence over those specified here. | ||||||
|     # Application configuration should go into files in config/initializers |     # Application configuration should go into files in config/initializers | ||||||
|     # -- all .rb files in that directory are automatically loaded. |     # -- all .rb files in that directory are automatically loaded. | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ Rails.application.configure do | ||||||
|   config.action_mailer.raise_delivery_errors = true |   config.action_mailer.raise_delivery_errors = true | ||||||
|   # Don't make a mess when bootstrapping a development environment |   # Don't make a mess when bootstrapping a development environment | ||||||
|   config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') |   config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') | ||||||
|   config.action_mailer.preview_path = 'app/mailers/previews' |   config.action_mailer.preview_path = "#{Rails.root}{/ee,}/app/mailers/previews" | ||||||
| 
 | 
 | ||||||
|   config.eager_load = false |   config.eager_load = false | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ rescue Gitlab::Runtime::IdentificationError => e | ||||||
|   \n!! RUNTIME IDENTIFICATION FAILED: #{e} |   \n!! RUNTIME IDENTIFICATION FAILED: #{e} | ||||||
|   Runtime based configuration settings may not work properly. |   Runtime based configuration settings may not work properly. | ||||||
|   If you continue to see this error, please file an issue via |   If you continue to see this error, please file an issue via | ||||||
|   https://gitlab.com/gitlab-org/gitlab/issues/new |   https://gitlab.com/gitlab-org/gitlab/-/issues/new | ||||||
|   NOTICE |   NOTICE | ||||||
|   Gitlab::AppLogger.error(message) |   Gitlab::AppLogger.error(message) | ||||||
|   Gitlab::ErrorTracking.track_exception(e) |   Gitlab::ErrorTracking.track_exception(e) | ||||||
|  | @ -1,3 +1,3 @@ | ||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
| 
 | 
 | ||||||
| GlobalID.prepend(Gitlab::Patch::GlobalID) | GlobalID.prepend(Gitlab::Patch::GlobalId) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,71 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | Rails.autoloaders.each do |autoloader| | ||||||
|  |   # We need to ignore these since these are non-Ruby files | ||||||
|  |   # that do not define Ruby classes / modules | ||||||
|  |   autoloader.ignore(Rails.root.join('lib/support')) | ||||||
|  | 
 | ||||||
|  |   # Ignore generators since these are loaded manually by Rails | ||||||
|  |   # https://github.com/rails/rails/blob/v6.1.3.2/railties/lib/rails/command/behavior.rb#L56-L65 | ||||||
|  |   autoloader.ignore(Rails.root.join('lib/generators')) | ||||||
|  |   autoloader.ignore(Rails.root.join('ee/lib/generators')) if Gitlab.ee? | ||||||
|  | 
 | ||||||
|  |   # Mailer previews are also loaded manually by Rails | ||||||
|  |   # https://github.com/rails/rails/blob/v6.1.3.2/actionmailer/lib/action_mailer/preview.rb#L121-L125 | ||||||
|  |   autoloader.ignore(Rails.root.join('app/mailers/previews')) | ||||||
|  |   autoloader.ignore(Rails.root.join('ee/app/mailers/previews')) if Gitlab.ee? | ||||||
|  | 
 | ||||||
|  |   autoloader.inflector.inflect( | ||||||
|  |     'api' => 'API', | ||||||
|  |     'api_authentication' => 'APIAuthentication', | ||||||
|  |     'api_guard' => 'APIGuard', | ||||||
|  |     'group_api_compatibility' => 'GroupAPICompatibility', | ||||||
|  |     'project_api_compatibility' => 'ProjectAPICompatibility', | ||||||
|  |     'ast' => 'AST', | ||||||
|  |     'cte' => 'CTE', | ||||||
|  |     'recursive_cte' => 'RecursiveCTE', | ||||||
|  |     'cidr' => 'CIDR', | ||||||
|  |     'cli' => 'CLI', | ||||||
|  |     'dn' => 'DN', | ||||||
|  |     'global_id_type' => 'GlobalIDType', | ||||||
|  |     'global_id_compatibility' => 'GlobalIDCompatibility', | ||||||
|  |     'hll' => 'HLL', | ||||||
|  |     'hll_redis_counter' => 'HLLRedisCounter', | ||||||
|  |     'redis_hll_metric' => 'RedisHLLMetric', | ||||||
|  |     'hmac_token' => 'HMACToken', | ||||||
|  |     'html' => 'HTML', | ||||||
|  |     'html_parser' => 'HTMLParser', | ||||||
|  |     'html_gitlab' => 'HTMLGitlab', | ||||||
|  |     'http' => 'HTTP', | ||||||
|  |     'http_connection_adapter' => 'HTTPConnectionAdapter', | ||||||
|  |     'http_clone_enabled_check' => 'HTTPCloneEnabledCheck', | ||||||
|  |     'hangouts_chat_http_override' => 'HangoutsChatHTTPOverride', | ||||||
|  |     'chunked_io' => 'ChunkedIO', | ||||||
|  |     'http_io' => 'HttpIO', | ||||||
|  |     'json_formatter' => 'JSONFormatter', | ||||||
|  |     'json_web_token' => 'JSONWebToken', | ||||||
|  |     'as_json' => 'AsJSON', | ||||||
|  |     'jwt_token' => 'JWTToken', | ||||||
|  |     'ldap_key' => 'LDAPKey', | ||||||
|  |     'mr_note' => 'MRNote', | ||||||
|  |     'pdf' => 'PDF', | ||||||
|  |     'rsa_token' => 'RSAToken', | ||||||
|  |     'san_extension' => 'SANExtension', | ||||||
|  |     'sca' => 'SCA', | ||||||
|  |     'spdx' => 'SPDX', | ||||||
|  |     'sql' => 'SQL', | ||||||
|  |     'sse_helpers' => 'SSEHelpers', | ||||||
|  |     'ssh_key' => 'SSHKey', | ||||||
|  |     'ssh_key_with_user' => 'SSHKeyWithUser', | ||||||
|  |     'ssh_public_key' => 'SSHPublicKey', | ||||||
|  |     'git_ssh_proxy' => 'GitSSHProxy', | ||||||
|  |     'git_user_default_ssh_config_check' => 'GitUserDefaultSSHConfigCheck', | ||||||
|  |     'binary_stl' => 'BinarySTL', | ||||||
|  |     'text_stl' => 'TextSTL', | ||||||
|  |     'svg' => 'SVG', | ||||||
|  |     'function_uri' => 'FunctionURI', | ||||||
|  |     'uuid' => 'UUID', | ||||||
|  |     'vulnerability_uuid' => 'VulnerabilityUUID', | ||||||
|  |     'vs_code_extension_activity_unique_counter' => 'VSCodeExtensionActivityUniqueCounter' | ||||||
|  |   ) | ||||||
|  | end | ||||||
|  | @ -6,7 +6,7 @@ class Gitlab::Seeder::Environments | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def seed! |   def seed! | ||||||
|     @project.create_mock_monitoring_service!(active: true) |     @project.create_mock_monitoring_integration!(active: true) | ||||||
| 
 | 
 | ||||||
|     create_master_deployments!('production') |     create_master_deployments!('production') | ||||||
|     create_master_deployments!('staging') |     create_master_deployments!('staging') | ||||||
|  |  | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class CreateDastSiteProfilesBuilds < ActiveRecord::Migration[6.1] | ||||||
|  |   def up | ||||||
|  |     table_comment = { owner: 'group::dynamic analysis', description: 'Join table between DAST Site Profiles and CI Builds' } | ||||||
|  | 
 | ||||||
|  |     create_table :dast_site_profiles_builds, primary_key: [:dast_site_profile_id, :ci_build_id], comment: table_comment.to_json do |t| | ||||||
|  |       t.bigint :dast_site_profile_id, null: false | ||||||
|  |       t.bigint :ci_build_id, null: false | ||||||
|  | 
 | ||||||
|  |       t.index :ci_build_id, unique: true, name: :dast_site_profiles_builds_on_ci_build_id | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     drop_table :dast_site_profiles_builds | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class AddCiBuildIdFkToDastSiteProfilesBuilds < ActiveRecord::Migration[6.1] | ||||||
|  |   include Gitlab::Database::MigrationHelpers | ||||||
|  | 
 | ||||||
|  |   DOWNTIME = false | ||||||
|  | 
 | ||||||
|  |   disable_ddl_transaction! | ||||||
|  | 
 | ||||||
|  |   def up | ||||||
|  |     add_concurrent_foreign_key :dast_site_profiles_builds, :ci_builds, column: :ci_build_id, on_delete: :cascade | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     with_lock_retries do | ||||||
|  |       remove_foreign_key :dast_site_profiles_builds, column: :ci_build_id | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class AddDastSiteProfileIdFkToDastSiteProfilesBuilds < ActiveRecord::Migration[6.1] | ||||||
|  |   include Gitlab::Database::MigrationHelpers | ||||||
|  | 
 | ||||||
|  |   DOWNTIME = false | ||||||
|  | 
 | ||||||
|  |   disable_ddl_transaction! | ||||||
|  | 
 | ||||||
|  |   def up | ||||||
|  |     add_concurrent_foreign_key :dast_site_profiles_builds, :dast_site_profiles, column: :dast_site_profile_id, on_delete: :cascade | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     with_lock_retries do | ||||||
|  |       remove_foreign_key :dast_site_profiles_builds, column: :dast_site_profile_id | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class CreateDastScannerProfilesBuilds < ActiveRecord::Migration[6.1] | ||||||
|  |   def up | ||||||
|  |     table_comment = { owner: 'group::dynamic analysis', description: 'Join table between DAST Scanner Profiles and CI Builds' } | ||||||
|  | 
 | ||||||
|  |     create_table :dast_scanner_profiles_builds, primary_key: [:dast_scanner_profile_id, :ci_build_id], comment: table_comment.to_json do |t| | ||||||
|  |       t.bigint :dast_scanner_profile_id, null: false | ||||||
|  |       t.bigint :ci_build_id, null: false | ||||||
|  | 
 | ||||||
|  |       t.index :ci_build_id, unique: true, name: :dast_scanner_profiles_builds_on_ci_build_id | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     drop_table :dast_scanner_profiles_builds | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class AddCiBuildIdFkToDastScannerProfilesBuilds < ActiveRecord::Migration[6.1] | ||||||
|  |   include Gitlab::Database::MigrationHelpers | ||||||
|  | 
 | ||||||
|  |   DOWNTIME = false | ||||||
|  | 
 | ||||||
|  |   disable_ddl_transaction! | ||||||
|  | 
 | ||||||
|  |   def up | ||||||
|  |     add_concurrent_foreign_key :dast_scanner_profiles_builds, :ci_builds, column: :ci_build_id, on_delete: :cascade | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     with_lock_retries do | ||||||
|  |       remove_foreign_key :dast_scanner_profiles_builds, column: :ci_build_id | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class AddDastScannerProfileIdFkToDastScannerProfilesBuilds < ActiveRecord::Migration[6.1] | ||||||
|  |   include Gitlab::Database::MigrationHelpers | ||||||
|  | 
 | ||||||
|  |   DOWNTIME = false | ||||||
|  | 
 | ||||||
|  |   disable_ddl_transaction! | ||||||
|  | 
 | ||||||
|  |   def up | ||||||
|  |     add_concurrent_foreign_key :dast_scanner_profiles_builds, :dast_scanner_profiles, column: :dast_scanner_profile_id, on_delete: :cascade | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     with_lock_retries do | ||||||
|  |       remove_foreign_key :dast_scanner_profiles_builds, column: :dast_scanner_profile_id | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | fa373e98739d57d829273cfa9246137e2c151be67e97183c1dcdb288150aaeb5 | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | c7cf4aad7637d793d1ace8fee02111bc9b0d2eea09efadb0fd616bc5c5e5550c | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | da868be7c8edefc462110b5b36415870cc0c7c59dba1e3d514348011a9e70642 | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | 2d025932dca7a407968e14872ce053461e69550098ca089d4e6ece323d240927 | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | 7529373266b6c9b179367d5fa8775f5e2ad600008957b3a821d689aec70c7407 | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | 3818094a4470ff7d0c105c000655dac4205e8265f78df638df0e2ef3dc6deaf3 | ||||||
|  | @ -12047,6 +12047,13 @@ CREATE TABLE dast_scanner_profiles ( | ||||||
|     CONSTRAINT check_568568fabf CHECK ((char_length(name) <= 255)) |     CONSTRAINT check_568568fabf CHECK ((char_length(name) <= 255)) | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | CREATE TABLE dast_scanner_profiles_builds ( | ||||||
|  |     dast_scanner_profile_id bigint NOT NULL, | ||||||
|  |     ci_build_id bigint NOT NULL | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | COMMENT ON TABLE dast_scanner_profiles_builds IS '{"owner":"group::dynamic analysis","description":"Join table between DAST Scanner Profiles and CI Builds"}'; | ||||||
|  | 
 | ||||||
| CREATE SEQUENCE dast_scanner_profiles_id_seq | CREATE SEQUENCE dast_scanner_profiles_id_seq | ||||||
|     START WITH 1 |     START WITH 1 | ||||||
|     INCREMENT BY 1 |     INCREMENT BY 1 | ||||||
|  | @ -12102,6 +12109,13 @@ CREATE TABLE dast_site_profiles ( | ||||||
|     CONSTRAINT check_f22f18002a CHECK ((char_length(auth_username) <= 255)) |     CONSTRAINT check_f22f18002a CHECK ((char_length(auth_username) <= 255)) | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | CREATE TABLE dast_site_profiles_builds ( | ||||||
|  |     dast_site_profile_id bigint NOT NULL, | ||||||
|  |     ci_build_id bigint NOT NULL | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | COMMENT ON TABLE dast_site_profiles_builds IS '{"owner":"group::dynamic analysis","description":"Join table between DAST Site Profiles and CI Builds"}'; | ||||||
|  | 
 | ||||||
| CREATE SEQUENCE dast_site_profiles_id_seq | CREATE SEQUENCE dast_site_profiles_id_seq | ||||||
|     START WITH 1 |     START WITH 1 | ||||||
|     INCREMENT BY 1 |     INCREMENT BY 1 | ||||||
|  | @ -21092,12 +21106,18 @@ ALTER TABLE ONLY dast_profiles_pipelines | ||||||
| ALTER TABLE ONLY dast_profiles | ALTER TABLE ONLY dast_profiles | ||||||
|     ADD CONSTRAINT dast_profiles_pkey PRIMARY KEY (id); |     ADD CONSTRAINT dast_profiles_pkey PRIMARY KEY (id); | ||||||
| 
 | 
 | ||||||
|  | ALTER TABLE ONLY dast_scanner_profiles_builds | ||||||
|  |     ADD CONSTRAINT dast_scanner_profiles_builds_pkey PRIMARY KEY (dast_scanner_profile_id, ci_build_id); | ||||||
|  | 
 | ||||||
| ALTER TABLE ONLY dast_scanner_profiles | ALTER TABLE ONLY dast_scanner_profiles | ||||||
|     ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id); |     ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id); | ||||||
| 
 | 
 | ||||||
| ALTER TABLE ONLY dast_site_profile_secret_variables | ALTER TABLE ONLY dast_site_profile_secret_variables | ||||||
|     ADD CONSTRAINT dast_site_profile_secret_variables_pkey PRIMARY KEY (id); |     ADD CONSTRAINT dast_site_profile_secret_variables_pkey PRIMARY KEY (id); | ||||||
| 
 | 
 | ||||||
|  | ALTER TABLE ONLY dast_site_profiles_builds | ||||||
|  |     ADD CONSTRAINT dast_site_profiles_builds_pkey PRIMARY KEY (dast_site_profile_id, ci_build_id); | ||||||
|  | 
 | ||||||
| ALTER TABLE ONLY dast_site_profiles_pipelines | ALTER TABLE ONLY dast_site_profiles_pipelines | ||||||
|     ADD CONSTRAINT dast_site_profiles_pipelines_pkey PRIMARY KEY (dast_site_profile_id, ci_pipeline_id); |     ADD CONSTRAINT dast_site_profiles_pipelines_pkey PRIMARY KEY (dast_site_profile_id, ci_pipeline_id); | ||||||
| 
 | 
 | ||||||
|  | @ -22343,6 +22363,10 @@ CREATE INDEX commit_id_and_note_id_index ON commit_user_mentions USING btree (co | ||||||
| 
 | 
 | ||||||
| CREATE INDEX composer_cache_files_index_on_deleted_at ON packages_composer_cache_files USING btree (delete_at, id); | CREATE INDEX composer_cache_files_index_on_deleted_at ON packages_composer_cache_files USING btree (delete_at, id); | ||||||
| 
 | 
 | ||||||
|  | CREATE UNIQUE INDEX dast_scanner_profiles_builds_on_ci_build_id ON dast_scanner_profiles_builds USING btree (ci_build_id); | ||||||
|  | 
 | ||||||
|  | CREATE UNIQUE INDEX dast_site_profiles_builds_on_ci_build_id ON dast_site_profiles_builds USING btree (ci_build_id); | ||||||
|  | 
 | ||||||
| CREATE UNIQUE INDEX design_management_designs_versions_uniqueness ON design_management_designs_versions USING btree (design_id, version_id); | CREATE UNIQUE INDEX design_management_designs_versions_uniqueness ON design_management_designs_versions USING btree (design_id, version_id); | ||||||
| 
 | 
 | ||||||
| CREATE INDEX design_user_mentions_on_design_id_and_note_id_index ON design_user_mentions USING btree (design_id, note_id); | CREATE INDEX design_user_mentions_on_design_id_and_note_id_index ON design_user_mentions USING btree (design_id, note_id); | ||||||
|  | @ -25705,6 +25729,9 @@ ALTER TABLE ONLY vulnerability_feedback | ||||||
| ALTER TABLE ONLY deploy_keys_projects | ALTER TABLE ONLY deploy_keys_projects | ||||||
|     ADD CONSTRAINT fk_58a901ca7e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; |     ADD CONSTRAINT fk_58a901ca7e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; | ||||||
| 
 | 
 | ||||||
|  | ALTER TABLE ONLY dast_scanner_profiles_builds | ||||||
|  |     ADD CONSTRAINT fk_5d46286ad3 FOREIGN KEY (dast_scanner_profile_id) REFERENCES dast_scanner_profiles(id) ON DELETE CASCADE; | ||||||
|  | 
 | ||||||
| ALTER TABLE ONLY issue_assignees | ALTER TABLE ONLY issue_assignees | ||||||
|     ADD CONSTRAINT fk_5e0c8d9154 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; |     ADD CONSTRAINT fk_5e0c8d9154 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; | ||||||
| 
 | 
 | ||||||
|  | @ -25864,6 +25891,9 @@ ALTER TABLE ONLY ci_pipeline_schedules | ||||||
| ALTER TABLE ONLY todos | ALTER TABLE ONLY todos | ||||||
|     ADD CONSTRAINT fk_91d1f47b13 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE; |     ADD CONSTRAINT fk_91d1f47b13 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE; | ||||||
| 
 | 
 | ||||||
|  | ALTER TABLE ONLY dast_site_profiles_builds | ||||||
|  |     ADD CONSTRAINT fk_94e80df60e FOREIGN KEY (dast_site_profile_id) REFERENCES dast_site_profiles(id) ON DELETE CASCADE; | ||||||
|  | 
 | ||||||
| ALTER TABLE ONLY vulnerability_feedback | ALTER TABLE ONLY vulnerability_feedback | ||||||
|     ADD CONSTRAINT fk_94f7c8a81e FOREIGN KEY (comment_author_id) REFERENCES users(id) ON DELETE SET NULL; |     ADD CONSTRAINT fk_94f7c8a81e FOREIGN KEY (comment_author_id) REFERENCES users(id) ON DELETE SET NULL; | ||||||
| 
 | 
 | ||||||
|  | @ -25927,6 +25957,9 @@ ALTER TABLE ONLY ci_builds | ||||||
| ALTER TABLE ONLY ci_pipelines | ALTER TABLE ONLY ci_pipelines | ||||||
|     ADD CONSTRAINT fk_a23be95014 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; |     ADD CONSTRAINT fk_a23be95014 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; | ||||||
| 
 | 
 | ||||||
|  | ALTER TABLE ONLY dast_site_profiles_builds | ||||||
|  |     ADD CONSTRAINT fk_a325505e99 FOREIGN KEY (ci_build_id) REFERENCES ci_builds(id) ON DELETE CASCADE; | ||||||
|  | 
 | ||||||
| ALTER TABLE ONLY bulk_import_entities | ALTER TABLE ONLY bulk_import_entities | ||||||
|     ADD CONSTRAINT fk_a44ff95be5 FOREIGN KEY (parent_id) REFERENCES bulk_import_entities(id) ON DELETE CASCADE; |     ADD CONSTRAINT fk_a44ff95be5 FOREIGN KEY (parent_id) REFERENCES bulk_import_entities(id) ON DELETE CASCADE; | ||||||
| 
 | 
 | ||||||
|  | @ -26137,6 +26170,9 @@ ALTER TABLE ONLY gitlab_subscriptions | ||||||
| ALTER TABLE ONLY ci_triggers | ALTER TABLE ONLY ci_triggers | ||||||
|     ADD CONSTRAINT fk_e3e63f966e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; |     ADD CONSTRAINT fk_e3e63f966e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; | ||||||
| 
 | 
 | ||||||
|  | ALTER TABLE ONLY dast_scanner_profiles_builds | ||||||
|  |     ADD CONSTRAINT fk_e4c49200f8 FOREIGN KEY (ci_build_id) REFERENCES ci_builds(id) ON DELETE CASCADE; | ||||||
|  | 
 | ||||||
| ALTER TABLE ONLY merge_requests | ALTER TABLE ONLY merge_requests | ||||||
|     ADD CONSTRAINT fk_e719a85f8a FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL; |     ADD CONSTRAINT fk_e719a85f8a FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ GET /groups/:id/epics?state=opened | ||||||
| | `author_id`         | integer          | no         | Return epics created by the given user `id`                                                                                 | | | `author_id`         | integer          | no         | Return epics created by the given user `id`                                                                                 | | ||||||
| | `labels`            | string           | no         | Return epics matching a comma separated list of labels names. Label names from the epic group or a parent group can be used | | | `labels`            | string           | no         | Return epics matching a comma separated list of labels names. Label names from the epic group or a parent group can be used | | ||||||
| | `with_labels_details` | boolean        | no         | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. Available in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413) and later | | | `with_labels_details` | boolean        | no         | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. Available in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413) and later | | ||||||
| | `order_by`          | string           | no         | Return epics ordered by `created_at` or `updated_at` fields. Default is `created_at`                                        | | | `order_by`          | string           | no         | Return epics ordered by `created_at`, `updated_at`, or `title` fields. Default is `created_at`                              | | ||||||
| | `sort`              | string           | no         | Return epics sorted in `asc` or `desc` order. Default is `desc`                                                             | | | `sort`              | string           | no         | Return epics sorted in `asc` or `desc` order. Default is `desc`                                                             | | ||||||
| | `search`            | string           | no         | Search epics against their `title` and `description`                                                                        | | | `search`            | string           | no         | Search epics against their `title` and `description`                                                                        | | ||||||
| | `state`             | string           | no         | Search epics against their `state`, possible filters: `opened`, `closed` and `all`, default: `all`                          | | | `state`             | string           | no         | Search epics against their `state`, possible filters: `opened`, `closed` and `all`, default: `all`                          | | ||||||
|  |  | ||||||
|  | @ -14234,6 +14234,8 @@ Roadmap sort values. | ||||||
| | <a id="epicsortend_date_desc"></a>`END_DATE_DESC` | Sort by end date in descending order. | | | <a id="epicsortend_date_desc"></a>`END_DATE_DESC` | Sort by end date in descending order. | | ||||||
| | <a id="epicsortstart_date_asc"></a>`START_DATE_ASC` | Sort by start date in ascending order. | | | <a id="epicsortstart_date_asc"></a>`START_DATE_ASC` | Sort by start date in ascending order. | | ||||||
| | <a id="epicsortstart_date_desc"></a>`START_DATE_DESC` | Sort by start date in descending order. | | | <a id="epicsortstart_date_desc"></a>`START_DATE_DESC` | Sort by start date in descending order. | | ||||||
|  | | <a id="epicsorttitle_asc"></a>`TITLE_ASC` | Sort by title in ascending order. | | ||||||
|  | | <a id="epicsorttitle_desc"></a>`TITLE_DESC` | Sort by title in descending order. | | ||||||
| | <a id="epicsortend_date_asc"></a>`end_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_ASC. | | | <a id="epicsortend_date_asc"></a>`end_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_ASC. | | ||||||
| | <a id="epicsortend_date_desc"></a>`end_date_desc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_DESC. | | | <a id="epicsortend_date_desc"></a>`end_date_desc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_DESC. | | ||||||
| | <a id="epicsortstart_date_asc"></a>`start_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use START_DATE_ASC. | | | <a id="epicsortstart_date_asc"></a>`start_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use START_DATE_ASC. | | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 5.2 KiB | 
|  | @ -6,17 +6,17 @@ type: reference, index | ||||||
| last_update: 2019-07-03 | last_update: 2019-07-03 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| # Pipelines for Merge Requests | # Pipelines for merge requests | ||||||
| 
 | 
 | ||||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/15310) in GitLab 11.6. | > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/15310) in GitLab 11.6. | ||||||
| 
 | 
 | ||||||
| In a [basic configuration](../pipelines/pipeline_architectures.md#basic-pipelines), GitLab runs a pipeline each time | In a [basic configuration](../pipelines/pipeline_architectures.md#basic-pipelines), GitLab runs a pipeline each time | ||||||
| changes are pushed to a branch. | changes are pushed to a branch. | ||||||
| 
 | 
 | ||||||
| If you want the pipeline to run jobs **only** on commits to a branch that is associated with a merge request, | If you want the pipeline to run jobs **only** on commits associated with a merge request, | ||||||
| you can use *pipelines for merge requests*. | you can use *pipelines for merge requests*. | ||||||
| 
 | 
 | ||||||
| In the UI, these pipelines are labeled as `detached`. Otherwise, these pipelines appear the same | In the UI, these pipelines are labeled as `detached`. Otherwise, these pipelines are the same | ||||||
| as other pipelines. | as other pipelines. | ||||||
| 
 | 
 | ||||||
| Pipelines for merge requests can run when you: | Pipelines for merge requests can run when you: | ||||||
|  | @ -25,13 +25,8 @@ Pipelines for merge requests can run when you: | ||||||
| - Commit changes to the source branch for the merge request. | - Commit changes to the source branch for the merge request. | ||||||
| - Select the **Run pipeline** button from the **Pipelines** tab in the merge request. | - Select the **Run pipeline** button from the **Pipelines** tab in the merge request. | ||||||
| 
 | 
 | ||||||
| Any user who has developer [permissions](../../user/permissions.md) |  | ||||||
| can run a pipeline for merge requests. |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
| 
 |  | ||||||
| If you use this feature with [merge when pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md), | If you use this feature with [merge when pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md), | ||||||
| pipelines for merge requests take precedence over the other regular pipelines. | pipelines for merge requests take precedence over other pipelines. | ||||||
| 
 | 
 | ||||||
| ## Prerequisites | ## Prerequisites | ||||||
| 
 | 
 | ||||||
|  | @ -39,29 +34,24 @@ To enable pipelines for merge requests: | ||||||
| 
 | 
 | ||||||
| - Your repository must be a GitLab repository, not an | - Your repository must be a GitLab repository, not an | ||||||
|   [external repository](../ci_cd_for_external_repos/index.md). |   [external repository](../ci_cd_for_external_repos/index.md). | ||||||
| - [In GitLab 11.10 and later](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25504), | - You must have the Developer [role](../../user/permissions.md) | ||||||
|   you must be using GitLab Runner 11.9. |   to run a pipeline for merge requests. | ||||||
| 
 | 
 | ||||||
| ## Configuring pipelines for merge requests | ## Configure pipelines for merge requests | ||||||
| 
 | 
 | ||||||
| To configure pipelines for merge requests you need to configure your [CI/CD configuration file](../yaml/README.md). | To configure pipelines for merge requests, you must configure your [CI/CD configuration file](../yaml/README.md). | ||||||
| There are a few different ways to do this: | To do this, you can use [`rules`](#use-rules-to-run-pipelines-for-merge-requests) or [`only/except`](#use-only-or-except-to-run-pipelines-for-merge-requests). | ||||||
| 
 | 
 | ||||||
| ### Use `rules` to run pipelines for merge requests | ### Use `rules` to run pipelines for merge requests | ||||||
| 
 | 
 | ||||||
| When using `rules`, which is the preferred method, we recommend starting with one | GitLab recommends that you use the `rules` keyword, which is available in | ||||||
| of the [`workflow:rules` templates](../yaml/README.md#workflowrules-templates) to ensure | [`workflow:rules` templates](../yaml/README.md#workflowrules-templates). | ||||||
| your basic configuration is correct. Instructions on how to do this, as well as how |  | ||||||
| to customize, are available at that link. |  | ||||||
| 
 | 
 | ||||||
| ### Use `only` or `except` to run pipelines for merge requests | ### Use `only` or `except` to run pipelines for merge requests | ||||||
| 
 | 
 | ||||||
| If you want to continue using `only/except`, this is possible but please review the drawbacks | You can use the `only/except` keywords. However, with this method, you must specify `only: - merge_requests` for each job. | ||||||
| below. |  | ||||||
| 
 |  | ||||||
| When you use this method, you have to specify `only: - merge_requests` for each job. In this |  | ||||||
| example, the pipeline contains a `test` job that is configured to run on merge requests. |  | ||||||
| 
 | 
 | ||||||
|  | In the following example, the pipeline contains a `test` job that is configured to run on merge requests. | ||||||
| The `build` and `deploy` jobs don't have the `only: - merge_requests` keyword, | The `build` and `deploy` jobs don't have the `only: - merge_requests` keyword, | ||||||
| so they don't run on merge requests. | so they don't run on merge requests. | ||||||
| 
 | 
 | ||||||
|  | @ -85,20 +75,18 @@ deploy: | ||||||
|     - main |     - main | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| #### Excluding certain jobs | #### Exclude specific jobs | ||||||
| 
 | 
 | ||||||
| The behavior of the `only: [merge_requests]` keyword is such that _only_ jobs with | When you use `only: [merge_requests]`, only jobs with | ||||||
| that keyword are run in the context of a merge request; no other jobs run. | that keyword are run in the context of a merge request. No other jobs run. | ||||||
| 
 | 
 | ||||||
| However, you can invert this behavior and have all of your jobs run _except_ | However, you can invert this behavior and have all of your jobs run except | ||||||
| for one or two. | for one or two. For example, you might have a pipeline with jobs `A`, `B`, and `C`, and you want: | ||||||
| 
 |  | ||||||
| Consider the following pipeline, with jobs `A`, `B`, and `C`. Imagine you want: |  | ||||||
| 
 | 
 | ||||||
| - All pipelines to always run `A` and `B`. | - All pipelines to always run `A` and `B`. | ||||||
| - `C` to run only for merge requests. | - `C` to run only for merge requests. | ||||||
| 
 | 
 | ||||||
| To achieve this, you can configure your `.gitlab-ci.yml` file as follows: | To achieve this outcome, configure your `.gitlab-ci.yml` file as follows: | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| .only-default: &only-default | .only-default: &only-default | ||||||
|  | @ -124,23 +112,20 @@ C: | ||||||
|     - merge_requests |     - merge_requests | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Therefore: | - `A` and `B` always run, because they get the `only:` rule to execute in all cases. | ||||||
| 
 | - `C` only runs for merge requests. It doesn't run for any pipeline | ||||||
| - Since `A` and `B` are getting the `only:` rule to execute in all cases, they always run. |  | ||||||
| - Since `C` specifies that it should only run for merge requests, it doesn't run for any pipeline |  | ||||||
|   except a merge request pipeline. |   except a merge request pipeline. | ||||||
| 
 | 
 | ||||||
| This helps you avoid having to add the `only:` rule to all of your jobs to make | In this example, you don't have to add the `only:` rule to all of your jobs to make | ||||||
| them always run. You can use this format to set up a Review App, helping to | them always run. You can use this format to set up a Review App, which helps to | ||||||
| save resources. | save resources. | ||||||
| 
 | 
 | ||||||
| #### Excluding certain branches | #### Exclude specific branches | ||||||
| 
 | 
 | ||||||
| Pipelines for merge requests require special treatment when | Branch refs use this format: `refs/heads/my-feature-branch`. | ||||||
| using [`only`/`except`](../yaml/README.md#only--except). Unlike ordinary | Merge request refs use this format: `refs/merge-requests/:iid/head`. | ||||||
| branch refs (for example `refs/heads/my-feature-branch`), merge request refs | 
 | ||||||
| use a special Git reference that looks like `refs/merge-requests/:iid/head`. Because | Because of this difference, the following configuration does not work as expected: | ||||||
| of this, the following configuration will **not** work as expected: |  | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| # Does not exclude a branch named "docs-my-fix"! | # Does not exclude a branch named "docs-my-fix"! | ||||||
|  | @ -149,7 +134,7 @@ test: | ||||||
|   except: [/^docs-/] |   except: [/^docs-/] | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Instead, you can use the | Instead, use the | ||||||
| [`$CI_COMMIT_REF_NAME` predefined environment | [`$CI_COMMIT_REF_NAME` predefined environment | ||||||
| variable](../variables/predefined_variables.md) in | variable](../variables/predefined_variables.md) in | ||||||
| combination with | combination with | ||||||
|  | @ -164,55 +149,43 @@ test: | ||||||
|       - $CI_COMMIT_REF_NAME =~ /^docs-/ |       - $CI_COMMIT_REF_NAME =~ /^docs-/ | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Pipelines for Merged Results **(PREMIUM)** |  | ||||||
| 
 |  | ||||||
| Read the [documentation on Pipelines for Merged Results](pipelines_for_merged_results/index.md). |  | ||||||
| 
 |  | ||||||
| ### Merge Trains **(PREMIUM)** |  | ||||||
| 
 |  | ||||||
| Read the [documentation on Merge Trains](pipelines_for_merged_results/merge_trains/index.md). |  | ||||||
| 
 |  | ||||||
| ## Run pipelines in the parent project for merge requests from a forked project **(PREMIUM)** | ## Run pipelines in the parent project for merge requests from a forked project **(PREMIUM)** | ||||||
| 
 | 
 | ||||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217451) in GitLab 13.3. | > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217451) in GitLab 13.3. | ||||||
| > - [Moved](https://about.gitlab.com/blog/2021/01/26/new-gitlab-product-subscription-model/) to GitLab Premium in 13.9. | > - [Moved](https://about.gitlab.com/blog/2021/01/26/new-gitlab-product-subscription-model/) to GitLab Premium in 13.9. | ||||||
| 
 | 
 | ||||||
| By default, external contributors working from forks can't create pipelines in the | By default, external contributors who work in forks can't create pipelines in the | ||||||
| parent project. When a pipeline for merge requests is triggered by a merge request | parent project. When a merge request that comes from a fork triggers a pipeline: | ||||||
| coming from a fork: |  | ||||||
| 
 | 
 | ||||||
| - It's created and runs in the fork (source) project, not the parent (target) project. | - The pipeline is created and runs in the fork (source) project, not the parent (target) project. | ||||||
| - It uses the fork project's CI/CD configuration and resources. | - The pipeline uses the fork project's CI/CD configuration and resources. | ||||||
| 
 | 
 | ||||||
| If a pipeline runs in a fork, the **fork** icon appears for the pipeline in the merge request. | If a pipeline runs in a fork, a **fork** badge appears for the pipeline in the merge request. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| Sometimes parent project members want the pipeline to run in the parent | Sometimes parent project members want the pipeline to run in the parent | ||||||
| project. This could be to ensure that the post-merge pipeline passes in the parent project. | project. They may want to ensure that the post-merge pipeline passes in the parent project. | ||||||
| For example, a fork project could try to use a corrupted runner that doesn't execute | For example, a fork project could try to use a corrupted runner that doesn't execute | ||||||
| test scripts properly, but reports a passed pipeline. Reviewers in the parent project | test scripts properly, but reports a passed pipeline. Reviewers in the parent project | ||||||
| could mistakenly trust the merge request because it passed a faked pipeline. | could mistakenly trust the merge request because it passed a faked pipeline. | ||||||
| 
 | 
 | ||||||
| Parent project members with at least [Developer permissions](../../user/permissions.md) | Parent project members with at least the [Developer role](../../user/permissions.md) | ||||||
| can create pipelines in the parent project for merge requests | can create pipelines in the parent project for merge requests | ||||||
| from a forked project. In the merge request, go to the **Pipelines** and click | from a forked project. In the merge request, go to the **Pipelines** tab and select | ||||||
| **Run pipeline** button. | **Run pipeline**. | ||||||
| 
 | 
 | ||||||
| WARNING: | WARNING: | ||||||
| Fork merge requests could contain malicious code that tries to steal secrets in the | Fork merge requests can contain malicious code that tries to steal secrets in the | ||||||
| parent project when the pipeline runs, even before merge. Reviewers must carefully | parent project when the pipeline runs, even before merge. As a reviewer, you must carefully | ||||||
| check the changes in the merge request before triggering the pipeline. GitLab shows | check the changes in the merge request before triggering the pipeline. GitLab shows | ||||||
| a warning that must be accepted before the pipeline can be triggered. | a warning that you must accept before you can trigger the pipeline. | ||||||
| 
 | 
 | ||||||
| ## Additional predefined variables | ## Predefined variables available for pipelines for merge requests | ||||||
| 
 | 
 | ||||||
| By using pipelines for merge requests, GitLab exposes additional predefined variables to the pipeline jobs. | When you use pipelines for merge requests, [additional predefined variables](../variables/predefined_variables.md#predefined-variables-for-merge-request-pipelines) are available to the CI/CD jobs. | ||||||
| Those variables contain information of the associated merge request, so that it's useful | These variables contain information from the associated merge request, so that you can | ||||||
| to integrate your job with [GitLab Merge Request API](../../api/merge_requests.md). | integrate your job with the [GitLab Merge Request API](../../api/merge_requests.md). | ||||||
| 
 |  | ||||||
| You can find the list of available variables in [the reference sheet](../variables/predefined_variables.md). |  | ||||||
| The variable names begin with the `CI_MERGE_REQUEST_` prefix. |  | ||||||
| 
 | 
 | ||||||
| ## Troubleshooting | ## Troubleshooting | ||||||
| 
 | 
 | ||||||
|  | @ -226,8 +199,8 @@ If you are seeing two pipelines when using `only/except`, please see the caveats | ||||||
| related to using `only/except` above (or, consider moving to `rules`). | related to using `only/except` above (or, consider moving to `rules`). | ||||||
| 
 | 
 | ||||||
| In [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/201845) and later, | In [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/201845) and later, | ||||||
| you can add `workflow:rules` to [switch from branch pipelines to merge request pipelines](../yaml/README.md#switch-between-branch-pipelines-and-merge-request-pipelines) | you can add `workflow:rules` to [switch from branch pipelines to merge request pipelines](../yaml/README.md#switch-between-branch-pipelines-and-merge-request-pipelines). | ||||||
| after a merge request is open on the branch. | The pipeline switches to merge request pipelines this after a merge request is open on the branch. | ||||||
| 
 | 
 | ||||||
| ### Two pipelines created when pushing an invalid CI configuration file | ### Two pipelines created when pushing an invalid CI configuration file | ||||||
| 
 | 
 | ||||||
|  | @ -235,3 +208,8 @@ Pushing to a branch with an invalid CI configuration file can trigger | ||||||
| the creation of two types of failed pipelines. One pipeline is a failed merge request | the creation of two types of failed pipelines. One pipeline is a failed merge request | ||||||
| pipeline, and the other is a failed branch pipeline, but both are caused by the same | pipeline, and the other is a failed branch pipeline, but both are caused by the same | ||||||
| invalid configuration. | invalid configuration. | ||||||
|  | 
 | ||||||
|  | ## Related topics | ||||||
|  | 
 | ||||||
|  | - [Pipelines for merged results](pipelines_for_merged_results/index.md). | ||||||
|  | - [Merge trains](pipelines_for_merged_results/merge_trains/index.md). | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ type: reference | ||||||
| last_update: 2019-07-03 | last_update: 2019-07-03 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| # Pipelines for Merged Results **(PREMIUM)** | # Pipelines for merged results **(PREMIUM)** | ||||||
| 
 | 
 | ||||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10. | > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10. | ||||||
| 
 | 
 | ||||||
|  | @ -57,7 +57,7 @@ To enable pipelines for merge results: | ||||||
| 
 | 
 | ||||||
| To enable pipelines for merged results for your project: | To enable pipelines for merged results for your project: | ||||||
| 
 | 
 | ||||||
| 1. [Configure your CI/CD configuration file](../index.md#configuring-pipelines-for-merge-requests) | 1. [Configure your CI/CD configuration file](../index.md#configure-pipelines-for-merge-requests) | ||||||
|    so that the pipeline or individual jobs run for merge requests. |    so that the pipeline or individual jobs run for merge requests. | ||||||
| 1. Visit your project's **Settings > General** and expand **Merge requests**. | 1. Visit your project's **Settings > General** and expand **Merge requests**. | ||||||
| 1. Check **Enable merged results pipelines**. | 1. Check **Enable merged results pipelines**. | ||||||
|  |  | ||||||
|  | @ -81,7 +81,7 @@ To enable merge trains: | ||||||
| To enable merge trains for your project: | To enable merge trains for your project: | ||||||
| 
 | 
 | ||||||
| 1. If you are on a self-managed GitLab instance, ensure the [feature flag](#merge-trains-feature-flag) is set correctly. | 1. If you are on a self-managed GitLab instance, ensure the [feature flag](#merge-trains-feature-flag) is set correctly. | ||||||
| 1. [Configure your CI/CD configuration file](../../index.md#configuring-pipelines-for-merge-requests) | 1. [Configure your CI/CD configuration file](../../index.md#configure-pipelines-for-merge-requests) | ||||||
|    so that the pipeline or individual jobs run for merge requests. |    so that the pipeline or individual jobs run for merge requests. | ||||||
| 1. Visit your project's **Settings > General** and expand **Merge requests**. | 1. Visit your project's **Settings > General** and expand **Merge requests**. | ||||||
| 1. In the **Merge method** section, verify that **Merge commit** is selected. | 1. In the **Merge method** section, verify that **Merge commit** is selected. | ||||||
|  |  | ||||||
|  | @ -326,7 +326,7 @@ makes your pipelines run for branches and tags. | ||||||
| Branch pipeline status is displayed in merge requests that use the branch | Branch pipeline status is displayed in merge requests that use the branch | ||||||
| as a source. However, this pipeline type does not support any features offered by | as a source. However, this pipeline type does not support any features offered by | ||||||
| [merge request pipelines](../merge_request_pipelines/), like | [merge request pipelines](../merge_request_pipelines/), like | ||||||
| [pipelines for merge results](../merge_request_pipelines/#pipelines-for-merged-results) | [pipelines for merged results](../merge_request_pipelines/pipelines_for_merged_results/index.md) | ||||||
| or [merge trains](../merge_request_pipelines/pipelines_for_merged_results/merge_trains/). | or [merge trains](../merge_request_pipelines/pipelines_for_merged_results/merge_trains/). | ||||||
| This template intentionally avoids those features. | This template intentionally avoids those features. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,50 @@ | ||||||
|  | --- | ||||||
|  | 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 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | # Storybook | ||||||
|  | 
 | ||||||
|  | The Storybook for the `gitlab-org/gitlab` project is available on our [GitLab Pages site](https://gitlab-org.gitlab.io/gitlab/storybook). | ||||||
|  | 
 | ||||||
|  | ## Storybook in local development | ||||||
|  | 
 | ||||||
|  | Storybook dependencies and configuration are located under the `storybook/` directory. | ||||||
|  | 
 | ||||||
|  | To build and launch Storybook locally, in the root directory of the `gitlab` project: | ||||||
|  | 
 | ||||||
|  | 1. Install Storybook dependencies: | ||||||
|  | 
 | ||||||
|  |     ```shell | ||||||
|  |     yarn storybook:install | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | 1. Build the Storybook site: | ||||||
|  | 
 | ||||||
|  |     ```shell | ||||||
|  |     yarn storybook:start | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | ## Adding components to Storybook | ||||||
|  | 
 | ||||||
|  | Stories can be added for any Vue component in the `gitlab` repository. | ||||||
|  | 
 | ||||||
|  | To add a story: | ||||||
|  | 
 | ||||||
|  | 1. Create a new `.stories.js` file in the same directory as the Vue component. | ||||||
|  |    The file name should have the same prefix as the Vue component. | ||||||
|  | 
 | ||||||
|  |     ```txt | ||||||
|  |     vue_shared/ | ||||||
|  |     ├─ components/ | ||||||
|  |     │  ├─ todo_button.vue | ||||||
|  |     │  ├─ todo_button.stories.js | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | 1. Write the story as per the [official Storybook instructions](https://storybook.js.org/docs/vue/writing-stories/introduction) | ||||||
|  |     | ||||||
|  |    Notes: | ||||||
|  |    - Specify the `title` field of the story as the component's file path from the `javascripts/` directory, | ||||||
|  |      e.g. if the component is located at `app/assets/javascripts/vue_shared/components/todo_button.vue`, specify the `title` as | ||||||
|  |      `vue_shared/components/To-do Button`. This will ensure the Storybook navigation maps closely to our internal directory structure. | ||||||
|  | @ -560,7 +560,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing. | ||||||
|    - `.yarn-cache` |    - `.yarn-cache` | ||||||
|    - `.assets-compile-cache` (the key includes `${NODE_ENV}` so it's actually two different caches). |    - `.assets-compile-cache` (the key includes `${NODE_ENV}` so it's actually two different caches). | ||||||
| 1. These cache definitions are composed of [multiple atomic caches](../ci/caching/index.md#use-multiple-caches). | 1. These cache definitions are composed of [multiple atomic caches](../ci/caching/index.md#use-multiple-caches). | ||||||
| 1. Only 6 specific jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches: | 1. Only the following jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches: | ||||||
|    - `update-setup-test-env-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml). |    - `update-setup-test-env-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml). | ||||||
|    - `update-gitaly-binaries-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml). |    - `update-gitaly-binaries-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml). | ||||||
|    - `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml). |    - `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml). | ||||||
|  | @ -568,6 +568,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing. | ||||||
|    - `update-assets-compile-production-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). |    - `update-assets-compile-production-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). | ||||||
|    - `update-assets-compile-test-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). |    - `update-assets-compile-test-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). | ||||||
|    - `update-yarn-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). |    - `update-yarn-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). | ||||||
|  |    - `update-storybook-yarn-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml). | ||||||
| 1. These jobs can also be forced to run in merge requests whose title include `UPDATE CACHE` (this can be useful to warm the caches in a MR that updates the cache keys). | 1. These jobs can also be forced to run in merge requests whose title include `UPDATE CACHE` (this can be useful to warm the caches in a MR that updates the cache keys). | ||||||
| 
 | 
 | ||||||
| ### Artifacts strategy | ### Artifacts strategy | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ This template requires: | ||||||
| - A project built in Rails that uses RSpec for testing. | - A project built in Rails that uses RSpec for testing. | ||||||
| - CI/CD configured to: | - CI/CD configured to: | ||||||
|   - Use a Docker image with Ruby available. |   - Use a Docker image with Ruby available. | ||||||
|   - Use [Pipelines for merge requests](../../../ci/merge_request_pipelines/index.md#configuring-pipelines-for-merge-requests) |   - Use [Pipelines for merge requests](../../../ci/merge_request_pipelines/index.md#configure-pipelines-for-merge-requests) | ||||||
| - [Pipelines for Merged Results](../../../ci/merge_request_pipelines/pipelines_for_merged_results/index.md#enable-pipelines-for-merged-results) | - [Pipelines for Merged Results](../../../ci/merge_request_pipelines/pipelines_for_merged_results/index.md#enable-pipelines-for-merged-results) | ||||||
|   enabled in the project settings. |   enabled in the project settings. | ||||||
| - A Docker image with Ruby available. The template uses `image: ruby:2.6` by default, but you [can override](../../../ci/yaml/includes.md#overriding-external-template-values) this. | - A Docker image with Ruby available. The template uses `image: ruby:2.6` by default, but you [can override](../../../ci/yaml/includes.md#overriding-external-template-values) this. | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ module API | ||||||
|     class User < UserBasic |     class User < UserBasic | ||||||
|       include UsersHelper |       include UsersHelper | ||||||
|       expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } |       expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } | ||||||
|       expose :bio, :bio_html, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title |       expose :bio, :bio_html, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title, :pronouns | ||||||
|       expose :bot?, as: :bot |       expose :bot?, as: :bot | ||||||
|       expose :work_information do |user| |       expose :work_information do |user| | ||||||
|         work_information(user) |         work_information(user) | ||||||
|  |  | ||||||
|  | @ -136,6 +136,7 @@ module Gitlab | ||||||
| 
 | 
 | ||||||
|   def self.process_name |   def self.process_name | ||||||
|     return 'sidekiq' if Gitlab::Runtime.sidekiq? |     return 'sidekiq' if Gitlab::Runtime.sidekiq? | ||||||
|  |     return 'action_cable' if Gitlab::Runtime.action_cable? | ||||||
|     return 'console' if Gitlab::Runtime.console? |     return 'console' if Gitlab::Runtime.console? | ||||||
|     return 'test' if Rails.env.test? |     return 'test' if Rails.env.test? | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| # we alter GlobalID so it will correctly find the record with its new model name. | # we alter GlobalID so it will correctly find the record with its new model name. | ||||||
| module Gitlab | module Gitlab | ||||||
|   module Patch |   module Patch | ||||||
|     module GlobalID |     module GlobalId | ||||||
|       def initialize(gid, options = {}) |       def initialize(gid, options = {}) | ||||||
|         super |         super | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35577,6 +35577,9 @@ msgstr "" | ||||||
| msgid "User was successfully updated." | msgid "User was successfully updated." | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  | msgid "UserAvailability|%{author} %{spanStart}(Busy)%{spanEnd}" | ||||||
|  | msgstr "" | ||||||
|  | 
 | ||||||
| msgid "UserAvailability|%{author} (Busy)" | msgid "UserAvailability|%{author} (Busy)" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,6 +40,9 @@ | ||||||
|     "markdownlint:no-trailing-spaces": "markdownlint --config doc/.markdownlint/markdownlint-no-trailing-spaces.yml", |     "markdownlint:no-trailing-spaces": "markdownlint --config doc/.markdownlint/markdownlint-no-trailing-spaces.yml", | ||||||
|     "markdownlint:no-trailing-spaces:fix": "yarn run markdownlint:no-trailing-spaces --fix", |     "markdownlint:no-trailing-spaces:fix": "yarn run markdownlint:no-trailing-spaces --fix", | ||||||
|     "postinstall": "node ./scripts/frontend/postinstall.js", |     "postinstall": "node ./scripts/frontend/postinstall.js", | ||||||
|  |     "storybook:install": "yarn --cwd ./storybook install", | ||||||
|  |     "storybook:build": "yarn --cwd ./storybook build", | ||||||
|  |     "storybook:start": "yarn --cwd ./storybook start", | ||||||
|     "stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js", |     "stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js", | ||||||
|     "webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js", |     "webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js", | ||||||
|     "webpack-vendor": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.vendor.config.js", |     "webpack-vendor": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.vendor.config.js", | ||||||
|  |  | ||||||
|  | @ -44,10 +44,10 @@ RSpec.describe Projects::ServicesController do | ||||||
|         let(:project) { create(:project) } |         let(:project) { create(:project) } | ||||||
| 
 | 
 | ||||||
|         context 'with chat notification service' do |         context 'with chat notification service' do | ||||||
|           let(:service) { project.create_microsoft_teams_service(webhook: 'http://webhook.com') } |           let(:service) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') } | ||||||
| 
 | 
 | ||||||
|           it 'returns success' do |           it 'returns success' do | ||||||
|             allow_any_instance_of(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true) |             allow_next(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true) | ||||||
| 
 | 
 | ||||||
|             put :test, params: project_params |             put :test, params: project_params | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,9 +5,9 @@ require 'spec_helper' | ||||||
| RSpec.describe 'User sees user popover', :js do | RSpec.describe 'User sees user popover', :js do | ||||||
|   include Spec::Support::Helpers::Features::NotesHelpers |   include Spec::Support::Helpers::Features::NotesHelpers | ||||||
| 
 | 
 | ||||||
|   let_it_be(:project) { create(:project, :repository) } |   let_it_be(:user) { create(:user, pronouns: 'they/them') } | ||||||
|  |   let_it_be(:project) { create(:project, :repository, creator: user) } | ||||||
| 
 | 
 | ||||||
|   let(:user) { project.creator } |  | ||||||
|   let(:merge_request) do |   let(:merge_request) do | ||||||
|     create(:merge_request, source_project: project, target_project: project) |     create(:merge_request, source_project: project, target_project: project) | ||||||
|   end |   end | ||||||
|  | @ -32,7 +32,7 @@ RSpec.describe 'User sees user popover', :js do | ||||||
|       expect(page).to have_css(popover_selector, visible: true) |       expect(page).to have_css(popover_selector, visible: true) | ||||||
| 
 | 
 | ||||||
|       page.within(popover_selector) do |       page.within(popover_selector) do | ||||||
|         expect(page).to have_content(user.name) |         expect(page).to have_content("#{user.name} (they/them)") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -67,7 +67,14 @@ describe('Global Search Store Actions', () => { | ||||||
|       it('calls Api.groupProjects', () => { |       it('calls Api.groupProjects', () => { | ||||||
|         actions.fetchProjects({ commit: mockCommit, state }); |         actions.fetchProjects({ commit: mockCommit, state }); | ||||||
| 
 | 
 | ||||||
|         expect(Api.groupProjects).toHaveBeenCalled(); |         expect(Api.groupProjects).toHaveBeenCalledWith( | ||||||
|  |           state.query.group_id, | ||||||
|  |           state.query.search, | ||||||
|  |           { | ||||||
|  |             order_by: 'similarity', | ||||||
|  |           }, | ||||||
|  |           expect.any(Function), | ||||||
|  |         ); | ||||||
|         expect(Api.projects).not.toHaveBeenCalled(); |         expect(Api.projects).not.toHaveBeenCalled(); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| import { shallowMount } from '@vue/test-utils'; | import { mount } from '@vue/test-utils'; | ||||||
| import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue'; | import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue'; | ||||||
| import CollapsedAssignee from '~/sidebar/components/assignees/collapsed_assignee.vue'; | import CollapsedAssignee from '~/sidebar/components/assignees/collapsed_assignee.vue'; | ||||||
| import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; |  | ||||||
| import userDataMock from '../../user_data_mock'; | import userDataMock from '../../user_data_mock'; | ||||||
| 
 | 
 | ||||||
| const TEST_USER = userDataMock(); | const TEST_USER = userDataMock(); | ||||||
|  | @ -17,11 +16,8 @@ describe('CollapsedAssignee assignee component', () => { | ||||||
|       ...props, |       ...props, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     wrapper = shallowMount(CollapsedAssignee, { |     wrapper = mount(CollapsedAssignee, { | ||||||
|       propsData, |       propsData, | ||||||
|       stubs: { |  | ||||||
|         UserNameWithStatus, |  | ||||||
|       }, |  | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,25 +1,21 @@ | ||||||
| import { GlSprintf } from '@gitlab/ui'; | import { mount } from '@vue/test-utils'; | ||||||
| import { shallowMount } from '@vue/test-utils'; |  | ||||||
| import { AVAILABILITY_STATUS } from '~/set_status_modal/utils'; | import { AVAILABILITY_STATUS } from '~/set_status_modal/utils'; | ||||||
| import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; | import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; | ||||||
| 
 | 
 | ||||||
| const name = 'Goku'; | const name = 'Administrator'; | ||||||
| const containerClasses = 'gl-cool-class gl-over-9000'; | const containerClasses = 'gl-cool-class gl-over-9000'; | ||||||
| 
 | 
 | ||||||
| describe('UserNameWithStatus', () => { | describe('UserNameWithStatus', () => { | ||||||
|   let wrapper; |   let wrapper; | ||||||
| 
 | 
 | ||||||
|   function createComponent(props = {}) { |   function createComponent(props = {}) { | ||||||
|     return shallowMount(UserNameWithStatus, { |     wrapper = mount(UserNameWithStatus, { | ||||||
|       propsData: { name, containerClasses, ...props }, |       propsData: { name, containerClasses, ...props }, | ||||||
|       stubs: { |  | ||||||
|         GlSprintf, |  | ||||||
|       }, |  | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   beforeEach(() => { |   beforeEach(() => { | ||||||
|     wrapper = createComponent(); |     createComponent(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   afterEach(() => { |   afterEach(() => { | ||||||
|  | @ -41,11 +37,39 @@ describe('UserNameWithStatus', () => { | ||||||
| 
 | 
 | ||||||
|   describe(`with availability="${AVAILABILITY_STATUS.BUSY}"`, () => { |   describe(`with availability="${AVAILABILITY_STATUS.BUSY}"`, () => { | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       wrapper = createComponent({ availability: AVAILABILITY_STATUS.BUSY }); |       createComponent({ availability: AVAILABILITY_STATUS.BUSY }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('will render "Busy"', () => { |     it('will render "Busy"', () => { | ||||||
|       expect(wrapper.html()).toContain('Goku (Busy)'); |       expect(wrapper.text()).toContain('(Busy)'); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('when user has pronouns set', () => { | ||||||
|  |     const pronouns = 'they/them'; | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { | ||||||
|  |       createComponent({ pronouns }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it("renders user's name with pronouns", () => { | ||||||
|  |       expect(wrapper.text()).toMatchInterpolatedText(`${name} (${pronouns})`); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('when user does not have pronouns set', () => { | ||||||
|  |     describe.each` | ||||||
|  |       pronouns | ||||||
|  |       ${undefined} | ||||||
|  |       ${null} | ||||||
|  |       ${''} | ||||||
|  |       ${'   '} | ||||||
|  |     `('when `pronouns` prop is $pronouns', ({ pronouns }) => {
 | ||||||
|  |       it("renders only the user's name", () => { | ||||||
|  |         createComponent({ pronouns }); | ||||||
|  | 
 | ||||||
|  |         expect(wrapper.text()).toMatchInterpolatedText(name); | ||||||
|  |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import { GlSkeletonLoader, GlSprintf, GlIcon } from '@gitlab/ui'; | import { GlSkeletonLoader, GlIcon } from '@gitlab/ui'; | ||||||
| import { shallowMount } from '@vue/test-utils'; | import { mountExtended } from 'helpers/vue_test_utils_helper'; | ||||||
| import { AVAILABILITY_STATUS } from '~/set_status_modal/utils'; | import { AVAILABILITY_STATUS } from '~/set_status_modal/utils'; | ||||||
| import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; | import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; | ||||||
| import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue'; | import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue'; | ||||||
|  | @ -13,6 +13,7 @@ const DEFAULT_PROPS = { | ||||||
|     bio: null, |     bio: null, | ||||||
|     workInformation: null, |     workInformation: null, | ||||||
|     status: null, |     status: null, | ||||||
|  |     pronouns: 'they/them', | ||||||
|     loaded: true, |     loaded: true, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|  | @ -30,23 +31,18 @@ describe('User Popover Component', () => { | ||||||
|     wrapper.destroy(); |     wrapper.destroy(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   const findByTestId = (testid) => wrapper.find(`[data-testid="${testid}"]`); |  | ||||||
|   const findUserStatus = () => wrapper.find('.js-user-status'); |   const findUserStatus = () => wrapper.find('.js-user-status'); | ||||||
|   const findTarget = () => document.querySelector('.js-user-link'); |   const findTarget = () => document.querySelector('.js-user-link'); | ||||||
|   const findUserName = () => wrapper.find(UserNameWithStatus); |   const findUserName = () => wrapper.find(UserNameWithStatus); | ||||||
|   const findSecurityBotDocsLink = () => findByTestId('user-popover-bot-docs-link'); |   const findSecurityBotDocsLink = () => wrapper.findByTestId('user-popover-bot-docs-link'); | ||||||
| 
 | 
 | ||||||
|   const createWrapper = (props = {}, options = {}) => { |   const createWrapper = (props = {}, options = {}) => { | ||||||
|     wrapper = shallowMount(UserPopover, { |     wrapper = mountExtended(UserPopover, { | ||||||
|       propsData: { |       propsData: { | ||||||
|         ...DEFAULT_PROPS, |         ...DEFAULT_PROPS, | ||||||
|         target: findTarget(), |         target: findTarget(), | ||||||
|         ...props, |         ...props, | ||||||
|       }, |       }, | ||||||
|       stubs: { |  | ||||||
|         GlSprintf, |  | ||||||
|         UserNameWithStatus, |  | ||||||
|       }, |  | ||||||
|       ...options, |       ...options, | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
|  | @ -232,6 +228,12 @@ describe('User Popover Component', () => { | ||||||
| 
 | 
 | ||||||
|       expect(wrapper.text()).not.toContain('(Busy)'); |       expect(wrapper.text()).not.toContain('(Busy)'); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     it('passes `pronouns` prop to `UserNameWithStatus` component', () => { | ||||||
|  |       createWrapper(); | ||||||
|  | 
 | ||||||
|  |       expect(findUserName().props('pronouns')).toBe('they/them'); | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('bot user', () => { |   describe('bot user', () => { | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ | ||||||
| require 'spec_helper' | require 'spec_helper' | ||||||
| 
 | 
 | ||||||
| RSpec.describe 'global_id' do | RSpec.describe 'global_id' do | ||||||
|   it 'prepends `Gitlab::Patch::GlobalID`' do |   it 'prepends `Gitlab::Patch::GlobalId`' do | ||||||
|     expect(GlobalID.ancestors).to include(Gitlab::Patch::GlobalID) |     expect(GlobalID.ancestors).to include(Gitlab::Patch::GlobalId) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'patches GlobalID to find aliased models when a deprecation exists' do |   it 'patches GlobalID to find aliased models when a deprecation exists' do | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ RSpec.describe API::Entities::User do | ||||||
|   subject { described_class.new(user, current_user: current_user).as_json } |   subject { described_class.new(user, current_user: current_user).as_json } | ||||||
| 
 | 
 | ||||||
|   it 'exposes correct attributes' do |   it 'exposes correct attributes' do | ||||||
|     expect(subject).to include(:bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title, :work_information) |     expect(subject).to include(:bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title, :work_information, :pronouns) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'exposes created_at if the current user can read the user profile' do |   it 'exposes created_at if the current user can read the user profile' do | ||||||
|  |  | ||||||
|  | @ -368,7 +368,7 @@ project: | ||||||
| - drone_ci_integration | - drone_ci_integration | ||||||
| - emails_on_push_integration | - emails_on_push_integration | ||||||
| - pipelines_email_integration | - pipelines_email_integration | ||||||
| - mattermost_slash_commands_service | - mattermost_slash_commands_integration | ||||||
| - slack_slash_commands_service | - slack_slash_commands_service | ||||||
| - irker_integration | - irker_integration | ||||||
| - packagist_integration | - packagist_integration | ||||||
|  | @ -378,8 +378,8 @@ project: | ||||||
| - assembla_integration | - assembla_integration | ||||||
| - asana_integration | - asana_integration | ||||||
| - slack_service | - slack_service | ||||||
| - microsoft_teams_service | - microsoft_teams_integration | ||||||
| - mattermost_service | - mattermost_integration | ||||||
| - hangouts_chat_integration | - hangouts_chat_integration | ||||||
| - unify_circuit_service | - unify_circuit_service | ||||||
| - buildkite_integration | - buildkite_integration | ||||||
|  | @ -393,8 +393,8 @@ project: | ||||||
| - bugzilla_integration | - bugzilla_integration | ||||||
| - ewm_integration | - ewm_integration | ||||||
| - external_wiki_integration | - external_wiki_integration | ||||||
| - mock_ci_service | - mock_ci_integration | ||||||
| - mock_monitoring_service | - mock_monitoring_integration | ||||||
| - forked_to_members | - forked_to_members | ||||||
| - forked_from_project | - forked_from_project | ||||||
| - forks | - forks | ||||||
|  |  | ||||||
|  | @ -5,27 +5,29 @@ require 'spec_helper' | ||||||
| RSpec.describe Integrations::MattermostSlashCommands do | RSpec.describe Integrations::MattermostSlashCommands do | ||||||
|   it_behaves_like Integrations::BaseSlashCommands |   it_behaves_like Integrations::BaseSlashCommands | ||||||
| 
 | 
 | ||||||
|   context 'Mattermost API' do |   describe 'Mattermost API' do | ||||||
|     let(:project) { create(:project) } |     let(:project) { create(:project) } | ||||||
|     let(:service) { project.build_mattermost_slash_commands_service } |     let(:integration) { project.build_mattermost_slash_commands_integration } | ||||||
|     let(:user) { create(:user) } |     let(:user) { create(:user) } | ||||||
| 
 | 
 | ||||||
|     before do |     before do | ||||||
|       session = ::Mattermost::Session.new(nil) |       session = ::Mattermost::Session.new(nil) | ||||||
|       session.base_uri = 'http://mattermost.example.com' |       session.base_uri = 'http://mattermost.example.com' | ||||||
| 
 | 
 | ||||||
|       allow_any_instance_of(::Mattermost::Client).to receive(:with_session) |       allow(session).to receive(:with_session).and_yield(session) | ||||||
|         .and_yield(session) |       allow(::Mattermost::Session).to receive(:new).and_return(session) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     describe '#configure' do |     describe '#configure' do | ||||||
|       subject do |       subject do | ||||||
|         service.configure(user, team_id: 'abc', |         integration.configure(user, | ||||||
|                                 trigger: 'gitlab', url: 'http://trigger.url', |                               team_id: 'abc', | ||||||
|                                 icon_url: 'http://icon.url/icon.png') |                               trigger: 'gitlab', | ||||||
|  |                               url: 'http://trigger.url', | ||||||
|  |                               icon_url: 'http://icon.url/icon.png') | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       context 'the requests succeeds' do |       context 'when the request succeeds' do | ||||||
|         before do |         before do | ||||||
|           stub_request(:post, 'http://mattermost.example.com/api/v4/commands') |           stub_request(:post, 'http://mattermost.example.com/api/v4/commands') | ||||||
|             .with(body: { |             .with(body: { | ||||||
|  | @ -48,18 +50,18 @@ RSpec.describe Integrations::MattermostSlashCommands do | ||||||
|             ) |             ) | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         it 'saves the service' do |         it 'saves the integration' do | ||||||
|           expect { subject }.to change { project.integrations.count }.by(1) |           expect { subject }.to change { project.integrations.count }.by(1) | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         it 'saves the token' do |         it 'saves the token' do | ||||||
|           subject |           subject | ||||||
| 
 | 
 | ||||||
|           expect(service.reload.token).to eq('token') |           expect(integration.reload.token).to eq('token') | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       context 'an error is received' do |       context 'when an error is received' do | ||||||
|         before do |         before do | ||||||
|           stub_request(:post, 'http://mattermost.example.com/api/v4/commands') |           stub_request(:post, 'http://mattermost.example.com/api/v4/commands') | ||||||
|             .to_return( |             .to_return( | ||||||
|  | @ -86,10 +88,10 @@ RSpec.describe Integrations::MattermostSlashCommands do | ||||||
| 
 | 
 | ||||||
|     describe '#list_teams' do |     describe '#list_teams' do | ||||||
|       subject do |       subject do | ||||||
|         service.list_teams(user) |         integration.list_teams(user) | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       context 'the requests succeeds' do |       context 'when the request succeeds' do | ||||||
|         before do |         before do | ||||||
|           stub_request(:get, 'http://mattermost.example.com/api/v4/users/me/teams') |           stub_request(:get, 'http://mattermost.example.com/api/v4/users/me/teams') | ||||||
|             .to_return( |             .to_return( | ||||||
|  | @ -104,7 +106,7 @@ RSpec.describe Integrations::MattermostSlashCommands do | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       context 'an error is received' do |       context 'when an error is received' do | ||||||
|         before do |         before do | ||||||
|           stub_request(:get, 'http://mattermost.example.com/api/v4/users/me/teams') |           stub_request(:get, 'http://mattermost.example.com/api/v4/users/me/teams') | ||||||
|             .to_return( |             .to_return( | ||||||
|  |  | ||||||
|  | @ -64,7 +64,8 @@ RSpec.describe Integrations::MicrosoftTeams do | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       it 'specifies the webhook when it is configured' do |       it 'specifies the webhook when it is configured' do | ||||||
|         expect(::MicrosoftTeams::Notifier).to receive(:new).with(webhook_url).and_return(double(:microsoft_teams_service).as_null_object) |         integration = double(:microsoft_teams_integration).as_null_object | ||||||
|  |         expect(::MicrosoftTeams::Notifier).to receive(:new).with(webhook_url).and_return(integration) | ||||||
| 
 | 
 | ||||||
|         chat_service.execute(push_sample_data) |         chat_service.execute(push_sample_data) | ||||||
|       end |       end | ||||||
|  |  | ||||||
|  | @ -36,8 +36,8 @@ RSpec.describe Project, factory_default: :keep do | ||||||
|     it { is_expected.to have_many(:protected_branches) } |     it { is_expected.to have_many(:protected_branches) } | ||||||
|     it { is_expected.to have_many(:exported_protected_branches) } |     it { is_expected.to have_many(:exported_protected_branches) } | ||||||
|     it { is_expected.to have_one(:slack_service) } |     it { is_expected.to have_one(:slack_service) } | ||||||
|     it { is_expected.to have_one(:microsoft_teams_service) } |     it { is_expected.to have_one(:microsoft_teams_integration) } | ||||||
|     it { is_expected.to have_one(:mattermost_service) } |     it { is_expected.to have_one(:mattermost_integration) } | ||||||
|     it { is_expected.to have_one(:hangouts_chat_integration) } |     it { is_expected.to have_one(:hangouts_chat_integration) } | ||||||
|     it { is_expected.to have_one(:unify_circuit_service) } |     it { is_expected.to have_one(:unify_circuit_service) } | ||||||
|     it { is_expected.to have_one(:webex_teams_service) } |     it { is_expected.to have_one(:webex_teams_service) } | ||||||
|  | @ -56,7 +56,7 @@ RSpec.describe Project, factory_default: :keep do | ||||||
|     it { is_expected.to have_one(:flowdock_integration) } |     it { is_expected.to have_one(:flowdock_integration) } | ||||||
|     it { is_expected.to have_one(:assembla_integration) } |     it { is_expected.to have_one(:assembla_integration) } | ||||||
|     it { is_expected.to have_one(:slack_slash_commands_service) } |     it { is_expected.to have_one(:slack_slash_commands_service) } | ||||||
|     it { is_expected.to have_one(:mattermost_slash_commands_service) } |     it { is_expected.to have_one(:mattermost_slash_commands_integration) } | ||||||
|     it { is_expected.to have_one(:buildkite_integration) } |     it { is_expected.to have_one(:buildkite_integration) } | ||||||
|     it { is_expected.to have_one(:bamboo_integration) } |     it { is_expected.to have_one(:bamboo_integration) } | ||||||
|     it { is_expected.to have_one(:teamcity_service) } |     it { is_expected.to have_one(:teamcity_service) } | ||||||
|  | @ -80,6 +80,8 @@ RSpec.describe Project, factory_default: :keep do | ||||||
|     it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') } |     it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') } | ||||||
|     it { is_expected.to have_one(:project_setting) } |     it { is_expected.to have_one(:project_setting) } | ||||||
|     it { is_expected.to have_one(:alerting_setting).class_name('Alerting::ProjectAlertingSetting') } |     it { is_expected.to have_one(:alerting_setting).class_name('Alerting::ProjectAlertingSetting') } | ||||||
|  |     it { is_expected.to have_one(:mock_ci_integration) } | ||||||
|  |     it { is_expected.to have_one(:mock_monitoring_integration) } | ||||||
|     it { is_expected.to have_many(:commit_statuses) } |     it { is_expected.to have_many(:commit_statuses) } | ||||||
|     it { is_expected.to have_many(:ci_pipelines) } |     it { is_expected.to have_many(:ci_pipelines) } | ||||||
|     it { is_expected.to have_many(:ci_refs) } |     it { is_expected.to have_many(:ci_refs) } | ||||||
|  |  | ||||||
|  | @ -179,10 +179,10 @@ RSpec.describe API::Services do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'POST /projects/:id/services/:slug/trigger' do |   describe 'POST /projects/:id/services/:slug/trigger' do | ||||||
|     describe 'Mattermost Service' do |     describe 'Mattermost integration' do | ||||||
|       let(:service_name) { 'mattermost_slash_commands' } |       let(:integration_name) { 'mattermost_slash_commands' } | ||||||
| 
 | 
 | ||||||
|       context 'no service is available' do |       context 'when no integration is available' do | ||||||
|         it 'returns a not found message' do |         it 'returns a not found message' do | ||||||
|           post api("/projects/#{project.id}/services/idonotexist/trigger") |           post api("/projects/#{project.id}/services/idonotexist/trigger") | ||||||
| 
 | 
 | ||||||
|  | @ -191,34 +191,34 @@ RSpec.describe API::Services do | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       context 'the service exists' do |       context 'when the integration exists' do | ||||||
|         let(:params) { { token: 'token' } } |         let(:params) { { token: 'token' } } | ||||||
| 
 | 
 | ||||||
|         context 'the service is not active' do |         context 'when the integration is not active' do | ||||||
|           before do |           before do | ||||||
|             project.create_mattermost_slash_commands_service( |             project.create_mattermost_slash_commands_integration( | ||||||
|               active: false, |               active: false, | ||||||
|               properties: params |               properties: params | ||||||
|             ) |             ) | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|           it 'when the service is inactive' do |           it 'when the integration is inactive' do | ||||||
|             post api("/projects/#{project.id}/services/#{service_name}/trigger"), params: params |             post api("/projects/#{project.id}/services/#{integration_name}/trigger"), params: params | ||||||
| 
 | 
 | ||||||
|             expect(response).to have_gitlab_http_status(:not_found) |             expect(response).to have_gitlab_http_status(:not_found) | ||||||
|           end |           end | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         context 'the service is active' do |         context 'when the integration is active' do | ||||||
|           before do |           before do | ||||||
|             project.create_mattermost_slash_commands_service( |             project.create_mattermost_slash_commands_integration( | ||||||
|               active: true, |               active: true, | ||||||
|               properties: params |               properties: params | ||||||
|             ) |             ) | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|           it 'returns status 200' do |           it 'returns status 200' do | ||||||
|             post api("/projects/#{project.id}/services/#{service_name}/trigger"), params: params |             post api("/projects/#{project.id}/services/#{integration_name}/trigger"), params: params | ||||||
| 
 | 
 | ||||||
|             expect(response).to have_gitlab_http_status(:ok) |             expect(response).to have_gitlab_http_status(:ok) | ||||||
|           end |           end | ||||||
|  | @ -226,7 +226,7 @@ RSpec.describe API::Services do | ||||||
| 
 | 
 | ||||||
|         context 'when the project can not be found' do |         context 'when the project can not be found' do | ||||||
|           it 'returns a generic 404' do |           it 'returns a generic 404' do | ||||||
|             post api("/projects/404/services/#{service_name}/trigger"), params: params |             post api("/projects/404/services/#{integration_name}/trigger"), params: params | ||||||
| 
 | 
 | ||||||
|             expect(response).to have_gitlab_http_status(:not_found) |             expect(response).to have_gitlab_http_status(:not_found) | ||||||
|             expect(json_response["message"]).to eq("404 Service Not Found") |             expect(json_response["message"]).to eq("404 Service Not Found") | ||||||
|  | @ -254,29 +254,29 @@ RSpec.describe API::Services do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'Mattermost service' do |   describe 'Mattermost integration' do | ||||||
|     let(:service_name) { 'mattermost' } |     let(:integration_name) { 'mattermost' } | ||||||
|     let(:params) do |     let(:params) do | ||||||
|       { webhook: 'https://hook.example.com', username: 'username' } |       { webhook: 'https://hook.example.com', username: 'username' } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     before do |     before do | ||||||
|       project.create_mattermost_service( |       project.create_mattermost_integration( | ||||||
|         active: true, |         active: true, | ||||||
|         properties: params |         properties: params | ||||||
|       ) |       ) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'accepts a username for update' do |     it 'accepts a username for update' do | ||||||
|       put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(username: 'new_username') |       put api("/projects/#{project.id}/services/#{integration_name}", user), params: params.merge(username: 'new_username') | ||||||
| 
 | 
 | ||||||
|       expect(response).to have_gitlab_http_status(:ok) |       expect(response).to have_gitlab_http_status(:ok) | ||||||
|       expect(json_response['properties']['username']).to eq('new_username') |       expect(json_response['properties']['username']).to eq('new_username') | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'Microsoft Teams service' do |   describe 'Microsoft Teams integration' do | ||||||
|     let(:service_name) { 'microsoft-teams' } |     let(:integration_name) { 'microsoft-teams' } | ||||||
|     let(:params) do |     let(:params) do | ||||||
|       { |       { | ||||||
|         webhook: 'https://hook.example.com', |         webhook: 'https://hook.example.com', | ||||||
|  | @ -286,21 +286,23 @@ RSpec.describe API::Services do | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     before do |     before do | ||||||
|       project.create_microsoft_teams_service( |       project.create_microsoft_teams_integration( | ||||||
|         active: true, |         active: true, | ||||||
|         properties: params |         properties: params | ||||||
|       ) |       ) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'accepts branches_to_be_notified for update' do |     it 'accepts branches_to_be_notified for update' do | ||||||
|       put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(branches_to_be_notified: 'all') |       put api("/projects/#{project.id}/services/#{integration_name}", user), | ||||||
|  |           params: params.merge(branches_to_be_notified: 'all') | ||||||
| 
 | 
 | ||||||
|       expect(response).to have_gitlab_http_status(:ok) |       expect(response).to have_gitlab_http_status(:ok) | ||||||
|       expect(json_response['properties']['branches_to_be_notified']).to eq('all') |       expect(json_response['properties']['branches_to_be_notified']).to eq('all') | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'accepts notify_only_broken_pipelines for update' do |     it 'accepts notify_only_broken_pipelines for update' do | ||||||
|       put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(notify_only_broken_pipelines: true) |       put api("/projects/#{project.id}/services/#{integration_name}", user), | ||||||
|  |           params: params.merge(notify_only_broken_pipelines: true) | ||||||
| 
 | 
 | ||||||
|       expect(response).to have_gitlab_http_status(:ok) |       expect(response).to have_gitlab_http_status(:ok) | ||||||
|       expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true) |       expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true) | ||||||
|  |  | ||||||
|  | @ -30,7 +30,7 @@ RSpec.describe Ci::RetryBuildService do | ||||||
|     project.add_reporter(reporter) |     project.add_reporter(reporter) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   clone_accessors = described_class.clone_accessors |   clone_accessors = described_class.clone_accessors.without(described_class.extra_accessors) | ||||||
| 
 | 
 | ||||||
|   reject_accessors = |   reject_accessors = | ||||||
|     %i[id status user token token_encrypted coverage trace runner |     %i[id status user token token_encrypted coverage trace runner | ||||||
|  | @ -98,7 +98,7 @@ RSpec.describe Ci::RetryBuildService do | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       clone_accessors.each do |attribute| |       clone_accessors.each do |attribute| | ||||||
|         it "clones #{attribute} build attribute" do |         it "clones #{attribute} build attribute", :aggregate_failures do | ||||||
|           expect(attribute).not_to be_in(forbidden_associations), "association #{attribute} must be `belongs_to`" |           expect(attribute).not_to be_in(forbidden_associations), "association #{attribute} must be `belongs_to`" | ||||||
|           expect(build.send(attribute)).not_to be_nil |           expect(build.send(attribute)).not_to be_nil | ||||||
|           expect(new_build.send(attribute)).not_to be_nil |           expect(new_build.send(attribute)).not_to be_nil | ||||||
|  | @ -134,7 +134,7 @@ RSpec.describe Ci::RetryBuildService do | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'has correct number of known attributes' do |     it 'has correct number of known attributes', :aggregate_failures do | ||||||
|       processed_accessors = clone_accessors + reject_accessors |       processed_accessors = clone_accessors + reject_accessors | ||||||
|       known_accessors = processed_accessors + ignore_accessors |       known_accessors = processed_accessors + ignore_accessors | ||||||
| 
 | 
 | ||||||
|  | @ -146,9 +146,10 @@ RSpec.describe Ci::RetryBuildService do | ||||||
|         Ci::Build.attribute_names.map(&:to_sym) + |         Ci::Build.attribute_names.map(&:to_sym) + | ||||||
|         Ci::Build.attribute_aliases.keys.map(&:to_sym) + |         Ci::Build.attribute_aliases.keys.map(&:to_sym) + | ||||||
|         Ci::Build.reflect_on_all_associations.map(&:name) + |         Ci::Build.reflect_on_all_associations.map(&:name) + | ||||||
|         [:tag_list, :needs_attributes] |         [:tag_list, :needs_attributes] - | ||||||
| 
 |         # ee-specific accessors should be tested in ee/spec/services/ci/retry_build_service_spec.rb instead | ||||||
|       current_accessors << :secrets if Gitlab.ee? |         described_class.extra_accessors - | ||||||
|  |         [:dast_site_profiles_build, :dast_scanner_profiles_build] # join tables | ||||||
| 
 | 
 | ||||||
|       current_accessors.uniq! |       current_accessors.uniq! | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -66,14 +66,14 @@ RSpec.shared_examples Integrations::BaseSlashCommands do | ||||||
|             } |             } | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|           let(:service) do |           let(:integration) do | ||||||
|             project.create_mattermost_slash_commands_service( |             project.create_mattermost_slash_commands_integration( | ||||||
|               properties: { token: 'token' } |               properties: { token: 'token' } | ||||||
|             ) |             ) | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|           it 'generates the url' do |           it 'generates the url' do | ||||||
|             response = service.trigger(params) |             response = integration.trigger(params) | ||||||
| 
 | 
 | ||||||
|             expect(response[:text]).to start_with(':wave: Hi there!') |             expect(response[:text]).to start_with(':wave: Hi there!') | ||||||
|           end |           end | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | node_modules/ | ||||||
|  | public/ | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | /* eslint-disable import/no-commonjs */ | ||||||
|  | const IS_EE = require('../../config/helpers/is_ee_env'); | ||||||
|  | 
 | ||||||
|  | module.exports = { | ||||||
|  |   stories: [ | ||||||
|  |     '../../app/assets/javascripts/**/*.stories.js', | ||||||
|  |     IS_EE && '../../ee/app/assets/javascripts/**/*.stories.js', | ||||||
|  |   ].filter(Boolean), | ||||||
|  |   addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'], | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | const stylesheetsRequireCtx = require.context( | ||||||
|  |   '../../app/assets/stylesheets', | ||||||
|  |   true, | ||||||
|  |   /application\.scss$/, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | stylesheetsRequireCtx('./application.scss'); | ||||||
|  | @ -0,0 +1,104 @@ | ||||||
|  | /* eslint-disable no-param-reassign */ | ||||||
|  | 
 | ||||||
|  | const { statSync } = require('fs'); | ||||||
|  | const path = require('path'); | ||||||
|  | const sass = require('node-sass'); // eslint-disable-line import/no-unresolved
 | ||||||
|  | const { buildIncludePaths, resolveGlobUrl } = require('node-sass-magic-importer/dist/toolbox'); // eslint-disable-line import/no-unresolved
 | ||||||
|  | const webpack = require('webpack'); | ||||||
|  | const gitlabWebpackConfig = require('../../config/webpack.config.js'); | ||||||
|  | 
 | ||||||
|  | const ROOT = path.resolve(__dirname, '../../'); | ||||||
|  | const TRANSPARENT_1X1_PNG = | ||||||
|  |   'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==)'; | ||||||
|  | const SASS_INCLUDE_PATHS = [ | ||||||
|  |   'app/assets/stylesheets', | ||||||
|  |   'app/assets/stylesheets/_ee', | ||||||
|  |   'ee/app/assets/stylesheets', | ||||||
|  |   'ee/app/assets/stylesheets/_ee', | ||||||
|  |   'node_modules', | ||||||
|  | ].map((p) => path.resolve(ROOT, p)); | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Custom importer for node-sass, used when LibSass encounters the `@import` directive. | ||||||
|  |  * Doc source: https://github.com/sass/node-sass#importer--v200---experimental
 | ||||||
|  |  * @param {*} url the path in import as-is, which LibSass encountered. | ||||||
|  |  * @param {*} prev the previously resolved path. | ||||||
|  |  * @returns {Object | null} the new import string. | ||||||
|  |  */ | ||||||
|  | function sassSmartImporter(url, prev) { | ||||||
|  |   const nodeSassOptions = this.options; | ||||||
|  |   const includePaths = buildIncludePaths(nodeSassOptions.includePaths, prev).filter( | ||||||
|  |     (includePath) => !includePath.includes('node_modules'), | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   // GitLab extensively uses glob-style import paths, but
 | ||||||
|  |   // Sass doesn't support glob-style URLs out of the box.
 | ||||||
|  |   // Here, we try and resolve the glob URL.
 | ||||||
|  |   // If it resolves, we update the @import statement with the resolved path.
 | ||||||
|  |   const filePaths = resolveGlobUrl(url, includePaths); | ||||||
|  |   if (filePaths) { | ||||||
|  |     const contents = filePaths | ||||||
|  |       .filter((file) => statSync(file).isFile()) | ||||||
|  |       .map((x) => `@import '${x}';`) | ||||||
|  |       .join(`\n`); | ||||||
|  | 
 | ||||||
|  |     return { contents }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return null; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const sassLoaderOptions = { | ||||||
|  |   functions: { | ||||||
|  |     'image-url($url)': function sassImageUrlStub() { | ||||||
|  |       return new sass.types.String(TRANSPARENT_1X1_PNG); | ||||||
|  |     }, | ||||||
|  |     'asset_path($url)': function sassAssetPathStub() { | ||||||
|  |       return new sass.types.String(TRANSPARENT_1X1_PNG); | ||||||
|  |     }, | ||||||
|  |     'asset_url($url)': function sassAssetUrlStub() { | ||||||
|  |       return new sass.types.String(TRANSPARENT_1X1_PNG); | ||||||
|  |     }, | ||||||
|  |     'url($url)': function sassUrlStub() { | ||||||
|  |       return new sass.types.String(TRANSPARENT_1X1_PNG); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   includePaths: SASS_INCLUDE_PATHS, | ||||||
|  |   importer: sassSmartImporter, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports = function storybookWebpackConfig({ config }) { | ||||||
|  |   // Add any missing extensions from the main GitLab webpack config
 | ||||||
|  |   config.resolve.extensions = Array.from( | ||||||
|  |     new Set([...config.resolve.extensions, ...gitlabWebpackConfig.resolve.extensions]), | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   // Replace any Storybook-defined CSS loaders with our custom one.
 | ||||||
|  |   config.module.rules = [ | ||||||
|  |     ...config.module.rules.filter((r) => !r.test.test('.css')), | ||||||
|  |     { | ||||||
|  |       test: /\.s?css$/, | ||||||
|  |       exclude: /typescale\/\w+_demo\.scss$/, // skip typescale demo stylesheets
 | ||||||
|  |       loaders: [ | ||||||
|  |         'style-loader', | ||||||
|  |         'css-loader', | ||||||
|  |         { | ||||||
|  |           loader: 'sass-loader', | ||||||
|  |           options: sassLoaderOptions, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  |   // Silence webpack warnings about moment/pikaday not being able to resolve.
 | ||||||
|  |   config.plugins.push(new webpack.IgnorePlugin(/moment/, /pikaday/)); | ||||||
|  | 
 | ||||||
|  |   // Add any missing aliases from the main GitLab webpack config
 | ||||||
|  |   Object.assign(config.resolve.alias, gitlabWebpackConfig.resolve.alias); | ||||||
|  |   // The main GitLab project aliases this `icons.svg` file to app/assets/javascripts/lib/utils/icons_path.js,
 | ||||||
|  |   // which depends on the existence of a global `gon` variable.
 | ||||||
|  |   // By deleting the alias, imports of this path will resolve as expected.
 | ||||||
|  |   delete config.resolve.alias['@gitlab/svgs/dist/icons.svg']; | ||||||
|  | 
 | ||||||
|  |   return config; | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | { | ||||||
|  |   "private": true, | ||||||
|  |   "scripts": { | ||||||
|  |     "test": "echo \"Error: no test specified\" && exit 1", | ||||||
|  |     "start": "start-storybook -p 9002 -c config", | ||||||
|  |     "build": "build-storybook -c config -o public" | ||||||
|  |   }, | ||||||
|  |   "dependencies": {}, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@storybook/addon-a11y": "^6.2.9", | ||||||
|  |     "@storybook/addon-actions": "^6.2.9", | ||||||
|  |     "@storybook/addon-controls": "^6.2.9", | ||||||
|  |     "@storybook/addon-essentials": "^6.2.9", | ||||||
|  |     "@storybook/vue": "6.2.9", | ||||||
|  |     "node-sass": "^4.14.1", | ||||||
|  |     "node-sass-magic-importer": "^5.3.2", | ||||||
|  |     "postcss-loader": "3.0.0", | ||||||
|  |     "sass-loader": "^7.1.0" | ||||||
|  |   } | ||||||
|  | } | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue