diff options
78 files changed, 1567 insertions, 810 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e810edd8133..2ea4eb0de65 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,6 +72,7 @@ variables: FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/report-suite.json RSPEC_TESTS_MAPPING_PATH: crystalball/mapping.json RSPEC_PACKED_TESTS_MAPPING_PATH: crystalball/packed-mapping.json + FRONTEND_FIXTURES_MAPPING_PATH: crystalball/frontend_fixtures_mapping.json ES_JAVA_OPTS: "-Xms256m -Xmx256m" ELASTIC_URL: "http://elastic:changeme@elasticsearch:9200" diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 0b4f7ca1dd9..efc65431d50 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1694,6 +1694,16 @@ changes: *code-backstage-patterns when: on_success +.setup:rules:generate-frontend-fixtures-mapping: + rules: + - <<: *if-not-ee + when: never + - <<: *if-dot-com-ee-2-hourly-schedule + - changes: + - ".gitlab/ci/setup.gitlab-ci.yml" + - ".gitlab/ci/test-metadata.gitlab-ci.yml" + - "scripts/rspec_helpers.sh" + .setup:rules:add-jh-folder: rules: - <<: *if-not-ee diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml index 4c674f38939..1eb3bd2ea41 100644 --- a/.gitlab/ci/setup.gitlab-ci.yml +++ b/.gitlab/ci/setup.gitlab-ci.yml @@ -68,6 +68,24 @@ verify-tests-yml: - install_tff_gem - scripts/verify-tff-mapping +generate-frontend-fixtures-mapping: + extends: + - .setup:rules:generate-frontend-fixtures-mapping + - .use-pg12 + - .rails-cache + needs: ["setup-test-env"] + stage: prepare + before_script: + - !reference [.default-before_script, before_script] + - source ./scripts/rspec_helpers.sh + - run_timed_command "scripts/gitaly-test-spawn" + script: + - generate_frontend_fixtures_mapping + artifacts: + expire_in: 7d + paths: + - ${FRONTEND_FIXTURES_MAPPING_PATH} + .detect-test-base: image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7 needs: [] @@ -78,17 +96,21 @@ verify-tests-yml: - install_gitlab_gem - install_tff_gem - retrieve_tests_mapping + - retrieve_frontend_fixtures_mapping - | if [ -n "$CI_MERGE_REQUEST_IID" ]; then tooling/bin/find_changes ${CHANGES_FILE}; tooling/bin/find_tests ${CHANGES_FILE} ${MATCHED_TESTS_FILE}; - echo "related rspec tests: $(cat $MATCHED_TESTS_FILE)"; + tooling/bin/find_changes ${CHANGES_FILE} ${MATCHED_TESTS_FILE} ${FRONTEND_FIXTURES_MAPPING_PATH}; + echo "Changed files: $(cat $CHANGES_FILE)"; + echo "Related rspec tests: $(cat $MATCHED_TESTS_FILE)"; fi artifacts: expire_in: 7d paths: - ${CHANGES_FILE} - ${MATCHED_TESTS_FILE} + - ${FRONTEND_FIXTURES_MAPPING_PATH} detect-tests: extends: diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml index 2d96fb6d4b0..d0d45cb9294 100644 --- a/.gitlab/ci/test-metadata.gitlab-ci.yml +++ b/.gitlab/ci/test-metadata.gitlab-ci.yml @@ -8,7 +8,7 @@ - knapsack/ - rspec_flaky/ - rspec_profiling/ - - crystalball/packed-mapping.json.gz + - crystalball/ retrieve-tests-metadata: extends: @@ -27,6 +27,7 @@ update-tests-metadata: stage: post-test dependencies: - retrieve-tests-metadata + - generate-frontend-fixtures-mapping - setup-test-env - rspec migration pg12 - rspec-all frontend_fixture diff --git a/app/assets/javascripts/google_cloud/components/errors/gcp_error.vue b/app/assets/javascripts/google_cloud/components/errors/gcp_error.vue new file mode 100644 index 00000000000..90aa0e1ae68 --- /dev/null +++ b/app/assets/javascripts/google_cloud/components/errors/gcp_error.vue @@ -0,0 +1,29 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + components: { GlAlert }, + props: { + error: { + type: String, + required: true, + }, + }, + i18n: { + title: __('Google Cloud project misconfigured'), + description: __( + 'GitLab and Google Cloud configuration seems to be incomplete. This probably can be fixed by your GitLab administration team. You may share these logs with them:', + ), + }, +}; +</script> + +<template> + <gl-alert :dismissible="false" variant="warning" :title="$options.i18n.title"> + {{ $options.i18n.description }} + <blockquote> + <code>{{ error }}</code> + </blockquote> + </gl-alert> +</template> diff --git a/app/assets/javascripts/google_cloud/components/errors/no_gcp_projects.vue b/app/assets/javascripts/google_cloud/components/errors/no_gcp_projects.vue new file mode 100644 index 00000000000..da229ac3f0e --- /dev/null +++ b/app/assets/javascripts/google_cloud/components/errors/no_gcp_projects.vue @@ -0,0 +1,26 @@ +<script> +import { GlAlert, GlButton } from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + components: { GlAlert, GlButton }, + i18n: { + title: __('Google Cloud project required'), + description: __( + 'You do not have any Google Cloud projects. Please create a Google Cloud project and then reload this page.', + ), + createLabel: __('Create Google Cloud project'), + }, +}; +</script> + +<template> + <gl-alert :dismissible="false" variant="warning" :title="$options.i18n.title"> + {{ $options.i18n.description }} + <template #actions> + <gl-button href="https://console.cloud.google.com/projectcreate" target="_blank"> + {{ $options.i18n.createLabel }} + </gl-button> + </template> + </gl-alert> +</template> diff --git a/app/assets/javascripts/google_cloud/components/app.vue b/app/assets/javascripts/google_cloud/components/screens/app.vue index 1e5be9df019..52c9b478916 100644 --- a/app/assets/javascripts/google_cloud/components/app.vue +++ b/app/assets/javascripts/google_cloud/components/screens/app.vue @@ -1,10 +1,10 @@ <script> import { GlTab, GlTabs } from '@gitlab/ui'; -import IncubationBanner from './incubation_banner.vue'; -import ServiceAccounts from './service_accounts.vue'; +import IncubationBanner from '../incubation_banner.vue'; +import ServiceAccountsList from '../service_accounts_list.vue'; export default { - components: { GlTab, GlTabs, IncubationBanner, ServiceAccounts }, + components: { GlTab, GlTabs, IncubationBanner, ServiceAccountsList }, props: { serviceAccounts: { type: Array, @@ -36,7 +36,7 @@ export default { /> <gl-tabs> <gl-tab :title="__('Configuration')"> - <service-accounts + <service-accounts-list class="gl-mx-3" :list="serviceAccounts" :create-url="createServiceAccountUrl" diff --git a/app/assets/javascripts/google_cloud/components/screens/service_accounts_form.vue b/app/assets/javascripts/google_cloud/components/screens/service_accounts_form.vue new file mode 100644 index 00000000000..6aead296918 --- /dev/null +++ b/app/assets/javascripts/google_cloud/components/screens/service_accounts_form.vue @@ -0,0 +1,81 @@ +<script> +import { GlButton, GlFormGroup, GlFormSelect } from '@gitlab/ui'; +import { __ } from '~/locale'; +import IncubationBanner from '../incubation_banner.vue'; + +export default { + components: { GlButton, GlFormGroup, GlFormSelect, IncubationBanner }, + props: { + gcpProjects: { required: true, type: Array }, + environments: { required: true, type: Array }, + cancelPath: { required: true, type: String }, + }, + methods: { + feedbackUrl(template) { + return `https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/meta/-/issues/new?issuable_template=${template}`; + }, + }, + i18n: { + title: __('Create service account'), + gcpProjectLabel: __('Google Cloud project'), + gcpProjectDescription: __( + 'New service account is generated for the selected Google Cloud project', + ), + environmentLabel: __('Environment'), + environmentDescription: __('Generated service account is linked to the selected environment'), + submitLabel: __('Create service account'), + cancelLabel: __('Cancel'), + }, +}; +</script> + +<template> + <div> + <incubation-banner + :share-feedback-url="feedbackUrl('general_feedback')" + :report-bug-url="feedbackUrl('report_bug')" + :feature-request-url="feedbackUrl('feature_request')" + /> + <header class="gl-my-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid"> + <h2 class="gl-font-size-h1">{{ $options.i18n.title }}</h2> + </header> + <gl-form-group + label-for="gcp_project" + :label="$options.i18n.gcpProjectLabel" + :description="$options.i18n.gcpProjectDescription" + > + <gl-form-select id="gcp_project" name="gcp_project" required> + <option + v-for="gcpProject in gcpProjects" + :key="gcpProject.project_id" + :value="gcpProject.project_id" + > + {{ gcpProject.name }} + </option> + </gl-form-select> + </gl-form-group> + <gl-form-group + label-for="environment" + :label="$options.i18n.environmentLabel" + :description="$options.i18n.environmentDescription" + > + <gl-form-select id="environment" name="environment" required> + <option value="*">{{ __('All') }}</option> + <option + v-for="environment in environments" + :key="environment.name" + :value="environment.name" + > + {{ environment.name }} + </option> + </gl-form-select> + </gl-form-group> + + <div class="form-actions row"> + <gl-button type="submit" category="primary" variant="confirm"> + {{ $options.i18n.submitLabel }} + </gl-button> + <gl-button class="gl-ml-1" :href="cancelPath">{{ $options.i18n.cancelLabel }}</gl-button> + </div> + </div> +</template> diff --git a/app/assets/javascripts/google_cloud/components/service_accounts.vue b/app/assets/javascripts/google_cloud/components/service_accounts_list.vue index b70b25a5dc3..b70b25a5dc3 100644 --- a/app/assets/javascripts/google_cloud/components/service_accounts.vue +++ b/app/assets/javascripts/google_cloud/components/service_accounts_list.vue diff --git a/app/assets/javascripts/google_cloud/index.js b/app/assets/javascripts/google_cloud/index.js index a156a632e9a..ba67877e005 100644 --- a/app/assets/javascripts/google_cloud/index.js +++ b/app/assets/javascripts/google_cloud/index.js @@ -1,11 +1,40 @@ import Vue from 'vue'; -import App from './components/app.vue'; +import { __ } from '~/locale'; +import App from './components/screens/app.vue'; +import ServiceAccountsForm from './components/screens/service_accounts_form.vue'; +import ErrorNoGcpProjects from './components/errors/no_gcp_projects.vue'; +import ErrorGcpError from './components/errors/gcp_error.vue'; const elementRenderer = (element, props = {}) => (createElement) => createElement(element, { props }); +const rootComponentMap = [ + { + root: '#js-google-cloud-error-no-gcp-projects', + component: ErrorNoGcpProjects, + }, + { + root: '#js-google-cloud-error-gcp-error', + component: ErrorGcpError, + }, + { + root: '#js-google-cloud-service-accounts', + component: ServiceAccountsForm, + }, + { + root: '#js-google-cloud', + component: App, + }, +]; + export default () => { - const root = document.querySelector('#js-google-cloud'); - const props = JSON.parse(root.getAttribute('data')); - return new Vue({ el: root, render: elementRenderer(App, props) }); + for (let i = 0; i < rootComponentMap.length; i += 1) { + const { root, component } = rootComponentMap[i]; + const element = document.querySelector(root); + if (element) { + const props = JSON.parse(element.getAttribute('data')); + return new Vue({ el: root, render: elementRenderer(component, props) }); + } + } + throw new Error(__('Unknown root')); }; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue index 3eda2828e97..18761d04c2e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue @@ -41,7 +41,6 @@ export default { rows="7" @input="$emit('input', $event.target.value)" ></textarea> - <slot name="text-muted"></slot> </div> </li> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index 08a44d81bf0..8830128b7d6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -181,9 +181,16 @@ export default { return this.mr.canRemoveSourceBranch; }, commitTemplateHelpPage() { - return helpPagePath('user/project/merge_requests/commit_templates.md', { - anchor: 'merge-commit-message-template', - }); + return helpPagePath('user/project/merge_requests/commit_templates.md'); + }, + commitTemplateHintText() { + if (this.shouldShowSquashEdit && this.shouldShowMergeEdit) { + return this.$options.i18n.mergeAndSquashCommitTemplatesHintText; + } + if (this.shouldShowSquashEdit) { + return this.$options.i18n.squashCommitTemplateHintText; + } + return this.$options.i18n.mergeCommitTemplateHintText; }, commits() { if (this.glFeatures.mergeRequestWidgetGraphql) { @@ -509,6 +516,12 @@ export default { mergeCommitTemplateHintText: s__( 'mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}', ), + squashCommitTemplateHintText: s__( + 'mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}', + ), + mergeAndSquashCommitTemplatesHintText: s__( + 'mrWidget|To change these default messages, edit the templates for both the merge and squash commit messages. %{linkStart}Learn more.%{linkEnd}', + ), }, }; </script> @@ -674,23 +687,22 @@ export default { :label="__('Merge commit message')" input-id="merge-message-edit" class="gl-m-0! gl-p-0!" - > - <template #text-muted> - <p class="form-text text-muted"> - <gl-sprintf :message="$options.i18n.mergeCommitTemplateHintText"> - <template #link="{ content }"> - <gl-link - :href="commitTemplateHelpPage" - class="inline-link" - target="_blank" - > - {{ content }} - </gl-link> - </template> - </gl-sprintf> - </p> - </template> - </commit-edit> + /> + <li class="gl-m-0! gl-p-0!"> + <p class="form-text text-muted"> + <gl-sprintf :message="commitTemplateHintText"> + <template #link="{ content }"> + <gl-link + :href="commitTemplateHelpPage" + class="inline-link" + target="_blank" + > + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </p> + </li> </ul> </div> <div @@ -792,19 +804,18 @@ export default { v-model="commitMessage" :label="__('Merge commit message')" input-id="merge-message-edit" - > - <template #text-muted> - <p class="form-text text-muted"> - <gl-sprintf :message="$options.i18n.mergeCommitTemplateHintText"> - <template #link="{ content }"> - <gl-link :href="commitTemplateHelpPage" class="inline-link" target="_blank"> - {{ content }} - </gl-link> - </template> - </gl-sprintf> - </p> - </template> - </commit-edit> + /> + <li> + <p class="form-text text-muted"> + <gl-sprintf :message="commitTemplateHintText"> + <template #link="{ content }"> + <gl-link :href="commitTemplateHelpPage" class="inline-link" target="_blank"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </p> + </li> </ul> </commits-header> </template> diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b3ce3506c43..17d4a449d4e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -452,6 +452,7 @@ class ProjectsController < Projects::ApplicationController :packages_enabled, :service_desk_enabled, :merge_commit_template, + :squash_commit_template, project_setting_attributes: project_setting_attributes ] + [project_feature_attributes: project_feature_attributes] end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index b6cb9cd3302..8ef53ae001b 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -386,6 +386,11 @@ module Types null: true, description: 'Template used to create merge commit message in merge requests.' + field :squash_commit_template, + GraphQL::Types::String, + null: true, + description: 'Template used to create squash commit message in merge requests.' + def label(title:) BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args| LabelsFinder diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 9e819a735b5..8bc4a9db20a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1317,7 +1317,7 @@ class MergeRequest < ApplicationRecord def default_merge_commit_message(include_description: false) if self.target_project.merge_commit_template.present? && !include_description - return ::Gitlab::MergeRequests::MergeCommitMessage.new(merge_request: self).message + return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self).merge_message end closes_issues_references = visible_closing_issues_for.map do |issue| @@ -1340,6 +1340,10 @@ class MergeRequest < ApplicationRecord end def default_squash_commit_message + if self.target_project.squash_commit_template.present? + return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self).squash_message + end + title end diff --git a/app/models/project.rb b/app/models/project.rb index a4e84b412ec..937e6f9c2d7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -452,6 +452,7 @@ class Project < ApplicationRecord to: :project_setting delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true + delegate :squash_commit_template, :squash_commit_template=, to: :project_setting, allow_nil: true delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb index 6c8d2226bc9..fc834286876 100644 --- a/app/models/project_setting.rb +++ b/app/models/project_setting.rb @@ -13,6 +13,7 @@ class ProjectSetting < ApplicationRecord self.primary_key = :project_id validates :merge_commit_template, length: { maximum: 500 } + validates :squash_commit_template, length: { maximum: 500 } def squash_enabled_by_default? %w[always default_on].include?(squash_option) diff --git a/app/views/projects/_merge_request_merge_commit_template.html.haml b/app/views/projects/_merge_request_merge_commit_template.html.haml index 185b730e0bb..ba170af9de5 100644 --- a/app/views/projects/_merge_request_merge_commit_template.html.haml +++ b/app/views/projects/_merge_request_merge_commit_template.html.haml @@ -12,6 +12,6 @@ %p.form-text.text-muted = s_('ProjectSettings|Maximum 500 characters.') = s_('ProjectSettings|Supported variables:') - - Gitlab::MergeRequests::MergeCommitMessage::PLACEHOLDERS.keys.each do |placeholder| + - Gitlab::MergeRequests::CommitMessageGenerator::PLACEHOLDERS.keys.each do |placeholder| %code = "%{#{placeholder}}".html_safe diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml index c5a25bec6eb..728ff597860 100644 --- a/app/views/projects/_merge_request_settings.html.haml +++ b/app/views/projects/_merge_request_settings.html.haml @@ -12,5 +12,7 @@ = render 'projects/merge_request_merge_commit_template', project: @project, form: form += render 'projects/merge_request_squash_commit_template', project: @project, form: form + - if @project.forked? = render 'projects/merge_request_target_project_settings', project: @project, form: form diff --git a/app/views/projects/_merge_request_squash_commit_template.html.haml b/app/views/projects/_merge_request_squash_commit_template.html.haml new file mode 100644 index 00000000000..51617bc027f --- /dev/null +++ b/app/views/projects/_merge_request_squash_commit_template.html.haml @@ -0,0 +1,16 @@ +- form = local_assigns.fetch(:form) + +.form-group + %b= s_('ProjectSettings|Squash commit message template') + %p.text-secondary + - configure_the_squash_commit_message_help_link_url = help_page_path('user/project/merge_requests/commit_templates.md', anchor: 'squash-commit-message-template') + - configure_the_squash_commit_message_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: configure_the_squash_commit_message_help_link_url } + = s_('ProjectSettings|The commit message used when squashing commits. %{link_start}Learn more about syntax and variables.%{link_end}').html_safe % { link_start: configure_the_squash_commit_message_help_link_start, link_end: '</a>'.html_safe } + .mb-2 + = form.text_area :squash_commit_template, class: 'form-control gl-form-input', rows: 8, maxlength: 500, placeholder: '%{title}' + %p.form-text.text-muted + = s_('ProjectSettings|Maximum 500 characters.') + = s_('ProjectSettings|Supported variables:') + - Gitlab::MergeRequests::CommitMessageGenerator::PLACEHOLDERS.keys.each do |placeholder| + %code + = "%{#{placeholder}}".html_safe diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 2627552058b..9e3d9b4258a 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -9,6 +9,9 @@ %td.status = render 'ci/status/badge', status: generic_commit_status.detailed_status(current_user) + %td + = generic_commit_status.name + %td.generic-commit-status-link - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url = link_to generic_commit_status.target_url do @@ -66,9 +69,6 @@ = generic_commit_status.stage %td - = generic_commit_status.name - - %td - if generic_commit_status.duration %p.duration = custom_icon("icon_timer") diff --git a/db/migrate/20211111164025_add_squash_commit_template_to_project_settings.rb b/db/migrate/20211111164025_add_squash_commit_template_to_project_settings.rb new file mode 100644 index 00000000000..6120a6ed0b4 --- /dev/null +++ b/db/migrate/20211111164025_add_squash_commit_template_to_project_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddSquashCommitTemplateToProjectSettings < Gitlab::Database::Migration[1.0] + enable_lock_retries! + + def change + add_column :project_settings, :squash_commit_template, :text # rubocop:disable Migration/AddLimitToTextColumns + end +end diff --git a/db/migrate/20211111164047_add_squash_commit_template_limit_to_project_settings.rb b/db/migrate/20211111164047_add_squash_commit_template_limit_to_project_settings.rb new file mode 100644 index 00000000000..578d2271d60 --- /dev/null +++ b/db/migrate/20211111164047_add_squash_commit_template_limit_to_project_settings.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class AddSquashCommitTemplateLimitToProjectSettings < Gitlab::Database::Migration[1.0] + disable_ddl_transaction! + + def up + add_text_limit :project_settings, :squash_commit_template, 500 + end + + def down + remove_text_limit :project_settings, :squash_commit_template + end +end diff --git a/db/schema_migrations/20211111164025 b/db/schema_migrations/20211111164025 new file mode 100644 index 00000000000..409cc160b9e --- /dev/null +++ b/db/schema_migrations/20211111164025 @@ -0,0 +1 @@ +d78fe687517e14ff67dc76eff63391e33b73d29446d2a0445595175c7cd6806a
\ No newline at end of file diff --git a/db/schema_migrations/20211111164047 b/db/schema_migrations/20211111164047 new file mode 100644 index 00000000000..30e0875cf73 --- /dev/null +++ b/db/schema_migrations/20211111164047 @@ -0,0 +1 @@ +c8ed7f8c0f818156dba9c25be848da97d4eb6dbf0aa9c48f87e940f3ca0967d9
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 3db0f0c14e9..1086961ab41 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -18299,7 +18299,9 @@ CREATE TABLE project_settings ( warn_about_potentially_unwanted_characters boolean DEFAULT true NOT NULL, merge_commit_template text, has_shimo boolean DEFAULT false NOT NULL, + squash_commit_template text, CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)), + CONSTRAINT check_b09644994b CHECK ((char_length(squash_commit_template) <= 500)), CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)), CONSTRAINT check_eaf7cfb6a7 CHECK ((char_length(merge_commit_template) <= 500)) ); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 0400dc31128..d49eee5d15c 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -12867,6 +12867,7 @@ Represents vulnerability finding of a security report on the pipeline. | <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has service desk enabled. | | <a id="projectsharedrunnersenabled"></a>`sharedRunnersEnabled` | [`Boolean`](#boolean) | Indicates if shared runners are enabled for the project. | | <a id="projectsnippetsenabled"></a>`snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user. | +| <a id="projectsquashcommittemplate"></a>`squashCommitTemplate` | [`String`](#string) | Template used to create squash commit message in merge requests. | | <a id="projectsquashreadonly"></a>`squashReadOnly` | [`Boolean!`](#boolean) | Indicates if `squashReadOnly` is enabled. | | <a id="projectsshurltorepo"></a>`sshUrlToRepo` | [`String`](#string) | URL to connect to the project via SSH. | | <a id="projectstarcount"></a>`starCount` | [`Int!`](#int) | Number of times the project has been starred. | diff --git a/doc/api/group_protected_environments.md b/doc/api/group_protected_environments.md index 0e1cd149c51..6e6b1ffddc3 100644 --- a/doc/api/group_protected_environments.md +++ b/doc/api/group_protected_environments.md @@ -103,7 +103,7 @@ POST /groups/:id/protected_environments | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) maintained by the authenticated user. | | `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).| -| `deploy_access_levels` | array | yes | Array of access levels allowed to deploy, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. | +| `deploy_access_levels` | array | yes | Array of access levels allowed to deploy, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. Here, `group_id` must be of a sub-group of the protecting group.| The assignable `user_id` are the users who belong to the given group with the Maintainer role (or above). The assignable `group_id` are the sub-groups under the given group. diff --git a/doc/api/projects.md b/doc/api/projects.md index 09fb22942e1..6d4aa3033f3 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -183,6 +183,7 @@ When the user is authenticated and `simple` is not set this returns something li "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on "marked_for_deletion_on": "2020-04-03", "statistics": { @@ -300,6 +301,7 @@ When the user is authenticated and `simple` is not set this returns something li "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "statistics": { "commit_count": 12, "storage_size": 2066080, @@ -470,6 +472,7 @@ GET /users/:user_id/projects "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on "marked_for_deletion_on": "2020-04-03", "statistics": { @@ -587,6 +590,7 @@ GET /users/:user_id/projects "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "statistics": { "commit_count": 12, "storage_size": 2066080, @@ -714,6 +718,7 @@ Example response: "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "statistics": { "commit_count": 37, "storage_size": 1038090, @@ -826,6 +831,7 @@ Example response: "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "statistics": { "commit_count": 12, "storage_size": 2066080, @@ -994,6 +1000,7 @@ GET /projects/:id "autoclose_referenced_issues": true, "suggestion_commit_message": null, "merge_commit_template": null, + "squash_commit_template": null, "marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on "marked_for_deletion_on": "2020-04-03", "compliance_frameworks": [ "sox" ], @@ -1307,6 +1314,7 @@ POST /projects/user/:user_id | `jobs_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable jobs for this project. Use `builds_access_level` instead. | | `lfs_enabled` | boolean | **{dotted-circle}** No | Enable LFS. | | `merge_commit_template` | string | **{dotted-circle}** No | [Template](../user/project/merge_requests/commit_templates.md) used to create merge commit message in merge requests. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20263) in GitLab 14.5.)_ | +| `squash_commit_template` | string | **{dotted-circle}** No | [Template](../user/project/merge_requests/commit_templates.md#squash-commit-message-template) used to create squash commit message in merge requests. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345275) in GitLab 14.6.)_ | | `merge_method` | string | **{dotted-circle}** No | Set the [merge method](#project-merge-method) used. | | `merge_requests_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. | | `merge_requests_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable merge requests for this project. Use `merge_requests_access_level` instead. | diff --git a/doc/ci/large_repositories/index.md b/doc/ci/large_repositories/index.md index fe4af9421db..f3044a03e04 100644 --- a/doc/ci/large_repositories/index.md +++ b/doc/ci/large_repositories/index.md @@ -260,4 +260,4 @@ For very active repositories with a large number of references and files, you ca enabled on all Gitaly servers, we found that we no longer need a pre-clone step for `gitlab-org/gitlab` development. - Optimize your CI/CD jobs by seeding repository data in a pre-clone step with the [`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) of GitLab Runner. See - [SaaS runners on Linux](../runners/runner_cloud/linux_runner_cloud.md#pre-clone-script) for details. + [SaaS runners on Linux](../runners/saas/linux_saas_runner.md#pre-clone-script) for details. diff --git a/doc/ci/runners/build_cloud/linux_build_cloud.md b/doc/ci/runners/build_cloud/linux_build_cloud.md index e8bad31c821..2892a30cd2e 100644 --- a/doc/ci/runners/build_cloud/linux_build_cloud.md +++ b/doc/ci/runners/build_cloud/linux_build_cloud.md @@ -1,9 +1,9 @@ --- -redirect_to: '../runner_cloud/linux_runner_cloud.md' +redirect_to: '../saas/linux_saas_runner.md' remove_date: '2022-02-05' --- -This document was moved to [another location](../runner_cloud/linux_runner_cloud.md). +This document was moved to [another location](../saas/linux_saas_runner.md). <!-- This redirect file can be deleted after 2022-02-05. --> <!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/build_cloud/macos/environment.md b/doc/ci/runners/build_cloud/macos/environment.md index aaef0d07098..a534e87cc34 100644 --- a/doc/ci/runners/build_cloud/macos/environment.md +++ b/doc/ci/runners/build_cloud/macos/environment.md @@ -1,9 +1,9 @@ --- -redirect_to: '../../runner_cloud/macos/environment.md' +redirect_to: '../../saas/macos/environment.md' remove_date: '2022-02-05' --- -This document was moved to [another location](../../runner_cloud/macos/environment.md). +This document was moved to [another location](../../saas/macos/environment.md). <!-- This redirect file can be deleted after 2022-02-05. --> <!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/build_cloud/macos_build_cloud.md b/doc/ci/runners/build_cloud/macos_build_cloud.md index e478f93f34c..50b7e0cfb79 100644 --- a/doc/ci/runners/build_cloud/macos_build_cloud.md +++ b/doc/ci/runners/build_cloud/macos_build_cloud.md @@ -1,9 +1,9 @@ --- -redirect_to: '../runner_cloud/macos_runner_cloud.md' +redirect_to: '../saas/macos_saas_runner.md' remove_date: '2022-02-05' --- -This document was moved to [another location](../runner_cloud/macos_runner_cloud.md). +This document was moved to [another location](../saas/macos_saas_runner.md). <!-- This redirect file can be deleted after 2022-02-05. --> <!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/build_cloud/windows_build_cloud.md b/doc/ci/runners/build_cloud/windows_build_cloud.md index 8d57ecf27ed..fb64938eb9f 100644 --- a/doc/ci/runners/build_cloud/windows_build_cloud.md +++ b/doc/ci/runners/build_cloud/windows_build_cloud.md @@ -1,9 +1,9 @@ --- -redirect_to: '../runner_cloud/windows_runner_cloud.md' +redirect_to: '../saas/windows_saas_runner.md' remove_date: '2022-02-05' --- -This document was moved to [another location](../runner_cloud/windows_runner_cloud.md). +This document was moved to [another location](../saas/windows_saas_runner.md). <!-- This redirect file can be deleted after 2022-02-05. --> <!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/runner_cloud/linux_runner_cloud.md b/doc/ci/runners/runner_cloud/linux_runner_cloud.md index bda495009e2..2892a30cd2e 100644 --- a/doc/ci/runners/runner_cloud/linux_runner_cloud.md +++ b/doc/ci/runners/runner_cloud/linux_runner_cloud.md @@ -1,186 +1,9 @@ --- -stage: Verify -group: Runner -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 +redirect_to: '../saas/linux_saas_runner.md' +remove_date: '2022-02-05' --- -# SaaS runners on Linux **(FREE SAAS)** +This document was moved to [another location](../saas/linux_saas_runner.md). -SaaS runners on Linux are autoscaled ephemeral Google Cloud Platform virtual machines. - -Autoscaling means reduced queue times to spin up CI/CD jobs, and isolated VMs for each job, thus maximizing security. These shared runners are available on GitLab.com. - -GitLab offers Ultimate tier capabilities and included CI/CD minutes per group per month for our [Open Source](https://about.gitlab.com/solutions/open-source/join/), [Education](https://about.gitlab.com/solutions/education/), and [Startups](https://about.gitlab.com/solutions/startups/) programs. For private projects, GitLab offers various [plans](https://about.gitlab.com/pricing/), starting with a Free tier. - -All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, Google COS and the latest Docker Engine -installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default -region of the VMs is US East1. -Each instance is used only for one job. This ensures that any sensitive data left on the system can't be accessed by other people's CI/CD jobs. - -NOTE: -The final disk space your jobs can use will be less than 25GB. Some disk space allocated to the instance will be occupied by the operating system, the Docker image, and a copy of your cloned repository. - -The `gitlab-shared-runners-manager-X.gitlab.com` fleet of runners are dedicated for GitLab projects as well as community forks of them. They use a slightly larger machine type (n1-standard-2) and have a bigger SSD disk size. They don't run untagged jobs and unlike the general fleet of shared runners, the instances are re-used up to 40 times. - -Jobs handled by the shared runners on GitLab.com (`shared-runners-manager-X.gitlab.com`), -**time out after 3 hours**, regardless of the timeout configured in a -project. Check the issues [#4010](https://gitlab.com/gitlab-com/infrastructure/-/issues/4010) and [#4070](https://gitlab.com/gitlab-com/infrastructure/-/issues/4070) for the reference. - -Below are the runners' settings. - -| Setting | GitLab.com | Default | -| ----------- | ----------------- | ---------- | -| Executor | `docker+machine` | - | -| Default Docker image | `ruby:2.5` | - | -| `privileged` (run [Docker in Docker](https://hub.docker.com/_/docker/)) | `true` | `false` | - -These runners share a [distributed cache](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) through use of a Google Cloud Storage (GCS) bucket. Cache contents not updated within the last 14 days are automatically removed through use of an [object lifecycle management policy](https://cloud.google.com/storage/docs/lifecycle). - -## Pre-clone script - -With SaaS runners on Linux, you can run commands in a CI -job before the runner attempts to run `git init` and `git fetch` to -download a GitLab repository. The -[`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) -can be used for: - -- Seeding the build directory with repository data -- Sending a request to a server -- Downloading assets from a CDN -- Any other commands that must run before the `git init` - -To use this feature, define a [CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables) called -`CI_PRE_CLONE_SCRIPT` that contains a bash script. - -NOTE: -The `CI_PRE_CLONE_SCRIPT` variable does not work on Windows runners. - -### Pre-clone script example - -This example was used in the `gitlab-org/gitlab` project until November 2021. -The project no longer uses this optimization because the [pack-objects cache](../../../administration/gitaly/configure_gitaly.md#pack-objects-cache) -lets Gitaly serve the full CI/CD fetch traffic. See [Git fetch caching](../../../development/pipelines.md#git-fetch-caching). - -The `CI_PRE_CLONE_SCRIPT` was defined as a project CI/CD variable: - -```shell -( - echo "Downloading archived master..." - wget -O /tmp/gitlab.tar.gz https://storage.googleapis.com/gitlab-ci-git-repo-cache/project-278964/gitlab-master-shallow.tar.gz - - if [ ! -f /tmp/gitlab.tar.gz ]; then - echo "Repository cache not available, cloning a new directory..." - exit - fi - - rm -rf $CI_PROJECT_DIR - echo "Extracting tarball into $CI_PROJECT_DIR..." - mkdir -p $CI_PROJECT_DIR - cd $CI_PROJECT_DIR - tar xzf /tmp/gitlab.tar.gz - rm -f /tmp/gitlab.tar.gz - chmod a+w $CI_PROJECT_DIR -) -``` - -The first step of the script downloads `gitlab-master.tar.gz` from Google Cloud Storage. -There was a [GitLab CI/CD job named `cache-repo`](https://gitlab.com/gitlab-org/gitlab/-/blob/5fb40526c8c8aaafc5f92eab36d5bbddaca3893d/.gitlab/ci/cache-repo.gitlab-ci.yml) -that was responsible for keeping that archive up-to-date. Every two hours on a scheduled pipeline, -it did the following: - -1. Create a fresh clone of the `gitlab-org/gitlab` repository on GitLab.com. -1. Save the data as a `.tar.gz`. -1. Upload it into the Google Cloud Storage bucket. - -When a job ran with this configuration, the output looked similar to: - -```shell -$ eval "$CI_PRE_CLONE_SCRIPT" -Downloading archived master... -Extracting tarball into /builds/gitlab-org/gitlab... -Fetching changes... -Reinitialized existing Git repository in /builds/gitlab-org/gitlab/.git/ -``` - -The `Reinitialized existing Git repository` message shows that -the pre-clone step worked. The runner runs `git init`, which -overwrites the Git configuration with the appropriate settings to fetch -from the GitLab repository. - -`CI_REPO_CACHE_CREDENTIALS` must contain the Google Cloud service account -JSON for uploading to the `gitlab-ci-git-repo-cache` bucket. - -Note that this bucket should be located in the same continent as the -runner, or [you can incur network egress charges](https://cloud.google.com/storage/pricing). - -## `config.toml` - -The full contents of our `config.toml` are: - -NOTE: -Settings that are not public are shown as `X`. - -**Google Cloud Platform** - -```toml -concurrent = X -check_interval = 1 -metrics_server = "X" -sentry_dsn = "X" - -[[runners]] - name = "docker-auto-scale" - request_concurrency = X - url = "https://gitlab.com/" - token = "SHARED_RUNNER_TOKEN" - pre_clone_script = "eval \"$CI_PRE_CLONE_SCRIPT\"" - executor = "docker+machine" - environment = [ - "DOCKER_DRIVER=overlay2", - "DOCKER_TLS_CERTDIR=" - ] - limit = X - [runners.docker] - image = "ruby:2.5" - privileged = true - volumes = [ - "/certs/client", - "/dummy-sys-class-dmi-id:/sys/class/dmi/id:ro" # Make kaniko builds work on GCP. - ] - [runners.machine] - IdleCount = 50 - IdleTime = 3600 - MaxBuilds = 1 # For security reasons we delete the VM after job has finished so it's not reused. - MachineName = "srm-%s" - MachineDriver = "google" - MachineOptions = [ - "google-project=PROJECT", - "google-disk-size=25", - "google-machine-type=n1-standard-1", - "google-username=core", - "google-tags=gitlab-com,srm", - "google-use-internal-ip", - "google-zone=us-east1-d", - "engine-opt=mtu=1460", # Set MTU for container interface, for more information check https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3214#note_82892928 - "google-machine-image=PROJECT/global/images/IMAGE", - "engine-opt=ipv6", # This will create IPv6 interfaces in the containers. - "engine-opt=fixed-cidr-v6=fc00::/7", - "google-operation-backoff-initial-interval=2" # Custom flag from forked docker-machine, for more information check https://github.com/docker/machine/pull/4600 - ] - [[runners.machine.autoscaling]] - Periods = ["* * * * * sat,sun *"] - Timezone = "UTC" - IdleCount = 70 - IdleTime = 3600 - [[runners.machine.autoscaling]] - Periods = ["* 30-59 3 * * * *", "* 0-30 4 * * * *"] - Timezone = "UTC" - IdleCount = 700 - IdleTime = 3600 - [runners.cache] - Type = "gcs" - Shared = true - [runners.cache.gcs] - CredentialsFile = "/path/to/file" - BucketName = "bucket-name" -``` +<!-- This redirect file can be deleted after 2022-02-05. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/runner_cloud/macos/environment.md b/doc/ci/runners/runner_cloud/macos/environment.md index 3332eab9b44..37ad21c28fc 100644 --- a/doc/ci/runners/runner_cloud/macos/environment.md +++ b/doc/ci/runners/runner_cloud/macos/environment.md @@ -1,43 +1,9 @@ --- -stage: Verify -group: Runner -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 +redirect_to: '../../saas/macos/environment.md' +remove_date: '2022-02-05' --- -# VM instances and images for SaaS runners on macOS **(FREE SAAS)** +This document was moved to [another location](../../saas/macos/environment.md). -When you use SaaS runners on macOS: - -- Each of your jobs runs in a newly provisioned VM, which is dedicated to the specific job. -- The VM is active only for the duration of the job and immediately deleted. - -## VM types - -The virtual machine where your job runs has `sudo` access with no password. -For the Beta, there is only one available machine type, `gbc-macos-large`. - -| Instance type | vCPUS | Memory (GB) | -| --------- | --- | ------- | -| `gbc-macos-large` | 4 | 10 | - -## VM images - -You can execute your build on one of the following images. -You specify this image in your `.gitlab-ci.yml` file. - -Each image is running a specific version of macOS and Xcode. - -| VM image | Included software | -|---------------------------|--------------------| -| macos-10.13-xcode-7 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/high-sierra.yml> | -| macos-10.13-xcode-8 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/high-sierra.yml> | -| macos-10.13-xcode-9 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/high-sierra.yml> | -| macos-10.14-xcode-10 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/mojave.yml> | -| macos-10.15-xcode-11 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/catalina.yml> | -| macos-11-xcode-12 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/big-sur.yml> | - -### Image update policy - -- Support for new macOS versions is planned. -- Additional details on the support policy and image update release process are documented - [in this project](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/55bf59c8fa88712960afff2bf6ecc5daa879a8f5/docs/overview.md#os-images). +<!-- This redirect file can be deleted after 2022-02-05. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
\ No newline at end of file diff --git a/doc/ci/runners/runner_cloud/macos_runner_cloud.md b/doc/ci/runners/runner_cloud/macos_runner_cloud.md index 40c4deb51aa..50b7e0cfb79 100644 --- a/doc/ci/runners/runner_cloud/macos_runner_cloud.md +++ b/doc/ci/runners/runner_cloud/macos_runner_cloud.md @@ -1,63 +1,9 @@ --- -stage: Verify -group: Runner -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 +redirect_to: '../saas/macos_saas_runner.md' +remove_date: '2022-02-05' --- -# SaaS runners on macOS (Beta) **(FREE SAAS)** +This document was moved to [another location](../saas/macos_saas_runner.md). -SaaS runners on macOS provide an on-demand macOS build environment integrated with -GitLab SaaS [CI/CD](../../../ci/index.md). -Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS, iOS, tvOS). You can take advantage -of all the capabilities of the GitLab single DevOps platform and not have to manage or operate a -build environment. - -SaaS runners on macOS are in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) -and shouldn't be relied upon for mission-critical production jobs. - -## Quickstart - -To start using SaaS runners on macOS, you must submit an access request [issue](https://gitlab.com/gitlab-com/macos-buildcloud-runners-beta/-/issues/new?issuable_template=beta_access_request). After your -access has been granted and your build environment configured, you must configure your -`.gitlab-ci.yml` pipeline file: - -1. Add a `.gitlab-ci.yml` file to your project repository. -1. Specify the [image](macos/environment.md#vm-images) you want to use. -1. Commit a change to your repository. - -The runners automatically run your build. - -## Example `.gitlab-ci.yml` file - -The following sample `.gitlab-ci.yml` file shows how to start using the SaaS runners on macOS: - -```yaml -.macos_saas_runners: - tags: - - shared-macos-amd64 - image: macos-11-xcode-12 - -stages: - - build - - test - -before_script: - - echo "started by ${GITLAB_USER_NAME}" - -build: - extends: - - .macos_saas_runners - stage: build - script: - - echo "running scripts in the build job" - -test: - extends: - - .macos_saas_runners - stage: test - script: - - echo "running scripts in the test job" -``` - -NOTE: -During the Beta period, the architecture of this solution will change. Rather than the jobs running on a specific VM instance, they will run on an ephemeral VM instance that is created by an autoscaling instance, known as the Runner Manager. We will notify all Beta participants of any downtime required to do this work. +<!-- This redirect file can be deleted after 2022-02-05. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/runner_cloud/windows_runner_cloud.md b/doc/ci/runners/runner_cloud/windows_runner_cloud.md index 87ee542fb14..fb64938eb9f 100644 --- a/doc/ci/runners/runner_cloud/windows_runner_cloud.md +++ b/doc/ci/runners/runner_cloud/windows_runner_cloud.md @@ -1,155 +1,9 @@ --- -stage: Verify -group: Runner -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 +redirect_to: '../saas/windows_saas_runner.md' +remove_date: '2022-02-05' --- -# SaaS runners on Windows (beta) **(FREE SAAS)** +This document was moved to [another location](../saas/windows_saas_runner.md). -SaaS runners on Windows are in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) -and shouldn't be used for production workloads. - -During this beta period, the [shared runner pipeline quota](../../../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota) -applies for groups and projects in the same manner as Linux runners. This may -change when the beta period ends, as discussed in this [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/30834). - -Windows runners on GitLab.com autoscale by launching virtual machines on -the Google Cloud Platform. This solution uses an -[autoscaling driver](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/tree/master/docs/readme.md) -developed by GitLab for the [custom executor](https://docs.gitlab.com/runner/executors/custom.html). -Windows runners execute your CI/CD jobs on `n1-standard-2` instances with -2 vCPUs and 7.5 GB RAM. You can find a full list of available Windows packages in -the [package documentation](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/blob/main/cookbooks/preinstalled-software/README.md). - -We want to keep iterating to get Windows runners in a stable state and -[generally available](https://about.gitlab.com/handbook/product/gitlab-the-product/#generally-available-ga). -You can follow our work towards this goal in the -[related epic](https://gitlab.com/groups/gitlab-org/-/epics/2162). - -## Configuration - -The full contents of our `config.toml` are: - -NOTE: -Settings that aren't public are shown as `X`. - -```toml -concurrent = X -check_interval = 3 - -[[runners]] - name = "windows-runner" - url = "https://gitlab.com/" - token = "TOKEN" - executor = "custom" - builds_dir = "C:\\GitLab-Runner\\builds" - cache_dir = "C:\\GitLab-Runner\\cache" - shell = "powershell" - [runners.custom] - config_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" - config_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "config"] - prepare_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" - prepare_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "prepare"] - run_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" - run_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "run"] - cleanup_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" - cleanup_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "cleanup"] -``` - -The full contents of our `autoscaler/config.toml` are: - -```toml -Provider = "gcp" -Executor = "winrm" -OS = "windows" -LogLevel = "info" -LogFormat = "text" -LogFile = "C:\\GitLab-Runner\\autoscaler\\autoscaler.log" -VMTag = "windows" - -[GCP] - ServiceAccountFile = "PATH" - Project = "some-project-df9323" - Zone = "us-east1-c" - MachineType = "n1-standard-2" - Image = "IMAGE" - DiskSize = 50 - DiskType = "pd-standard" - Subnetwork = "default" - Network = "default" - Tags = ["TAGS"] - Username = "gitlab_runner" - -[WinRM] - MaximumTimeout = 3600 - ExecutionMaxRetries = 0 - -[ProviderCache] - Enabled = true - Directory = "C:\\GitLab-Runner\\autoscaler\\machines" -``` - -## Example `.gitlab-ci.yml` file - -Below is a sample `.gitlab-ci.yml` file that shows how to start using the runners for Windows: - -```yaml -.shared_windows_runners: - tags: - - shared-windows - - windows - - windows-1809 - -stages: - - build - - test - -before_script: - - Set-Variable -Name "time" -Value (date -Format "%H:%m") - - echo ${time} - - echo "started by ${GITLAB_USER_NAME}" - -build: - extends: - - .shared_windows_runners - stage: build - script: - - echo "running scripts in the build job" - -test: - extends: - - .shared_windows_runners - stage: test - script: - - echo "running scripts in the test job" -``` - -## Limitations and known issues - -- All the limitations mentioned in our [beta - definition](https://about.gitlab.com/handbook/product/#beta). -- The average provisioning time for a new Windows VM is 5 minutes. - This means that you may notice slower build start times - on the Windows runner fleet during the beta. In a future - release we intend to update the autoscaler to enable - the pre-provisioning of virtual machines. This is intended to significantly reduce - the time it takes to provision a VM on the Windows fleet. You can - follow along in the [related issue](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/-/issues/32). -- The Windows runner fleet may be unavailable occasionally - for maintenance or updates. -- The Windows runner virtual machine instances do not use the - GitLab Docker executor. This means that you can't specify - [`image`](../../../ci/yaml/index.md#image) or [`services`](../../../ci/yaml/index.md#services) in - your pipeline configuration. -- For the beta release, we have included a set of software packages in - the base VM image. If your CI job requires additional software that's - not included in this list, then you must add installation - commands to [`before_script`](../../../ci/yaml/index.md#before_script) or [`script`](../../../ci/yaml/index.md#script) to install the required - software. Note that each job runs on a new VM instance, so the - installation of additional software packages needs to be repeated for - each job in your pipeline. -- The job may stay in a pending state for longer than the - Linux runners. -- There is the possibility that we introduce breaking changes which will - require updates to pipelines that are using the Windows runner - fleet. +<!-- This redirect file can be deleted after 2022-02-05. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/ci/runners/saas/linux_saas_runner.md b/doc/ci/runners/saas/linux_saas_runner.md new file mode 100644 index 00000000000..bda495009e2 --- /dev/null +++ b/doc/ci/runners/saas/linux_saas_runner.md @@ -0,0 +1,186 @@ +--- +stage: Verify +group: Runner +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 +--- + +# SaaS runners on Linux **(FREE SAAS)** + +SaaS runners on Linux are autoscaled ephemeral Google Cloud Platform virtual machines. + +Autoscaling means reduced queue times to spin up CI/CD jobs, and isolated VMs for each job, thus maximizing security. These shared runners are available on GitLab.com. + +GitLab offers Ultimate tier capabilities and included CI/CD minutes per group per month for our [Open Source](https://about.gitlab.com/solutions/open-source/join/), [Education](https://about.gitlab.com/solutions/education/), and [Startups](https://about.gitlab.com/solutions/startups/) programs. For private projects, GitLab offers various [plans](https://about.gitlab.com/pricing/), starting with a Free tier. + +All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, Google COS and the latest Docker Engine +installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default +region of the VMs is US East1. +Each instance is used only for one job. This ensures that any sensitive data left on the system can't be accessed by other people's CI/CD jobs. + +NOTE: +The final disk space your jobs can use will be less than 25GB. Some disk space allocated to the instance will be occupied by the operating system, the Docker image, and a copy of your cloned repository. + +The `gitlab-shared-runners-manager-X.gitlab.com` fleet of runners are dedicated for GitLab projects as well as community forks of them. They use a slightly larger machine type (n1-standard-2) and have a bigger SSD disk size. They don't run untagged jobs and unlike the general fleet of shared runners, the instances are re-used up to 40 times. + +Jobs handled by the shared runners on GitLab.com (`shared-runners-manager-X.gitlab.com`), +**time out after 3 hours**, regardless of the timeout configured in a +project. Check the issues [#4010](https://gitlab.com/gitlab-com/infrastructure/-/issues/4010) and [#4070](https://gitlab.com/gitlab-com/infrastructure/-/issues/4070) for the reference. + +Below are the runners' settings. + +| Setting | GitLab.com | Default | +| ----------- | ----------------- | ---------- | +| Executor | `docker+machine` | - | +| Default Docker image | `ruby:2.5` | - | +| `privileged` (run [Docker in Docker](https://hub.docker.com/_/docker/)) | `true` | `false` | + +These runners share a [distributed cache](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) through use of a Google Cloud Storage (GCS) bucket. Cache contents not updated within the last 14 days are automatically removed through use of an [object lifecycle management policy](https://cloud.google.com/storage/docs/lifecycle). + +## Pre-clone script + +With SaaS runners on Linux, you can run commands in a CI +job before the runner attempts to run `git init` and `git fetch` to +download a GitLab repository. The +[`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) +can be used for: + +- Seeding the build directory with repository data +- Sending a request to a server +- Downloading assets from a CDN +- Any other commands that must run before the `git init` + +To use this feature, define a [CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables) called +`CI_PRE_CLONE_SCRIPT` that contains a bash script. + +NOTE: +The `CI_PRE_CLONE_SCRIPT` variable does not work on Windows runners. + +### Pre-clone script example + +This example was used in the `gitlab-org/gitlab` project until November 2021. +The project no longer uses this optimization because the [pack-objects cache](../../../administration/gitaly/configure_gitaly.md#pack-objects-cache) +lets Gitaly serve the full CI/CD fetch traffic. See [Git fetch caching](../../../development/pipelines.md#git-fetch-caching). + +The `CI_PRE_CLONE_SCRIPT` was defined as a project CI/CD variable: + +```shell +( + echo "Downloading archived master..." + wget -O /tmp/gitlab.tar.gz https://storage.googleapis.com/gitlab-ci-git-repo-cache/project-278964/gitlab-master-shallow.tar.gz + + if [ ! -f /tmp/gitlab.tar.gz ]; then + echo "Repository cache not available, cloning a new directory..." + exit + fi + + rm -rf $CI_PROJECT_DIR + echo "Extracting tarball into $CI_PROJECT_DIR..." + mkdir -p $CI_PROJECT_DIR + cd $CI_PROJECT_DIR + tar xzf /tmp/gitlab.tar.gz + rm -f /tmp/gitlab.tar.gz + chmod a+w $CI_PROJECT_DIR +) +``` + +The first step of the script downloads `gitlab-master.tar.gz` from Google Cloud Storage. +There was a [GitLab CI/CD job named `cache-repo`](https://gitlab.com/gitlab-org/gitlab/-/blob/5fb40526c8c8aaafc5f92eab36d5bbddaca3893d/.gitlab/ci/cache-repo.gitlab-ci.yml) +that was responsible for keeping that archive up-to-date. Every two hours on a scheduled pipeline, +it did the following: + +1. Create a fresh clone of the `gitlab-org/gitlab` repository on GitLab.com. +1. Save the data as a `.tar.gz`. +1. Upload it into the Google Cloud Storage bucket. + +When a job ran with this configuration, the output looked similar to: + +```shell +$ eval "$CI_PRE_CLONE_SCRIPT" +Downloading archived master... +Extracting tarball into /builds/gitlab-org/gitlab... +Fetching changes... +Reinitialized existing Git repository in /builds/gitlab-org/gitlab/.git/ +``` + +The `Reinitialized existing Git repository` message shows that +the pre-clone step worked. The runner runs `git init`, which +overwrites the Git configuration with the appropriate settings to fetch +from the GitLab repository. + +`CI_REPO_CACHE_CREDENTIALS` must contain the Google Cloud service account +JSON for uploading to the `gitlab-ci-git-repo-cache` bucket. + +Note that this bucket should be located in the same continent as the +runner, or [you can incur network egress charges](https://cloud.google.com/storage/pricing). + +## `config.toml` + +The full contents of our `config.toml` are: + +NOTE: +Settings that are not public are shown as `X`. + +**Google Cloud Platform** + +```toml +concurrent = X +check_interval = 1 +metrics_server = "X" +sentry_dsn = "X" + +[[runners]] + name = "docker-auto-scale" + request_concurrency = X + url = "https://gitlab.com/" + token = "SHARED_RUNNER_TOKEN" + pre_clone_script = "eval \"$CI_PRE_CLONE_SCRIPT\"" + executor = "docker+machine" + environment = [ + "DOCKER_DRIVER=overlay2", + "DOCKER_TLS_CERTDIR=" + ] + limit = X + [runners.docker] + image = "ruby:2.5" + privileged = true + volumes = [ + "/certs/client", + "/dummy-sys-class-dmi-id:/sys/class/dmi/id:ro" # Make kaniko builds work on GCP. + ] + [runners.machine] + IdleCount = 50 + IdleTime = 3600 + MaxBuilds = 1 # For security reasons we delete the VM after job has finished so it's not reused. + MachineName = "srm-%s" + MachineDriver = "google" + MachineOptions = [ + "google-project=PROJECT", + "google-disk-size=25", + "google-machine-type=n1-standard-1", + "google-username=core", + "google-tags=gitlab-com,srm", + "google-use-internal-ip", + "google-zone=us-east1-d", + "engine-opt=mtu=1460", # Set MTU for container interface, for more information check https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3214#note_82892928 + "google-machine-image=PROJECT/global/images/IMAGE", + "engine-opt=ipv6", # This will create IPv6 interfaces in the containers. + "engine-opt=fixed-cidr-v6=fc00::/7", + "google-operation-backoff-initial-interval=2" # Custom flag from forked docker-machine, for more information check https://github.com/docker/machine/pull/4600 + ] + [[runners.machine.autoscaling]] + Periods = ["* * * * * sat,sun *"] + Timezone = "UTC" + IdleCount = 70 + IdleTime = 3600 + [[runners.machine.autoscaling]] + Periods = ["* 30-59 3 * * * *", "* 0-30 4 * * * *"] + Timezone = "UTC" + IdleCount = 700 + IdleTime = 3600 + [runners.cache] + Type = "gcs" + Shared = true + [runners.cache.gcs] + CredentialsFile = "/path/to/file" + BucketName = "bucket-name" +``` diff --git a/doc/ci/runners/saas/macos/environment.md b/doc/ci/runners/saas/macos/environment.md new file mode 100644 index 00000000000..3332eab9b44 --- /dev/null +++ b/doc/ci/runners/saas/macos/environment.md @@ -0,0 +1,43 @@ +--- +stage: Verify +group: Runner +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 +--- + +# VM instances and images for SaaS runners on macOS **(FREE SAAS)** + +When you use SaaS runners on macOS: + +- Each of your jobs runs in a newly provisioned VM, which is dedicated to the specific job. +- The VM is active only for the duration of the job and immediately deleted. + +## VM types + +The virtual machine where your job runs has `sudo` access with no password. +For the Beta, there is only one available machine type, `gbc-macos-large`. + +| Instance type | vCPUS | Memory (GB) | +| --------- | --- | ------- | +| `gbc-macos-large` | 4 | 10 | + +## VM images + +You can execute your build on one of the following images. +You specify this image in your `.gitlab-ci.yml` file. + +Each image is running a specific version of macOS and Xcode. + +| VM image | Included software | +|---------------------------|--------------------| +| macos-10.13-xcode-7 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/high-sierra.yml> | +| macos-10.13-xcode-8 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/high-sierra.yml> | +| macos-10.13-xcode-9 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/high-sierra.yml> | +| macos-10.14-xcode-10 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/mojave.yml> | +| macos-10.15-xcode-11 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/catalina.yml> | +| macos-11-xcode-12 | <https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/main/toolchain/big-sur.yml> | + +### Image update policy + +- Support for new macOS versions is planned. +- Additional details on the support policy and image update release process are documented + [in this project](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/macstadium/orka/-/blob/55bf59c8fa88712960afff2bf6ecc5daa879a8f5/docs/overview.md#os-images). diff --git a/doc/ci/runners/saas/macos_saas_runner.md b/doc/ci/runners/saas/macos_saas_runner.md new file mode 100644 index 00000000000..40c4deb51aa --- /dev/null +++ b/doc/ci/runners/saas/macos_saas_runner.md @@ -0,0 +1,63 @@ +--- +stage: Verify +group: Runner +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 +--- + +# SaaS runners on macOS (Beta) **(FREE SAAS)** + +SaaS runners on macOS provide an on-demand macOS build environment integrated with +GitLab SaaS [CI/CD](../../../ci/index.md). +Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS, iOS, tvOS). You can take advantage +of all the capabilities of the GitLab single DevOps platform and not have to manage or operate a +build environment. + +SaaS runners on macOS are in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) +and shouldn't be relied upon for mission-critical production jobs. + +## Quickstart + +To start using SaaS runners on macOS, you must submit an access request [issue](https://gitlab.com/gitlab-com/macos-buildcloud-runners-beta/-/issues/new?issuable_template=beta_access_request). After your +access has been granted and your build environment configured, you must configure your +`.gitlab-ci.yml` pipeline file: + +1. Add a `.gitlab-ci.yml` file to your project repository. +1. Specify the [image](macos/environment.md#vm-images) you want to use. +1. Commit a change to your repository. + +The runners automatically run your build. + +## Example `.gitlab-ci.yml` file + +The following sample `.gitlab-ci.yml` file shows how to start using the SaaS runners on macOS: + +```yaml +.macos_saas_runners: + tags: + - shared-macos-amd64 + image: macos-11-xcode-12 + +stages: + - build + - test + +before_script: + - echo "started by ${GITLAB_USER_NAME}" + +build: + extends: + - .macos_saas_runners + stage: build + script: + - echo "running scripts in the build job" + +test: + extends: + - .macos_saas_runners + stage: test + script: + - echo "running scripts in the test job" +``` + +NOTE: +During the Beta period, the architecture of this solution will change. Rather than the jobs running on a specific VM instance, they will run on an ephemeral VM instance that is created by an autoscaling instance, known as the Runner Manager. We will notify all Beta participants of any downtime required to do this work. diff --git a/doc/ci/runners/saas/windows_saas_runner.md b/doc/ci/runners/saas/windows_saas_runner.md new file mode 100644 index 00000000000..87ee542fb14 --- /dev/null +++ b/doc/ci/runners/saas/windows_saas_runner.md @@ -0,0 +1,155 @@ +--- +stage: Verify +group: Runner +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 +--- + +# SaaS runners on Windows (beta) **(FREE SAAS)** + +SaaS runners on Windows are in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) +and shouldn't be used for production workloads. + +During this beta period, the [shared runner pipeline quota](../../../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota) +applies for groups and projects in the same manner as Linux runners. This may +change when the beta period ends, as discussed in this [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/30834). + +Windows runners on GitLab.com autoscale by launching virtual machines on +the Google Cloud Platform. This solution uses an +[autoscaling driver](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/tree/master/docs/readme.md) +developed by GitLab for the [custom executor](https://docs.gitlab.com/runner/executors/custom.html). +Windows runners execute your CI/CD jobs on `n1-standard-2` instances with +2 vCPUs and 7.5 GB RAM. You can find a full list of available Windows packages in +the [package documentation](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/blob/main/cookbooks/preinstalled-software/README.md). + +We want to keep iterating to get Windows runners in a stable state and +[generally available](https://about.gitlab.com/handbook/product/gitlab-the-product/#generally-available-ga). +You can follow our work towards this goal in the +[related epic](https://gitlab.com/groups/gitlab-org/-/epics/2162). + +## Configuration + +The full contents of our `config.toml` are: + +NOTE: +Settings that aren't public are shown as `X`. + +```toml +concurrent = X +check_interval = 3 + +[[runners]] + name = "windows-runner" + url = "https://gitlab.com/" + token = "TOKEN" + executor = "custom" + builds_dir = "C:\\GitLab-Runner\\builds" + cache_dir = "C:\\GitLab-Runner\\cache" + shell = "powershell" + [runners.custom] + config_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" + config_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "config"] + prepare_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" + prepare_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "prepare"] + run_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" + run_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "run"] + cleanup_exec = "C:\\GitLab-Runner\\autoscaler\\autoscaler.exe" + cleanup_args = ["--config", "C:\\GitLab-Runner\\autoscaler\\config.toml", "custom", "cleanup"] +``` + +The full contents of our `autoscaler/config.toml` are: + +```toml +Provider = "gcp" +Executor = "winrm" +OS = "windows" +LogLevel = "info" +LogFormat = "text" +LogFile = "C:\\GitLab-Runner\\autoscaler\\autoscaler.log" +VMTag = "windows" + +[GCP] + ServiceAccountFile = "PATH" + Project = "some-project-df9323" + Zone = "us-east1-c" + MachineType = "n1-standard-2" + Image = "IMAGE" + DiskSize = 50 + DiskType = "pd-standard" + Subnetwork = "default" + Network = "default" + Tags = ["TAGS"] + Username = "gitlab_runner" + +[WinRM] + MaximumTimeout = 3600 + ExecutionMaxRetries = 0 + +[ProviderCache] + Enabled = true + Directory = "C:\\GitLab-Runner\\autoscaler\\machines" +``` + +## Example `.gitlab-ci.yml` file + +Below is a sample `.gitlab-ci.yml` file that shows how to start using the runners for Windows: + +```yaml +.shared_windows_runners: + tags: + - shared-windows + - windows + - windows-1809 + +stages: + - build + - test + +before_script: + - Set-Variable -Name "time" -Value (date -Format "%H:%m") + - echo ${time} + - echo "started by ${GITLAB_USER_NAME}" + +build: + extends: + - .shared_windows_runners + stage: build + script: + - echo "running scripts in the build job" + +test: + extends: + - .shared_windows_runners + stage: test + script: + - echo "running scripts in the test job" +``` + +## Limitations and known issues + +- All the limitations mentioned in our [beta + definition](https://about.gitlab.com/handbook/product/#beta). +- The average provisioning time for a new Windows VM is 5 minutes. + This means that you may notice slower build start times + on the Windows runner fleet during the beta. In a future + release we intend to update the autoscaler to enable + the pre-provisioning of virtual machines. This is intended to significantly reduce + the time it takes to provision a VM on the Windows fleet. You can + follow along in the [related issue](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/-/issues/32). +- The Windows runner fleet may be unavailable occasionally + for maintenance or updates. +- The Windows runner virtual machine instances do not use the + GitLab Docker executor. This means that you can't specify + [`image`](../../../ci/yaml/index.md#image) or [`services`](../../../ci/yaml/index.md#services) in + your pipeline configuration. +- For the beta release, we have included a set of software packages in + the base VM image. If your CI job requires additional software that's + not included in this list, then you must add installation + commands to [`before_script`](../../../ci/yaml/index.md#before_script) or [`script`](../../../ci/yaml/index.md#script) to install the required + software. Note that each job runs on a new VM instance, so the + installation of additional software packages needs to be repeated for + each job in your pipeline. +- The job may stay in a pending state for longer than the + Linux runners. +- There is the possibility that we introduce breaking changes which will + require updates to pipelines that are using the Windows runner + fleet. diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 65e6419dc43..1cdbe351c3f 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -2209,7 +2209,7 @@ its parent pipeline or another child pipeline in the same parent-child pipeline ```yaml create-artifact: stage: build - script: echo 'sample artifact' > artifact.txt + script: echo "sample artifact" > artifact.txt artifacts: paths: [artifact.txt] @@ -2413,10 +2413,10 @@ job2: ```yaml job1: - script: echo 'test' + script: echo "test" job2: - script: echo 'test' + script: echo "test" only: - branches - tags @@ -2673,7 +2673,7 @@ you can use this image from the GitLab Container Registry: `registry.gitlab.com/ rules: - if: $CI_COMMIT_TAG # Run this job when a tag is created manually script: - - echo 'Running the release job.' + - echo "Running the release job." release: name: 'Release $CI_COMMIT_TAG' description: 'Release created using the release-cli.' @@ -2691,7 +2691,7 @@ This example creates a release: ```yaml script: - - echo 'release job' + - echo "release job" ``` An [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/223856) exists to remove this requirement. @@ -2729,7 +2729,7 @@ To create a release when a new tag is added to the project: ```yaml job: - script: echo 'Running the release job for the new tag.' + script: echo "Running the release job for the new tag." release: tag_name: $CI_COMMIT_TAG description: 'Release description' @@ -2742,7 +2742,7 @@ should **not** configure the job to run only for new tags. A semantic versioning ```yaml job: - script: echo 'Running the release job and creating a new tag.' + script: echo "Running the release job and creating a new tag." release: tag_name: ${MAJOR}_${MINOR}_${REVISION} description: 'Release description' diff --git a/doc/ci/yaml/workflow.md b/doc/ci/yaml/workflow.md index 67ca1150553..332214638d8 100644 --- a/doc/ci/yaml/workflow.md +++ b/doc/ci/yaml/workflow.md @@ -4,7 +4,7 @@ group: Pipeline Authoring 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 --- -# GitLab CI/CD `workflow` keyword +# GitLab CI/CD `workflow` keyword **(FREE)** Use the [`workflow`](index.md#workflow) keyword to control when pipelines are created. diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md index 71a11d2024c..a813502db51 100644 --- a/doc/development/pipelines.md +++ b/doc/development/pipelines.md @@ -820,7 +820,7 @@ We no longer use this optimization for `gitlab-org/gitlab` because the [pack-obj allows Gitaly to serve the full CI/CD fetch traffic now. See [Git fetch caching](#git-fetch-caching). The pre-clone step works by using the `CI_PRE_CLONE_SCRIPT` variable -[defined by GitLab.com shared runners](../ci/runners/runner_cloud/linux_runner_cloud.md#pre-clone-script). +[defined by GitLab.com shared runners](../ci/runners/saas/linux_saas_runner.md#pre-clone-script). --- diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md index e949f968c2b..f083f886924 100644 --- a/doc/user/analytics/ci_cd_analytics.md +++ b/doc/user/analytics/ci_cd_analytics.md @@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/38318) to CI/CD Analytics in GitLab 12.8. GitLab tracks the history of your pipeline successes and failures, as well as how long each pipeline -ran. To view this information, go to **Analytics > CI/CD Analytics**. +ran. To view this information for a project, go to **Analytics > CI/CD Analytics**. View successful pipelines: diff --git a/doc/user/project/merge_requests/commit_templates.md b/doc/user/project/merge_requests/commit_templates.md index b615c86288c..c1a8754b4a2 100644 --- a/doc/user/project/merge_requests/commit_templates.md +++ b/doc/user/project/merge_requests/commit_templates.md @@ -31,7 +31,7 @@ This commit message can be customized to follow any guidelines you might have. To do so, expand the **Merge requests** tab within your project's **General** settings and change the **Merge commit message template** text: - + You can use static text and following variables: @@ -49,3 +49,18 @@ Empty variables that are the only word in a line will be removed along with all Merge commit template field has a limit of 500 characters. This limit only applies to the template itself. + +## Squash commit message template + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345275) in GitLab 14.6. + +As a project maintainer, you're able to configure squash commit message template. It will be used during merge with +squash to create squash commit message. It uses the same syntax and variables as merge commit message template. + + + +Default squash commit message can be recreated using following template: + +```plaintext +%{title} +``` diff --git a/doc/user/project/merge_requests/creating_merge_requests.md b/doc/user/project/merge_requests/creating_merge_requests.md index 918f9830edc..e15874d2df8 100644 --- a/doc/user/project/merge_requests/creating_merge_requests.md +++ b/doc/user/project/merge_requests/creating_merge_requests.md @@ -21,6 +21,10 @@ You can create a merge request from the list of merge requests. 1. Select a source and target branch and then **Compare branches and continue**. 1. Fill out the fields and select **Create merge request**. +NOTE: +Merge requests are designed around a one-to-one (1:1) branch relationship. Only one open merge request may +be associated with a given target branch at a time. + ## From an issue You can [create a merge request from an issue](../repository/web_editor.md#create-a-new-branch-from-an-issue). diff --git a/doc/user/project/merge_requests/img/squash_commit_message_template_v14_6.png b/doc/user/project/merge_requests/img/squash_commit_message_template_v14_6.png Binary files differnew file mode 100644 index 00000000000..b4b98235467 --- /dev/null +++ b/doc/user/project/merge_requests/img/squash_commit_message_template_v14_6.png diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md index e551f65b75d..b6c1f29788c 100644 --- a/doc/user/project/merge_requests/squash_and_merge.md +++ b/doc/user/project/merge_requests/squash_and_merge.md @@ -28,9 +28,9 @@ NOTE: The squashed commit in this example is followed by a merge commit, because the merge method for this repository uses a merge commit. You can disable merge commits in **Project Settings > General > Merge requests > Merge method > Fast-forward merge**. -The squashed commit's default commit message is taken from the merge request title. +The squashed commit's default commit message is taken from the merge request title. It can be changed using [squash commit message template](commit_templates.md#squash-commit-message-template). -It can be customized before merging a merge request. +It can also be customized before merging a merge request.  diff --git a/doc/user/project/merge_requests/versions.md b/doc/user/project/merge_requests/versions.md index 3922ee4d770..796ffc7866c 100644 --- a/doc/user/project/merge_requests/versions.md +++ b/doc/user/project/merge_requests/versions.md @@ -2,10 +2,9 @@ stage: Create group: Code Review 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 -type: reference, concepts --- -# Merge requests versions +# Merge requests versions **(FREE)** Every time you push to a branch that is tied to a merge request, a new version of merge request diff is created. When you visit a merge request that contains diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md index abedae5d10b..3077a5daf19 100644 --- a/doc/user/project/releases/index.md +++ b/doc/user/project/releases/index.md @@ -100,7 +100,7 @@ release_job: rules: - if: $CI_COMMIT_TAG # Run this job when a tag is created manually script: - - echo 'running release_job' + - echo "running release_job" release: name: 'Release $CI_COMMIT_TAG' description: 'Created using the release-cli $EXTRA_DESCRIPTION' # $EXTRA_DESCRIPTION must be defined @@ -149,7 +149,7 @@ release_job: when: never # Do not run this job when a tag is created manually - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch script: - - echo 'running release_job for $TAG' + - echo "running release_job for $TAG" release: name: 'Release $TAG' description: 'Created using the release-cli $EXTRA_DESCRIPTION' # $EXTRA_DESCRIPTION and the $TAG @@ -224,14 +224,14 @@ A pipeline can have multiple `release` jobs, for example: ```yaml ios-release: script: - - echo 'iOS release job' + - echo "iOS release job" release: tag_name: v1.0.0-ios description: 'iOS release v1.0.0' android-release: script: - - echo 'Android release job' + - echo "Android release job" release: tag_name: v1.0.0-android description: 'Android release v1.0.0' diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 7b8b713cf15..45bd10bf9c3 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -315,7 +315,7 @@ Set up your project's merge request settings: - Enable [require an associated issue from Jira](../../../integration/jira/issues.md#require-associated-jira-issue-for-merge-requests-to-be-merged). - Enable [`delete source branch after merge` option by default](../merge_requests/getting_started.md#deleting-the-source-branch). - Configure [suggested changes commit messages](../merge_requests/reviews/suggestions.md#configure-the-commit-message-for-applied-suggestions). -- Configure [merge commit message template](../merge_requests/commit_templates.md). +- Configure [merge and squash commit message templates](../merge_requests/commit_templates.md). - Configure [the default target project](../merge_requests/creating_merge_requests.md#set-the-default-target-project) for merge requests coming from forks. ### Service Desk diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index e3f1e90b80f..d7600f8a9b5 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -115,6 +115,7 @@ module API expose :squash_option expose :suggestion_commit_message expose :merge_commit_template + expose :squash_commit_template expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) { options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) } diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 2425982d405..d7de8bd8b8b 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -62,6 +62,7 @@ module API optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests' optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions' optional :merge_commit_template, type: String, desc: 'Template used to create merge commit message' + optional :squash_commit_template, type: String, desc: 'Template used to create squash commit message' optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning' optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled' @@ -162,6 +163,7 @@ module API :avatar, :suggestion_commit_message, :merge_commit_template, + :squash_commit_template, :repository_storage, :compliance_framework_setting, :packages_enabled, diff --git a/lib/gitlab/merge_requests/merge_commit_message.rb b/lib/gitlab/merge_requests/commit_message_generator.rb index 2a6a7859b33..c420385b7c1 100644 --- a/lib/gitlab/merge_requests/merge_commit_message.rb +++ b/lib/gitlab/merge_requests/commit_message_generator.rb @@ -1,31 +1,21 @@ # frozen_string_literal: true module Gitlab module MergeRequests - class MergeCommitMessage + class CommitMessageGenerator def initialize(merge_request:) @merge_request = merge_request end - def message + def merge_message return unless @merge_request.target_project.merge_commit_template.present? - message = @merge_request.target_project.merge_commit_template - message = message.delete("\r") + replace_placeholders(@merge_request.target_project.merge_commit_template) + end - # Remove placeholders that correspond to empty values and are the last word in the line - # along with all whitespace characters preceding them. - # This allows us to recreate previous default merge commit message behaviour - we skipped new line character - # before empty description and before closed issues when none were present. - PLACEHOLDERS.each do |key, value| - unless value.call(merge_request).present? - message = message.gsub(BLANK_PLACEHOLDERS_REGEXES[key], '') - end - end + def squash_message + return unless @merge_request.target_project.squash_commit_template.present? - Gitlab::StringPlaceholderReplacer - .replace_string_placeholders(message, PLACEHOLDERS_REGEX) do |key| - PLACEHOLDERS[key].call(merge_request) - end + replace_placeholders(@merge_request.target_project.squash_commit_template) end private @@ -55,6 +45,26 @@ module Gitlab BLANK_PLACEHOLDERS_REGEXES = (PLACEHOLDERS.map do |key, value| [key, Regexp.new("[\n\r]+%{#{Regexp.escape(key)}}$")] end).to_h.freeze + + def replace_placeholders(message) + # convert CRLF to LF + message = message.delete("\r") + + # Remove placeholders that correspond to empty values and are the last word in the line + # along with all whitespace characters preceding them. + # This allows us to recreate previous default merge commit message behaviour - we skipped new line character + # before empty description and before closed issues when none were present. + PLACEHOLDERS.each do |key, value| + unless value.call(merge_request).present? + message = message.gsub(BLANK_PLACEHOLDERS_REGEXES[key], '') + end + end + + Gitlab::StringPlaceholderReplacer + .replace_string_placeholders(message, PLACEHOLDERS_REGEX) do |key| + PLACEHOLDERS[key].call(merge_request) + end + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 834556a2003..d9e85ad9dd7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9710,6 +9710,9 @@ msgstr "" msgid "Create %{workspace} label" msgstr "" +msgid "Create Google Cloud project" +msgstr "" + msgid "Create New Directory" msgstr "" @@ -15276,6 +15279,9 @@ msgstr "" msgid "Generate site and private keys at" msgstr "" +msgid "Generated service account is linked to the selected environment" +msgstr "" + msgid "Generic" msgstr "" @@ -15825,6 +15831,9 @@ msgstr "" msgid "GitLab account request rejected" msgstr "" +msgid "GitLab and Google Cloud configuration seems to be incomplete. This probably can be fixed by your GitLab administration team. You may share these logs with them:" +msgstr "" + msgid "GitLab commit" msgstr "" @@ -16215,6 +16224,15 @@ msgstr "" msgid "Google Cloud authorizations required" msgstr "" +msgid "Google Cloud project" +msgstr "" + +msgid "Google Cloud project misconfigured" +msgstr "" + +msgid "Google Cloud project required" +msgstr "" + msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service." msgstr "" @@ -16836,6 +16854,12 @@ msgstr "" msgid "GroupSettings|What are badges?" msgstr "" +msgid "GroupSettings|When the number of active users exceeds this number, additional users must be %{user_cap_docs_link_start}approved by an owner%{user_cap_docs_link_end}. Leave empty if you don't want to enforce approvals." +msgstr "" + +msgid "GroupSettings|When the number of active users exceeds this number, additional users must be %{user_cap_docs_link_start}approved by an owner%{user_cap_docs_link_end}. Leave empty if you don't want to enforce approvals. Increasing the user cap will not automatically approve pending users." +msgstr "" + msgid "GroupSettings|You can only transfer the group to a group you manage." msgstr "" @@ -23265,6 +23289,9 @@ msgstr "" msgid "New schedule" msgstr "" +msgid "New service account is generated for the selected Google Cloud project" +msgstr "" + msgid "New snippet" msgstr "" @@ -27422,6 +27449,9 @@ msgstr "" msgid "ProjectSettings|Snippets" msgstr "" +msgid "ProjectSettings|Squash commit message template" +msgstr "" + msgid "ProjectSettings|Squash commits when merging" msgstr "" @@ -27446,6 +27476,9 @@ msgstr "" msgid "ProjectSettings|The commit message used when merging, if the merge method creates a merge commit. %{link_start}Learn more about syntax and variables.%{link_end}" msgstr "" +msgid "ProjectSettings|The commit message used when squashing commits. %{link_start}Learn more about syntax and variables.%{link_end}" +msgstr "" + msgid "ProjectSettings|The default target project for merge requests created in this fork project." msgstr "" @@ -37091,6 +37124,9 @@ msgstr "" msgid "Unknown response text" msgstr "" +msgid "Unknown root" +msgstr "" + msgid "Unknown user" msgstr "" @@ -39083,9 +39119,6 @@ msgstr "" msgid "When paused, GitLab still tracks the changes. This is useful for cluster/index migrations." msgstr "" -msgid "When the number of active users exceeds this number, additional users must be %{user_cap_docs_link_start}approved by an owner%{user_cap_docs_link_end}. Leave empty if you don't want to enforce approvals." -msgstr "" - msgid "When this merge request is accepted" msgid_plural "When these merge requests are accepted" msgstr[0] "" @@ -39736,6 +39769,9 @@ msgstr "" msgid "You could not create a new trigger." msgstr "" +msgid "You do not have any Google Cloud projects. Please create a Google Cloud project and then reload this page." +msgstr "" + msgid "You do not have any subscriptions yet" msgstr "" @@ -41681,9 +41717,15 @@ msgstr "" msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated." msgstr "" +msgid "mrWidget|To change these default messages, edit the templates for both the merge and squash commit messages. %{linkStart}Learn more.%{linkEnd}" +msgstr "" + msgid "mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}" msgstr "" +msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}" +msgstr "" + msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description." msgstr "" diff --git a/package.json b/package.json index 4f1c12e72cb..13d157e2e15 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "@rails/ujs": "6.1.4-1", "@sentry/browser": "5.30.0", "@sourcegraph/code-host-integration": "0.0.60", - "@tiptap/core": "^2.0.0-beta.140", - "@tiptap/extension-blockquote": "^2.0.0-beta.24", + "@tiptap/core": "^2.0.0-beta.142", + "@tiptap/extension-blockquote": "^2.0.0-beta.25", "@tiptap/extension-bold": "^2.0.0-beta.24", "@tiptap/extension-bullet-list": "^2.0.0-beta.23", "@tiptap/extension-code": "^2.0.0-beta.25", diff --git a/scripts/api/get_job_id.rb b/scripts/api/get_job_id.rb index 166c9198951..c32299706ba 100755 --- a/scripts/api/get_job_id.rb +++ b/scripts/api/get_job_id.rb @@ -10,6 +10,7 @@ class JobFinder pipeline_query: {}.freeze, job_query: {}.freeze ).freeze + MAX_PIPELINES_TO_ITERATE = 200 def initialize(options) @project = options.delete(:project) @@ -41,8 +42,11 @@ class JobFinder def find_job_with_artifact return if artifact_path.nil? - client.pipelines(project, pipeline_query_params).auto_paginate do |pipeline| + client.pipelines(project, pipeline_query_params).paginate_with_limit(MAX_PIPELINES_TO_ITERATE) do |pipeline| + $stderr.puts "Iterating over #{pipeline}" # rubocop:disable Style/StderrPuts client.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job| + next if job_name && !found_job_by_name?(job) + return job if found_job_with_artifact?(job) # rubocop:disable Cop/AvoidReturnFromBlocks end end @@ -53,7 +57,7 @@ class JobFinder def find_job_with_filtered_pipelines return if pipeline_query.empty? - client.pipelines(project, pipeline_query_params).auto_paginate do |pipeline| + client.pipelines(project, pipeline_query_params).paginate_with_limit(MAX_PIPELINES_TO_ITERATE) do |pipeline| client.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job| return job if found_job_by_name?(job) # rubocop:disable Cop/AvoidReturnFromBlocks end diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh index 8714f1c0060..70a63695f5e 100644 --- a/scripts/rspec_helpers.sh +++ b/scripts/rspec_helpers.sh @@ -16,17 +16,27 @@ function retrieve_tests_metadata() { # always target the canonical project here, so the branch must be hardcoded local project_path="gitlab-org/gitlab" local artifact_branch="master" + local username="gitlab-bot" + local job_name="update-tests-metadata" local test_metadata_job_id # Ruby - test_metadata_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata") - - if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then - scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" - fi - - if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then - scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}" + test_metadata_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=${username}" -Q "scope=success" --job-name "${job_name}") + + if [[ -n "${test_metadata_job_id}" ]]; then + echo "test_metadata_job_id: ${test_metadata_job_id}" + + if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then + scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" + fi + + if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then + scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}" + fi + else + echo "test_metadata_job_id couldn't be found!" + echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" + echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}" fi fi } @@ -61,18 +71,63 @@ function retrieve_tests_mapping() { # always target the canonical project here, so the branch must be hardcoded local project_path="gitlab-org/gitlab" local artifact_branch="master" + local username="gitlab-bot" + local job_name="update-tests-metadata" local test_metadata_with_mapping_job_id - test_metadata_with_mapping_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") + test_metadata_with_mapping_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=${username}" -Q "scope=success" --job-name "${job_name}") - if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then - (scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_with_mapping_job_id}" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}" + if [[ -n "${test_metadata_with_mapping_job_id}" ]]; then + echo "test_metadata_with_mapping_job_id: ${test_metadata_with_mapping_job_id}" + + if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then + (scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_with_mapping_job_id}" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}" + fi + else + echo "test_metadata_with_mapping_job_id couldn't be found!" + echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}" fi fi scripts/unpack-test-mapping "${RSPEC_PACKED_TESTS_MAPPING_PATH}" "${RSPEC_TESTS_MAPPING_PATH}" } +function retrieve_frontend_fixtures_mapping() { + mkdir -p $(dirname "$FRONTEND_FIXTURES_MAPPING_PATH") + + if [[ -n "${RETRIEVE_TESTS_METADATA_FROM_PAGES}" ]]; then + if [[ ! -f "${FRONTEND_FIXTURES_MAPPING_PATH}" ]]; then + (curl --location -o "${FRONTEND_FIXTURES_MAPPING_PATH}" "https://gitlab-org.gitlab.io/gitlab/${FRONTEND_FIXTURES_MAPPING_PATH}") || echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}" + fi + else + # ${CI_DEFAULT_BRANCH} might not be master in other forks but we want to + # always target the canonical project here, so the branch must be hardcoded + local project_path="gitlab-org/gitlab" + local artifact_branch="master" + local username="gitlab-bot" + local job_name="generate-frontend-fixtures-mapping" + local test_metadata_with_mapping_job_id + + # On the MR that introduces 'generate-frontend-fixtures-mapping', we cannot retrieve the file from a master scheduled pipeline, so we take it from a known MR pipeline + if [[ "${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}" == "339343-execute-related-jests-specs-for-mrs-with-backend-changes" ]]; then + test_metadata_with_mapping_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --pipeline-id "414921396" -Q "scope=success" --job-name "${job_name}") + else + test_metadata_with_mapping_job_id=$(scripts/api/get_job_id.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" -q "ref=${artifact_branch}" -q "username=${username}" -Q "scope=success" --job-name "${job_name}") + fi + + if [[ $? -eq 0 ]] && [[ -n "${test_metadata_with_mapping_job_id}" ]]; then + echo "test_metadata_with_mapping_job_id: ${test_metadata_with_mapping_job_id}" + + if [[ ! -f "${FRONTEND_FIXTURES_MAPPING_PATH}" ]]; then + (scripts/api/download_job_artifact.rb --endpoint "https://gitlab.com/api/v4" --project "${project_path}" --job-id "${test_metadata_with_mapping_job_id}" --artifact-path "${FRONTEND_FIXTURES_MAPPING_PATH}") || echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}" + fi + else + echo "test_metadata_with_mapping_job_id couldn't be found!" + echo "{}" > "${FRONTEND_FIXTURES_MAPPING_PATH}" + fi + fi +} + function update_tests_mapping() { if ! crystalball_rspec_data_exists; then echo "No crystalball rspec data found." @@ -113,7 +168,7 @@ function rspec_simple_job() { export NO_KNAPSACK="1" - bin/rspec -Ispec -rspec_helper --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts} + eval "bin/rspec -Ispec -rspec_helper --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts}" } function rspec_db_library_code() { @@ -256,3 +311,27 @@ function rspec_matched_foss_tests() { echo "No impacted FOSS rspec tests to run" fi } + +function generate_frontend_fixtures_mapping() { + local pattern="" + + if [[ -d "ee/" ]]; then + pattern=",ee/" + fi + + if [[ -d "jh/" ]]; then + pattern="${pattern},jh/" + fi + + if [[ -n "${pattern}" ]]; then + pattern="{${pattern}}" + fi + + pattern="${pattern}spec/frontend/fixtures/**/*.rb" + + export GENERATE_FRONTEND_FIXTURES_MAPPING="true" + + mkdir -p $(dirname "$FRONTEND_FIXTURES_MAPPING_PATH") + + rspec_simple_job "--pattern \"${pattern}\"" +} diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb index 06795344c5c..67a232607cd 100644 --- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb +++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb @@ -7,19 +7,26 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do let(:user) { project.creator } let(:issue_1) { create(:issue, project: project)} let(:issue_2) { create(:issue, project: project)} + let(:source_branch) { 'csv' } + let(:target_branch) { 'master' } + let(:squash) { false } let(:merge_request) do create( :merge_request, - :simple, source_project: project, - description: "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" + target_project: project, + source_branch: source_branch, + target_branch: target_branch, + description: "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}", + squash: squash ) end - let(:textbox) { page.find(:css, '#merge-message-edit', visible: false) } - let(:default_message) do + let(:merge_textbox) { page.find(:css, '#merge-message-edit', visible: false) } + let(:squash_textbox) { page.find(:css, '#squash-message-edit', visible: false) } + let(:default_merge_commit_message) do [ - "Merge branch 'feature' into 'master'", + "Merge branch '#{source_branch}' into '#{target_branch}'", merge_request.title, "Closes #{issue_1.to_reference} and #{issue_2.to_reference}", "See merge request #{merge_request.to_reference(full: true)}" @@ -35,8 +42,8 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do it 'has commit message without description' do expect(page).not_to have_selector('#merge-message-edit') first('.js-mr-widget-commits-count').click - expect(textbox).to be_visible - expect(textbox.value).to eq(default_message) + expect(merge_textbox).to be_visible + expect(merge_textbox.value).to eq(default_merge_commit_message) end context 'when target project has merge commit template set' do @@ -45,8 +52,34 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do it 'uses merge commit template' do expect(page).not_to have_selector('#merge-message-edit') first('.js-mr-widget-commits-count').click - expect(textbox).to be_visible - expect(textbox.value).to eq(merge_request.title) + expect(merge_textbox).to be_visible + expect(merge_textbox.value).to eq(merge_request.title) + end + end + + context 'when squash is performed' do + let(:squash) { true } + + it 'has default message with merge request title' do + expect(page).not_to have_selector('#squash-message-edit') + first('.js-mr-widget-commits-count').click + expect(squash_textbox).to be_visible + expect(merge_textbox).to be_visible + expect(squash_textbox.value).to eq(merge_request.title) + expect(merge_textbox.value).to eq(default_merge_commit_message) + end + + context 'when target project has squash commit template set' do + let(:project) { create(:project, :public, :repository, squash_commit_template: '%{description}') } + + it 'uses squash commit template' do + expect(page).not_to have_selector('#squash-message-edit') + first('.js-mr-widget-commits-count').click + expect(squash_textbox).to be_visible + expect(merge_textbox).to be_visible + expect(squash_textbox.value).to eq(merge_request.description) + expect(merge_textbox.value).to eq(default_merge_commit_message) + end end end end diff --git a/spec/frontend/google_cloud/components/errors/gcp_error_spec.js b/spec/frontend/google_cloud/components/errors/gcp_error_spec.js new file mode 100644 index 00000000000..4062a8b902a --- /dev/null +++ b/spec/frontend/google_cloud/components/errors/gcp_error_spec.js @@ -0,0 +1,34 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlAlert } from '@gitlab/ui'; +import GcpError from '~/google_cloud/components/errors/gcp_error.vue'; + +describe('GcpError component', () => { + let wrapper; + + const findAlert = () => wrapper.findComponent(GlAlert); + const findBlockquote = () => wrapper.find('blockquote'); + + const propsData = { error: 'IAM and CloudResourceManager API disabled' }; + + beforeEach(() => { + wrapper = shallowMount(GcpError, { propsData }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('contains alert', () => { + expect(findAlert().exists()).toBe(true); + }); + + it('contains relevant text', () => { + const alertText = findAlert().text(); + expect(findAlert().props('title')).toBe(GcpError.i18n.title); + expect(alertText).toContain(GcpError.i18n.description); + }); + + it('contains error stacktrace', () => { + expect(findBlockquote().text()).toBe(propsData.error); + }); +}); diff --git a/spec/frontend/google_cloud/components/errors/no_gcp_projects_spec.js b/spec/frontend/google_cloud/components/errors/no_gcp_projects_spec.js new file mode 100644 index 00000000000..e1e20377880 --- /dev/null +++ b/spec/frontend/google_cloud/components/errors/no_gcp_projects_spec.js @@ -0,0 +1,33 @@ +import { mount } from '@vue/test-utils'; +import { GlAlert, GlButton } from '@gitlab/ui'; +import NoGcpProjects from '~/google_cloud/components/errors/no_gcp_projects.vue'; + +describe('NoGcpProjects component', () => { + let wrapper; + + const findAlert = () => wrapper.findComponent(GlAlert); + const findButton = () => wrapper.findComponent(GlButton); + + beforeEach(() => { + wrapper = mount(NoGcpProjects); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('contains alert', () => { + expect(findAlert().exists()).toBe(true); + }); + + it('contains relevant text', () => { + expect(findAlert().props('title')).toBe(NoGcpProjects.i18n.title); + expect(findAlert().text()).toContain(NoGcpProjects.i18n.description); + }); + + it('contains create gcp project button', () => { + const button = findButton(); + expect(button.text()).toBe(NoGcpProjects.i18n.createLabel); + expect(button.attributes('href')).toBe('https://console.cloud.google.com/projectcreate'); + }); +}); diff --git a/spec/frontend/google_cloud/components/app_spec.js b/spec/frontend/google_cloud/components/screens/app_spec.js index bb86eb5c22e..bd766a79f51 100644 --- a/spec/frontend/google_cloud/components/app_spec.js +++ b/spec/frontend/google_cloud/components/screens/app_spec.js @@ -1,8 +1,8 @@ import { shallowMount } from '@vue/test-utils'; import { GlTab, GlTabs } from '@gitlab/ui'; -import App from '~/google_cloud/components/app.vue'; +import App from '~/google_cloud/components/screens/app.vue'; import IncubationBanner from '~/google_cloud/components/incubation_banner.vue'; -import ServiceAccounts from '~/google_cloud/components/service_accounts.vue'; +import ServiceAccountsList from '~/google_cloud/components/service_accounts_list.vue'; describe('google_cloud App component', () => { let wrapper; @@ -13,7 +13,7 @@ describe('google_cloud App component', () => { const findConfigurationTab = () => findTabItems().at(0); const findDeploymentTab = () => findTabItems().at(1); const findServicesTab = () => findTabItems().at(2); - const findServiceAccounts = () => findConfigurationTab().findComponent(ServiceAccounts); + const findServiceAccountsList = () => findConfigurationTab().findComponent(ServiceAccountsList); beforeEach(() => { const propsData = { @@ -47,7 +47,7 @@ describe('google_cloud App component', () => { }); it('should contain service accounts component', () => { - expect(findServiceAccounts().exists()).toBe(true); + expect(findServiceAccountsList().exists()).toBe(true); }); }); diff --git a/spec/frontend/google_cloud/components/screens/service_accounts_form_spec.js b/spec/frontend/google_cloud/components/screens/service_accounts_form_spec.js new file mode 100644 index 00000000000..3899b7d8a9d --- /dev/null +++ b/spec/frontend/google_cloud/components/screens/service_accounts_form_spec.js @@ -0,0 +1,65 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlButton, GlFormGroup, GlFormSelect } from '@gitlab/ui'; +import IncubationBanner from '~/google_cloud/components/incubation_banner.vue'; +import ServiceAccountsForm from '~/google_cloud/components/screens/service_accounts_form.vue'; + +describe('ServiceAccountsForm component', () => { + let wrapper; + + const findIncubationBanner = () => wrapper.findComponent(IncubationBanner); + const findHeader = () => wrapper.find('header'); + const findAllFormGroups = () => wrapper.findAllComponents(GlFormGroup); + const findAllFormSelects = () => wrapper.findAllComponents(GlFormSelect); + const findAllButtons = () => wrapper.findAllComponents(GlButton); + + const propsData = { gcpProjects: [], environments: [], cancelPath: '#cancel-url' }; + + beforeEach(() => { + wrapper = shallowMount(ServiceAccountsForm, { propsData }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('contains incubation banner', () => { + expect(findIncubationBanner().exists()).toBe(true); + }); + + it('contains header', () => { + expect(findHeader().exists()).toBe(true); + }); + + it('contains GCP project form group', () => { + const formGroup = findAllFormGroups().at(0); + expect(formGroup.exists()).toBe(true); + }); + + it('contains GCP project dropdown', () => { + const select = findAllFormSelects().at(0); + expect(select.exists()).toBe(true); + }); + + it('contains Environments form group', () => { + const formGorup = findAllFormGroups().at(1); + expect(formGorup.exists()).toBe(true); + }); + + it('contains Environments dropdown', () => { + const select = findAllFormSelects().at(1); + expect(select.exists()).toBe(true); + }); + + it('contains Submit button', () => { + const button = findAllButtons().at(0); + expect(button.exists()).toBe(true); + expect(button.text()).toBe(ServiceAccountsForm.i18n.submitLabel); + }); + + it('contains Cancel button', () => { + const button = findAllButtons().at(1); + expect(button.exists()).toBe(true); + expect(button.text()).toBe(ServiceAccountsForm.i18n.cancelLabel); + expect(button.attributes('href')).toBe('#cancel-url'); + }); +}); diff --git a/spec/frontend/google_cloud/components/service_accounts_spec.js b/spec/frontend/google_cloud/components/service_accounts_list_spec.js index 3d097078f03..cdb3f74051c 100644 --- a/spec/frontend/google_cloud/components/service_accounts_spec.js +++ b/spec/frontend/google_cloud/components/service_accounts_list_spec.js @@ -1,6 +1,6 @@ import { mount } from '@vue/test-utils'; import { GlButton, GlEmptyState, GlTable } from '@gitlab/ui'; -import ServiceAccounts from '~/google_cloud/components/service_accounts.vue'; +import ServiceAccountsList from '~/google_cloud/components/service_accounts_list.vue'; describe('ServiceAccounts component', () => { describe('when the project does not have any service accounts', () => { @@ -15,7 +15,7 @@ describe('ServiceAccounts component', () => { createUrl: '#create-url', emptyIllustrationUrl: '#empty-illustration-url', }; - wrapper = mount(ServiceAccounts, { propsData }); + wrapper = mount(ServiceAccountsList, { propsData }); }); afterEach(() => { @@ -48,7 +48,7 @@ describe('ServiceAccounts component', () => { createUrl: '#create-url', emptyIllustrationUrl: '#empty-illustration-url', }; - wrapper = mount(ServiceAccounts, { propsData }); + wrapper = mount(ServiceAccountsList, { propsData }); }); it('shows the title', () => { diff --git a/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js index f965fc32dc1..c30f6f1dfd1 100644 --- a/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js @@ -3,7 +3,6 @@ import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit const testCommitMessage = 'Test commit message'; const testLabel = 'Test label'; -const testTextMuted = 'Test text muted'; const testInputId = 'test-input-id'; describe('Commits edit component', () => { @@ -64,7 +63,6 @@ describe('Commits edit component', () => { beforeEach(() => { createComponent({ header: `<div class="test-header">${testCommitMessage}</div>`, - 'text-muted': `<p class="test-text-muted">${testTextMuted}</p>`, }); }); @@ -74,12 +72,5 @@ describe('Commits edit component', () => { expect(headerSlotElement.exists()).toBe(true); expect(headerSlotElement.text()).toBe(testCommitMessage); }); - - it('renders text-muted slot correctly', () => { - const textMutedElement = wrapper.find('.test-text-muted'); - - expect(textMutedElement.exists()).toBe(true); - expect(textMutedElement.text()).toBe(testTextMuted); - }); }); }); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index a9cf085f84c..7082a19a8e7 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -1,5 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; +import { GlSprintf } from '@gitlab/ui'; import simplePoll from '~/lib/utils/simple_poll'; import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue'; import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue'; @@ -487,6 +488,7 @@ describe('ReadyToMerge', () => { const findCommitEditElements = () => wrapper.findAll(CommitEdit); const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown); const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label'); + const findTipLink = () => wrapper.find(GlSprintf); describe('squash checkbox', () => { it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => { @@ -751,6 +753,12 @@ describe('ReadyToMerge', () => { expect(findCommitDropdownElement().exists()).toBeTruthy(); }); }); + + it('renders a tip including a link to docs on templates', () => { + createComponent(); + + expect(findTipLink().exists()).toBe(true); + }); }); describe('Merge request project settings', () => { diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 4f205e861dd..adf5507571b 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -34,7 +34,7 @@ RSpec.describe GitlabSchema.types['Project'] do container_repositories container_repositories_count pipeline_analytics squash_read_only sast_ci_configuration cluster_agent cluster_agents agent_configurations - ci_template timelogs merge_commit_template + ci_template timelogs merge_commit_template squash_commit_template ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 2f7db15ae29..6ffe2187466 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -563,6 +563,7 @@ Project: - autoclose_referenced_issues - suggestion_commit_message - merge_commit_template +- squash_commit_template ProjectTracingSetting: - external_url Author: diff --git a/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb b/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb new file mode 100644 index 00000000000..4de5c9b9c82 --- /dev/null +++ b/spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb @@ -0,0 +1,243 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do + let(:merge_commit_template) { nil } + let(:squash_commit_template) { nil } + let(:project) do + create( + :project, + :public, + :repository, + merge_commit_template: merge_commit_template, + squash_commit_template: squash_commit_template + ) + end + + let(:user) { project.creator } + let(:merge_request_description) { "Merge Request Description\nNext line" } + let(:merge_request_title) { 'Bugfix' } + let(:merge_request) do + create( + :merge_request, + :simple, + source_project: project, + target_project: project, + author: user, + description: merge_request_description, + title: merge_request_title + ) + end + + subject { described_class.new(merge_request: merge_request) } + + shared_examples_for 'commit message with template' do |message_template_name| + it 'returns nil when template is not set in target project' do + expect(result_message).to be_nil + end + + context 'when project has custom commit template' do + let(message_template_name) { <<~MSG.rstrip } + %{title} + + See merge request %{reference} + MSG + + it 'uses custom template' do + expect(result_message).to eq <<~MSG.rstrip + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + + context 'when project has commit template with closed issues' do + let(message_template_name) { <<~MSG.rstrip } + Merge branch '%{source_branch}' into '%{target_branch}' + + %{title} + + %{issues} + + See merge request %{reference} + MSG + + it 'omits issues and new lines when no issues are mentioned in description' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + + context 'when MR closes issues' do + let(:issue_1) { create(:issue, project: project) } + let(:issue_2) { create(:issue, project: project) } + let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" } + + it 'includes them and keeps new line characters' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + Closes #{issue_1.to_reference} and #{issue_2.to_reference} + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + end + + context 'when project has commit template with description' do + let(message_template_name) { <<~MSG.rstrip } + Merge branch '%{source_branch}' into '%{target_branch}' + + %{title} + + %{description} + + See merge request %{reference} + MSG + + it 'uses template' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + Merge Request Description + Next line + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + + context 'when description is empty string' do + let(:merge_request_description) { '' } + + it 'skips description placeholder and removes new line characters before it' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + + context 'when description is nil' do + let(:merge_request_description) { nil } + + it 'skips description placeholder and removes new line characters before it' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + + context 'when description is blank string' do + let(:merge_request_description) { "\n\r \n" } + + it 'skips description placeholder and removes new line characters before it' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + end + + context 'when custom commit template contains placeholder in the middle or beginning of the line' do + let(message_template_name) { <<~MSG.rstrip } + Merge branch '%{source_branch}' into '%{target_branch}' + + %{description} %{title} + + See merge request %{reference} + MSG + + it 'uses custom template' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Merge Request Description + Next line Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + + context 'when description is empty string' do + let(:merge_request_description) { '' } + + it 'does not remove new line characters before empty placeholder' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + end + + context 'when project has template with CRLF newlines' do + let(message_template_name) do + "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}" + end + + it 'converts it to LF newlines' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + Merge Request Description + Next line + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + + context 'when description is empty string' do + let(:merge_request_description) { '' } + + it 'skips description placeholder and removes new line characters before it' do + expect(result_message).to eq <<~MSG.rstrip + Merge branch 'feature' into 'master' + + Bugfix + + See merge request #{merge_request.to_reference(full: true)} + MSG + end + end + end + end + + describe '#merge_message' do + let(:result_message) { subject.merge_message } + + it_behaves_like 'commit message with template', :merge_commit_template + end + + describe '#squash_message' do + let(:result_message) { subject.squash_message } + + it_behaves_like 'commit message with template', :squash_commit_template + end +end diff --git a/spec/lib/gitlab/merge_requests/merge_commit_message_spec.rb b/spec/lib/gitlab/merge_requests/merge_commit_message_spec.rb deleted file mode 100644 index 884f8df5e56..00000000000 --- a/spec/lib/gitlab/merge_requests/merge_commit_message_spec.rb +++ /dev/null @@ -1,219 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::MergeRequests::MergeCommitMessage do - let(:merge_commit_template) { nil } - let(:project) { create(:project, :public, :repository, merge_commit_template: merge_commit_template) } - let(:user) { project.creator } - let(:merge_request_description) { "Merge Request Description\nNext line" } - let(:merge_request_title) { 'Bugfix' } - let(:merge_request) do - create( - :merge_request, - :simple, - source_project: project, - target_project: project, - author: user, - description: merge_request_description, - title: merge_request_title - ) - end - - subject { described_class.new(merge_request: merge_request) } - - it 'returns nil when template is not set in target project' do - expect(subject.message).to be_nil - end - - context 'when project has custom merge commit template' do - let(:merge_commit_template) { <<~MSG.rstrip } - %{title} - - See merge request %{reference} - MSG - - it 'uses custom template' do - expect(subject.message).to eq <<~MSG.rstrip - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - - context 'when project has merge commit template with closed issues' do - let(:merge_commit_template) { <<~MSG.rstrip } - Merge branch '%{source_branch}' into '%{target_branch}' - - %{title} - - %{issues} - - See merge request %{reference} - MSG - - it 'omits issues and new lines when no issues are mentioned in description' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - - context 'when MR closes issues' do - let(:issue_1) { create(:issue, project: project) } - let(:issue_2) { create(:issue, project: project) } - let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" } - - it 'includes them and keeps new line characters' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - Closes #{issue_1.to_reference} and #{issue_2.to_reference} - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - end - - context 'when project has merge commit template with description' do - let(:merge_commit_template) { <<~MSG.rstrip } - Merge branch '%{source_branch}' into '%{target_branch}' - - %{title} - - %{description} - - See merge request %{reference} - MSG - - it 'uses template' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - Merge Request Description - Next line - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - - context 'when description is empty string' do - let(:merge_request_description) { '' } - - it 'skips description placeholder and removes new line characters before it' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - - context 'when description is nil' do - let(:merge_request_description) { nil } - - it 'skips description placeholder and removes new line characters before it' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - - context 'when description is blank string' do - let(:merge_request_description) { "\n\r \n" } - - it 'skips description placeholder and removes new line characters before it' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - end - - context 'when custom merge commit template contains placeholder in the middle or beginning of the line' do - let(:merge_commit_template) { <<~MSG.rstrip } - Merge branch '%{source_branch}' into '%{target_branch}' - - %{description} %{title} - - See merge request %{reference} - MSG - - it 'uses custom template' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Merge Request Description - Next line Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - - context 'when description is empty string' do - let(:merge_request_description) { '' } - - it 'does not remove new line characters before empty placeholder' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - end - - context 'when project has template with CRLF newlines' do - let(:merge_commit_template) do - "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}" - end - - it 'converts it to LF newlines' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - Merge Request Description - Next line - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - - context 'when description is empty string' do - let(:merge_request_description) { '' } - - it 'skips description placeholder and removes new line characters before it' do - expect(subject.message).to eq <<~MSG.rstrip - Merge branch 'feature' into 'master' - - Bugfix - - See merge request #{merge_request.to_reference(full: true)} - MSG - end - end - end -end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 92ef9aeb2d8..102800bcca2 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -178,6 +178,13 @@ RSpec.describe MergeRequest, factory_default: :keep do it 'returns the merge request title' do expect(subject.default_squash_commit_message).to eq(subject.title) end + + it 'uses template from target project' do + subject.target_project.squash_commit_template = 'Squashed branch %{source_branch} into %{target_branch}' + + expect(subject.default_squash_commit_message) + .to eq('Squashed branch master into feature') + end end describe 'modules' do diff --git a/spec/support/frontend_fixtures.rb b/spec/support/frontend_fixtures.rb new file mode 100644 index 00000000000..5587d9059dd --- /dev/null +++ b/spec/support/frontend_fixtures.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +return unless ENV['CI'] +return unless ENV['GENERATE_FRONTEND_FIXTURES_MAPPING'] == 'true' + +RSpec.configure do |config| + config.before(:suite) do + $fixtures_mapping = Hash.new { |h, k| h[k] = [] } # rubocop:disable Style/GlobalVars + end + + config.after(:suite) do + next unless ENV['FRONTEND_FIXTURES_MAPPING_PATH'] + + File.write(ENV['FRONTEND_FIXTURES_MAPPING_PATH'], $fixtures_mapping.to_json) # rubocop:disable Style/GlobalVars + end +end diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb index fb909008f12..84cd0181533 100644 --- a/spec/support/helpers/javascript_fixtures_helpers.rb +++ b/spec/support/helpers/javascript_fixtures_helpers.rb @@ -13,6 +13,12 @@ module JavaScriptFixturesHelpers included do |base| base.around do |example| + # Don't actually run the example when we're only interested in the `test file -> JSON frontend fixture` mapping + if ENV['GENERATE_FRONTEND_FIXTURES_MAPPING'] == 'true' + $fixtures_mapping[example.metadata[:file_path].delete_prefix('./')] << File.join(fixture_root_path, example.description) # rubocop:disable Style/GlobalVars + next + end + # pick an arbitrary date from the past, so tests are not time dependent # Also see spec/frontend/__helpers__/fake_date/jest.js Timecop.freeze(Time.utc(2015, 7, 3, 10)) { example.run } diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb index 60f4c1664f7..8c96f286c79 100644 --- a/spec/views/projects/edit.html.haml_spec.rb +++ b/spec/views/projects/edit.html.haml_spec.rb @@ -92,6 +92,22 @@ RSpec.describe 'projects/edit' do end end + context 'squash template' do + it 'displays a placeholder if none is set' do + render + + expect(rendered).to have_field('project[squash_commit_template]', placeholder: '%{title}') + end + + it 'displays the user entered value' do + project.update!(squash_commit_template: '%{first_multiline_commit}') + + render + + expect(rendered).to have_field('project[squash_commit_template]', with: '%{first_multiline_commit}') + end + end + context 'forking' do before do assign(:project, project) diff --git a/tooling/bin/find_changes b/tooling/bin/find_changes index 20df085879a..c6b8bafbd85 100755 --- a/tooling/bin/find_changes +++ b/tooling/bin/find_changes @@ -3,19 +3,76 @@ require 'gitlab' -gitlab_token = ENV.fetch('PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE', '') -gitlab_endpoint = ENV.fetch('CI_API_V4_URL') -mr_project_path = ENV.fetch('CI_MERGE_REQUEST_PROJECT_PATH') -mr_iid = ENV.fetch('CI_MERGE_REQUEST_IID') +class FindChanges # rubocop:disable Gitlab/NamespacedClass + def initialize(output_file:, matched_tests_file: nil, frontend_fixtures_mapping_path: nil) + @gitlab_token = ENV.fetch('PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE', '') + @gitlab_endpoint = ENV.fetch('CI_API_V4_URL') + @mr_project_path = ENV.fetch('CI_MERGE_REQUEST_PROJECT_PATH') + @mr_iid = ENV.fetch('CI_MERGE_REQUEST_IID') + @output_file = output_file + @matched_tests_file = matched_tests_file + @frontend_fixtures_mapping_path = frontend_fixtures_mapping_path + end -output_file = ARGV.shift + def execute + add_frontend_fixture_files! + + File.write(output_file, file_changes.join(' ')) + end + + private + + def add_frontend_fixture_files? + matched_tests_file && frontend_fixtures_mapping_path + end + + def add_frontend_fixture_files! + return unless add_frontend_fixture_files? + + # If we have a `test file -> JSON frontend fixture` mapping file, we add the files JSON frontend fixtures + # files to the list of changed files so that Jest can automatically run the dependent tests thanks to --findRelatedTests + test_files.each do |test_file| + file_changes.concat(frontend_fixtures_mapping[test_file]) if frontend_fixtures_mapping.key?(test_file) + end + end + + def file_changes + @file_changes ||= + if File.exist?(output_file) + File.read(output_file).split(' ') + else + Gitlab.configure do |config| + config.endpoint = gitlab_endpoint + config.private_token = gitlab_token + end + + mr_changes = Gitlab.merge_request_changes(mr_project_path, mr_iid) -Gitlab.configure do |config| - config.endpoint = gitlab_endpoint - config.private_token = gitlab_token + mr_changes.changes.map { |change| change['new_path'] } + end + end + + def test_files + return [] if !matched_tests_file || !File.exist?(matched_tests_file) + + File.read(matched_tests_file).split(' ') + end + + def frontend_fixtures_mapping + return {} if !frontend_fixtures_mapping_path || !File.exist?(frontend_fixtures_mapping_path) + + JSON.parse(File.read(frontend_fixtures_mapping_path)) # rubocop:disable Gitlab/Json + end + + attr_reader :gitlab_token, :gitlab_endpoint, :mr_project_path, :mr_iid, :output_file, :matched_tests_file, :frontend_fixtures_mapping_path end -mr_changes = Gitlab.merge_request_changes(mr_project_path, mr_iid) -file_changes = mr_changes.changes.map { |change| change['new_path'] } +output_file = ARGV.shift +raise ArgumentError, "An path to an output file must be given as first argument of #{__FILE__}." if output_file.nil? + +matched_tests_file = ARGV.shift +frontend_fixtures_mapping_path = ARGV.shift -File.write(output_file, file_changes.join(' ')) +FindChanges + .new(output_file: output_file, matched_tests_file: matched_tests_file, frontend_fixtures_mapping_path: frontend_fixtures_mapping_path) + .execute diff --git a/yarn.lock b/yarn.lock index f61b3e004cf..cb49b73219d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1491,10 +1491,10 @@ dom-accessibility-api "^0.5.1" pretty-format "^26.4.2" -"@tiptap/core@^2.0.0-beta.140": - version "2.0.0-beta.140" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.140.tgz#9d6771edbc0591b77a46182e863076496a164dda" - integrity sha512-PUgIaYVcTSIeK1dvaGJ91Q/a6EM+PucUMyOCvO0VhU5nFqCkxiqAsBVUqdAomIXthLZya1WgfaKIEbwNEBexJA== +"@tiptap/core@^2.0.0-beta.142": + version "2.0.0-beta.142" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.142.tgz#44e5dc9f63719669fefad422ddae671e8cfe37c4" + integrity sha512-0oAkc2a4ZKI+1yfqAM9EN5Rk9KwJyosLEQutKK43c8ufdUawfIeEJEIG99rsm2AfORSk4e2eyMwkUwUoBht7gw== dependencies: "@types/prosemirror-commands" "^1.0.4" "@types/prosemirror-keymap" "^1.0.4" @@ -1511,10 +1511,10 @@ prosemirror-transform "^1.3.3" prosemirror-view "^1.23.1" -"@tiptap/extension-blockquote@^2.0.0-beta.24": - version "2.0.0-beta.24" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.24.tgz#4dcaf676ded8c3b551efd8f2d6b51cc882ba7d03" - integrity sha512-u9D/ZOziO4rMBKeLj7JA7fOc9h8wU6zrzVEsX9MbJwmuicoJZ1lIQ9cyrFWwmlfznzuXLaAxm3iZuHt7xxMppQ== +"@tiptap/extension-blockquote@^2.0.0-beta.25": + version "2.0.0-beta.25" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.25.tgz#f133381163b92b34d9dc6e0d78a52cf56098eb40" + integrity sha512-994bQQhyOmPGCNTnzHY5UwZceBh42Xcr6MEB3XnnYk5kgfNnOlrnHHt3l8mJeSnC78Bs1cRSs1BKmDF8WufauQ== "@tiptap/extension-bold@^2.0.0-beta.24": version "2.0.0-beta.24" |