diff options
39 files changed, 327 insertions, 110 deletions
@@ -169,7 +169,7 @@ gem 'asciidoctor-include-ext', '~> 0.4.0', require: false gem 'asciidoctor-plantuml', '~> 0.0.12' gem 'asciidoctor-kroki', '~> 0.5.0', require: false gem 'rouge', '~> 3.30.0' -gem 'truncato', '~> 0.7.11' +gem 'truncato', '~> 0.7.12' gem 'bootstrap_form', '~> 4.2.0' gem 'nokogiri', '~> 1.13.0' gem 'escape_utils', '~> 1.1' diff --git a/Gemfile.lock b/Gemfile.lock index 5a630a545a3..4f70e275509 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1380,7 +1380,7 @@ GEM mixlib-shellout (>= 2.0, < 4.0) net-scp (>= 1.2, < 4.0) net-ssh (>= 2.9, < 7.0) - truncato (0.7.11) + truncato (0.7.12) htmlentities (~> 4.3.1) nokogiri (>= 1.7.0, <= 2.0) tty-color (0.6.0) @@ -1757,7 +1757,7 @@ DEPENDENCIES timecop (~> 0.9.1) timfel-krb5-auth (~> 0.8) toml-rb (~> 2.0) - truncato (~> 0.7.11) + truncato (~> 0.7.12) typhoeus (~> 1.4.0) u2f (~> 0.2.1) undercover (~> 0.4.4) diff --git a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue index 5e5d799d627..fea4b56153f 100644 --- a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue +++ b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue @@ -79,7 +79,7 @@ export default { :title="$options.i18n.copyTrigger" css-class="gl-border-none gl-py-0 gl-px-2" /> - <div class="label-container"> + <div class="gl-display-inline-block gl-ml-3"> <gl-badge v-if="!item.canAccessProject" variant="danger"> <span v-gl-tooltip.viewport @@ -95,7 +95,7 @@ export default { :title="item.description" truncate-target="child" placement="top" - class="trigger-description gl-display-flex" + class="gl-max-w-15 gl-display-flex" > <div class="gl-flex-grow-1 gl-text-truncate">{{ item.description }}</div> </tooltip-on-truncate> diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss index be72ec33465..004dc22c9b8 100644 --- a/app/assets/stylesheets/_page_specific_files.scss +++ b/app/assets/stylesheets/_page_specific_files.scss @@ -27,7 +27,6 @@ @import './pages/registry'; @import './pages/search'; @import './pages/service_desk'; -@import './pages/settings_ci_cd'; @import './pages/settings'; @import './pages/storage_quota'; @import './pages/tree'; diff --git a/app/assets/stylesheets/pages/settings_ci_cd.scss b/app/assets/stylesheets/pages/settings_ci_cd.scss deleted file mode 100644 index 7d74070b4f2..00000000000 --- a/app/assets/stylesheets/pages/settings_ci_cd.scss +++ /dev/null @@ -1,18 +0,0 @@ -.triggers-container { - .label-container { - display: inline-block; - margin-left: 10px; - } -} - -.trigger-description { - max-width: 100px; -} - -.trigger-actions { - white-space: nowrap; -} - -.auto-devops-card { - margin-bottom: $gl-vert-padding; -} diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 537c08c9ebb..d19db2b11ab 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -243,12 +243,12 @@ class Projects::IssuesController < Projects::ApplicationController end def import_csv - if uploader = UploadService.new(project, params[:file]).execute - ImportIssuesCsvWorker.perform_async(current_user.id, project.id, uploader.upload.id) # rubocop:disable CodeReuse/Worker + result = Issues::PrepareImportCsvService.new(project, current_user, file: params[:file]).execute - flash[:notice] = _("Your issues are being imported. Once finished, you'll get a confirmation email.") + if result.success? + flash[:notice] = result.message else - flash[:alert] = _("File upload error.") + flash[:alert] = result.message end redirect_to project_issues_path(project) diff --git a/app/services/ci/runners/reconcile_existing_runner_versions_service.rb b/app/services/ci/runners/reconcile_existing_runner_versions_service.rb index da9be7d7207..1950d82845b 100644 --- a/app/services/ci/runners/reconcile_existing_runner_versions_service.rb +++ b/app/services/ci/runners/reconcile_existing_runner_versions_service.rb @@ -3,8 +3,6 @@ module Ci module Runners class ReconcileExistingRunnerVersionsService - include BaseServiceUtility - VERSION_BATCH_SIZE = 100 def execute @@ -12,7 +10,7 @@ module Ci total_deleted = cleanup_runner_versions(insert_result[:versions_from_runners]) total_updated = update_status_on_outdated_runner_versions(insert_result[:versions_from_runners]) - success({ + ServiceResponse.success(payload: { total_inserted: insert_result[:new_record_count], total_updated: total_updated, total_deleted: total_deleted diff --git a/app/services/ci/runners/unregister_runner_service.rb b/app/services/ci/runners/unregister_runner_service.rb index 4ee1e73c458..742b21f77df 100644 --- a/app/services/ci/runners/unregister_runner_service.rb +++ b/app/services/ci/runners/unregister_runner_service.rb @@ -14,6 +14,7 @@ module Ci def execute @runner&.destroy + ServiceResponse.success end end end diff --git a/app/services/import/prepare_service.rb b/app/services/import/prepare_service.rb new file mode 100644 index 00000000000..278bd463dcd --- /dev/null +++ b/app/services/import/prepare_service.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Import + class PrepareService < ::BaseService + def execute + uploader = UploadService.new(project, params[:file]).execute + + if uploader + enqueue_import(uploader.upload) + + ServiceResponse.success(message: success_message) + else + ServiceResponse.error(message: _('File upload error.')) + end + end + + private + + def enqueue_import(upload) + worker.perform_async(current_user.id, project.id, upload.id) + end + + def worker + raise NotImplementedError + end + + def success_message + raise NotImplementedError + end + end +end diff --git a/app/services/issues/prepare_import_csv_service.rb b/app/services/issues/prepare_import_csv_service.rb new file mode 100644 index 00000000000..7afe363117e --- /dev/null +++ b/app/services/issues/prepare_import_csv_service.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Issues + class PrepareImportCsvService < Import::PrepareService + extend ::Gitlab::Utils::Override + + private + + override :worker + def worker + ImportIssuesCsvWorker + end + + override :success_message + def success_message + _("Your issues are being imported. Once finished, you'll get a confirmation email.") + end + end +end diff --git a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml index c294df5ac62..3691c470ea7 100644 --- a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml +++ b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml @@ -2,7 +2,7 @@ = form_errors(group, pajamas_alert: true) %fieldset .form-group - .card.auto-devops-card + .card.gl-mb-3 .card-body - learn_more_link = link_to _('Learn more.'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer' - help_text = s_('GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found.') diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index 64f45ec89d1..ea77bda0b0f 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -20,7 +20,7 @@ %fieldset.builds-feature.js-auto-devops-settings .form-group = f.fields_for :auto_devops_attributes, @auto_devops do |form| - .card.auto-devops-card + .card.gl-mb-3 .card-body - autodevops_help_link = link_to _('Learn more.'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer' - auto_devops_badge = auto_devops_enabled ? (gl_badge_tag badge_for_auto_devops_scope(@project), { variant: :info }, { class: 'js-instance-default-badge gl-ml-3 gl-mt-n1'}) : '' diff --git a/app/views/projects/triggers/_index.html.haml b/app/views/projects/triggers/_index.html.haml index 8b3d0ef17a4..0c53ed48210 100644 --- a/app/views/projects/triggers/_index.html.haml +++ b/app/views/projects/triggers/_index.html.haml @@ -1,4 +1,4 @@ -.row.gl-mt-3.gl-mb-3.triggers-container +.row.gl-mt-3.gl-mb-3 .col-lg-12 .card .card-header diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml index ce036606a1c..bce7dc8a94b 100644 --- a/app/views/projects/triggers/_trigger.html.haml +++ b/app/views/projects/triggers/_trigger.html.haml @@ -6,7 +6,7 @@ - else %span= trigger.short_token - .label-container + .gl-display-inline-block.gl-ml-3 - unless trigger.can_access_project? = gl_badge_tag s_('Trigger|invalid'), { variant: :danger }, { title: s_('Trigger|Trigger user has insufficient permissions to project'), data: { toggle: 'tooltip', container: 'body' } } @@ -27,7 +27,7 @@ - else Never - %td.text-right.trigger-actions + %td.text-right.gl-white-space-nowrap - revoke_trigger_confirmation = "By revoking a trigger you will break any processes making use of it. Are you sure?" - if can?(current_user, :admin_trigger, trigger) = link_to edit_project_trigger_path(@project, trigger), method: :get, title: "Edit", class: "gl-button btn btn-default btn-icon" do diff --git a/app/workers/ci/runners/reconcile_existing_runner_versions_cron_worker.rb b/app/workers/ci/runners/reconcile_existing_runner_versions_cron_worker.rb index f87f7794792..69ab477c80a 100644 --- a/app/workers/ci/runners/reconcile_existing_runner_versions_cron_worker.rb +++ b/app/workers/ci/runners/reconcile_existing_runner_versions_cron_worker.rb @@ -30,7 +30,7 @@ module Ci end result = ::Ci::Runners::ReconcileExistingRunnerVersionsService.new.execute - result.each { |key, value| log_extra_metadata_on_done(key, value) } + result.payload.each { |key, value| log_extra_metadata_on_done(key, value) } end end end diff --git a/config/initializers/truncato.rb b/config/initializers/truncato.rb deleted file mode 100644 index dd0a8a313c3..00000000000 --- a/config/initializers/truncato.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -# Source: https://github.com/jorgemanrubia/truncato/issues/20#issuecomment-1135105823 -silence_warnings do - Truncato::ARTIFICIAL_ROOT_NAME = 'truncato-artificial-root' -end diff --git a/doc/.vale/gitlab/MultiLineLinks.yml b/doc/.vale/gitlab/MultiLineLinks.yml index bb0efc3a2de..380c5a78189 100644 --- a/doc/.vale/gitlab/MultiLineLinks.yml +++ b/doc/.vale/gitlab/MultiLineLinks.yml @@ -10,5 +10,5 @@ link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html level: warning scope: raw raw: - - '\[[^\]]*?\n[^\]]*?\]\([^\)]*?\)|' - - '\[[^\]]*?\]\([^\)]*?\n[^\)]*\)' + - '\[[^\[\]]*?\n[^\[\]]*?\]\([^\)]*?\)|' + - '\[[^\[\]]*?\]\([^\)]*?\n[^\)]*\)' diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md index 6932a107d20..962cc28bf30 100644 --- a/doc/ci/pipelines/multi_project_pipelines.md +++ b/doc/ci/pipelines/multi_project_pipelines.md @@ -45,8 +45,8 @@ To create multi-project pipelines, you can: > [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8. -When you create a multi-project pipeline in your `.gitlab-ci.yml` file, -you create what is called a *trigger job*. For example: +When you use the [`trigger`](../yaml/index.md#trigger) keyword to create a multi-project +pipeline in your `.gitlab-ci.yml` file, you create what is called a *trigger job*. For example: ```yaml rspec: diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md index 7a1ef61f109..a93d0814391 100644 --- a/doc/ci/variables/where_variables_can_be_used.md +++ b/doc/ci/variables/where_variables_can_be_used.md @@ -40,6 +40,7 @@ There are two places defined variables can be used. On the: | [`services:name`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). | | [`services`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). | | [`tags`](../yaml/index.md#tags) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35742) in GitLab 14.1. | +| [`trigger` and `trigger:project`](../yaml/index.md#trigger) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367660) in GitLab 15.3. | | [`variables`](../yaml/index.md#variables) | yes | GitLab/Runner | The variable expansion is first made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab, and then any unrecognized or unavailable variables are expanded by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). | ### `config.toml` file diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 2540650abb7..aea40af21ab 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -3889,7 +3889,9 @@ Use `trigger` to start a downstream pipeline that is either: **Possible inputs**: -- For multi-project pipelines, path to the downstream project. +- For multi-project pipelines, path to the downstream project. CI/CD variables + [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file) + in GitLab 15.3 and later. - For child pipelines, path to the child pipeline CI/CD configuration file. **Example of `trigger` for multi-project pipeline**: diff --git a/doc/ci/yaml/script.md b/doc/ci/yaml/script.md index 1064ecb391b..4bffcbca1cc 100644 --- a/doc/ci/yaml/script.md +++ b/doc/ci/yaml/script.md @@ -197,23 +197,17 @@ or by running commands or programs that output ANSI escape codes. For example, using [Bash with color codes](https://misc.flogisoft.com/bash/tip_colors_and_formatting): -<!-- vale gitlab.MultiLineLinks = NO --> - ```yaml job: script: - echo -e "\e[31mThis text is red,\e[0m but this text isn't\e[31m however this text is red again." ``` -<!-- vale gitlab.MultiLineLinks = YES --> - You can define the color codes in Shell environment variables, or even [custom CI/CD variables](../variables/index.md#custom-cicd-variables), which makes the commands easier to read and reusable. For example, using the same example as above and environment variables defined in a `before_script`: -<!-- vale gitlab.MultiLineLinks = NO --> - ```yaml job: before_script: @@ -223,8 +217,6 @@ job: - echo "This text is not colored" ``` -<!-- vale gitlab.MultiLineLinks = YES --> - Or with [PowerShell color codes](https://superuser.com/a/1259916): ```yaml diff --git a/doc/development/database/index.md b/doc/development/database/index.md index fb8782f95d0..8cf9a2eec04 100644 --- a/doc/development/database/index.md +++ b/doc/development/database/index.md @@ -23,19 +23,30 @@ info: To determine the technical writer assigned to the Stage/Group associated w ## Migrations +- [Different types of migrations](../migration_style_guide.md#choose-an-appropriate-migration-type) +- [Create a regular migration](../migration_style_guide.md#create-a-regular-schema-migration), including creating new models +- [Post-deployment migrations guidelines](post_deployment_migrations.md) and [how to create one](post_deployment_migrations.md#creating-migrations) +- [Background migrations guidelines](background_migrations.md) +- [Batched background migrations guidelines](batched_background_migrations.md) +- [Deleting migrations](deleting_migrations.md) +- [Running database migrations](database_debugging.md#migration-wrangling) - [Migrations for multiple databases](migrations_for_multiple_databases.md) - [Avoiding downtime in migrations](avoiding_downtime_in_migrations.md) -- [SQL guidelines](../sql.md) for working with SQL queries +- [When and how to write Rails migrations tests](../testing_guide/testing_migrations_guide.md) - [Migrations style guide](../migration_style_guide.md) for creating safe SQL migrations - [Testing Rails migrations](../testing_guide/testing_migrations_guide.md) guide - [Post deployment migrations](post_deployment_migrations.md) - [Background migrations](background_migrations.md) - [Swapping tables](swapping_tables.md) - [Deleting migrations](deleting_migrations.md) +- [SQL guidelines](../sql.md) for working with SQL queries - [Partitioning tables](table_partitioning.md) ## Debugging +- [Resetting the database](database_debugging.md#delete-everything-and-start-over) +- [Accessing the database](database_debugging.md#manually-access-the-database) +- [Troubleshooting and debugging the database](database_debugging.md) - Tracing the source of an SQL query using query comments with [Marginalia](database_query_comments.md) - Tracing the source of an SQL query in Rails console using [Verbose Query Logs](https://guides.rubyonrails.org/debugging_rails_applications.html#verbose-query-logs) @@ -55,7 +66,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w - [Insert into tables in batches](insert_into_tables_in_batches.md) - [Ordering table columns](ordering_table_columns.md) - [Verifying database capabilities](verifying_database_capabilities.md) -- [Database Debugging and Troubleshooting](database_debugging.md) - [Query Count Limits](query_count_limits.md) - [Creating enums](creating_enums.md) - [Client-side connection-pool](client_side_connection_pool.md) diff --git a/doc/development/fe_guide/content_editor.md b/doc/development/fe_guide/content_editor.md index d4c29cb8a24..f262e48b6da 100644 --- a/doc/development/fe_guide/content_editor.md +++ b/doc/development/fe_guide/content_editor.md @@ -296,6 +296,7 @@ const builtInContentEditorExtensions = [ Dropcursor, Emoji, // Other extensions +] ``` ### The Markdown serializer diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index bb38d094d2d..913964d5d39 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -45,6 +45,8 @@ work it needs to perform and how long it takes to complete: One exception is a migration that takes longer but is absolutely critical for the application to operate correctly. For example, you might have indices that enforce unique tuples, or that are needed for query performance in critical parts of the application. In cases where the migration would be unacceptably slow, however, a better option might be to guard the feature with a [feature flag](feature_flags/index.md) and perform a post-deployment migration instead. The feature can then be turned on after the migration finishes. + + Migrations used to add new models are also part of these regular schema migrations. The only differences are the Rails command used to generate the migrations and the additional generated files, one for the model and one for the model's spec. 1. [**Post-deployment migrations.**](database/post_deployment_migrations.md) These are Rails migrations in `db/post_migrate` and are run independently from the GitLab.com deployments. Pending post migrations are executed on a daily basis at the discretion of release manager through the [post-deploy migration pipeline](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/post_deploy_migration/readme.md#how-to-determine-if-a-post-deploy-migration-has-been-executed-on-gitlabcom). @@ -109,6 +111,20 @@ bundle exec rails g migration migration_name_here This generates the migration file in `db/migrate`. +### Regular schema migrations to add new models + +To create a new model you can use the following Rails generator: + +```shell +bundle exec rails g model model_name_here +``` + +This will generate: + +- the migration file in `db/migrate` +- the model file in `app/models` +- the spec file in `spec/models` + ## Schema Changes Changes to the schema should be committed to `db/structure.sql`. This @@ -269,7 +285,7 @@ which is a "versioned" class. For new migrations, the latest version should be u can be looked up in `Gitlab::Database::Migration::MIGRATION_CLASSES`) to use the latest version of migration helpers. -In this example, we use version 1.0 of the migration class: +In this example, we use version 2.0 of the migration class: ```ruby class TestMigration < Gitlab::Database::Migration[2.0] diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index c265cbaf3f9..09ead8d2a8f 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -424,13 +424,9 @@ For performance reasons, if a query returns more than 10,000 records, [GitLab ex ### Visibility settings -If created before GitLab 12.2 (July 2019), these items have the +Projects, groups, and snippets have the [Internal visibility](../public_access.md#internal-projects-and-groups) -setting [disabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/12388): - -- Projects -- Groups -- Snippets +setting [disabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/12388). ### SSH maximum number of connections diff --git a/doc/user/group/access_and_permissions.md b/doc/user/group/access_and_permissions.md index 2ecfda576ba..c469d6c2f6d 100644 --- a/doc/user/group/access_and_permissions.md +++ b/doc/user/group/access_and_permissions.md @@ -132,8 +132,9 @@ To prevent sharing outside of the group's hierarchy: ## Prevent a project from being shared with groups -Prevent projects in a group from [sharing -a project with another group](../project/members/share_project_with_groups.md) to enable tighter control over project access. +Prevent projects in a group from +[sharing a project with another group](../project/members/share_project_with_groups.md) +to enable tighter control over project access. To prevent a project from being shared with other groups: diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md index 4677c8216c9..3d5b855a9d3 100644 --- a/doc/user/project/members/share_project_with_groups.md +++ b/doc/user/project/members/share_project_with_groups.md @@ -83,8 +83,8 @@ The following outcomes occur: ## Share project with group lock -It is possible to prevent projects in a group from [sharing -a project with another group](../members/share_project_with_groups.md). +It is possible to prevent projects in a group from +[sharing a project with another group](../members/share_project_with_groups.md). This allows for tighter control over project access. Learn more about [Share with group lock](../../group/access_and_permissions.md#prevent-a-project-from-being-shared-with-groups). diff --git a/lib/gitlab/ci/variables/helpers.rb b/lib/gitlab/ci/variables/helpers.rb index 3a62f01e2e3..7cc727bb3ea 100644 --- a/lib/gitlab/ci/variables/helpers.rb +++ b/lib/gitlab/ci/variables/helpers.rb @@ -21,7 +21,7 @@ module Gitlab end def transform_from_yaml_variables(vars) - return vars.stringify_keys if vars.is_a?(Hash) + return vars.stringify_keys.transform_values(&:to_s) if vars.is_a?(Hash) vars.to_a.to_h { |var| [var[:key].to_s, var[:value]] } end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index badac688229..c48be8efb1b 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1607,22 +1607,32 @@ RSpec.describe Projects::IssuesController do project.add_developer(user) end - it "returns 302 for project members with developer role" do - import_csv + context 'when upload proceeds correctly' do + it "returns 302 for project members with developer role" do + import_csv - expect(flash[:notice]).to eq(_("Your issues are being imported. Once finished, you'll get a confirmation email.")) - expect(response).to redirect_to(project_issues_path(project)) - end + expect(flash[:notice]).to eq(_("Your issues are being imported. Once finished, you'll get a confirmation email.")) + expect(response).to redirect_to(project_issues_path(project)) + end + + it 'enqueues an import job' do + expect(ImportIssuesCsvWorker).to receive(:perform_async).with(user.id, project.id, Integer) - it "shows error when upload fails" do - expect_next_instance_of(UploadService) do |upload_service| - expect(upload_service).to receive(:execute).and_return(nil) + import_csv end + end - import_csv + context 'when upload fails' do + it "shows error when upload fails" do + expect_next_instance_of(UploadService) do |upload_service| + expect(upload_service).to receive(:execute).and_return(nil) + end - expect(flash[:alert]).to include(_('File upload error.')) - expect(response).to redirect_to(project_issues_path(project)) + import_csv + + expect(flash[:alert]).to include(_('File upload error.')) + expect(response).to redirect_to(project_issues_path(project)) + end end end diff --git a/spec/frontend/ide/utils_spec.js b/spec/frontend/ide/utils_spec.js index 2f8447af518..fd9d481251d 100644 --- a/spec/frontend/ide/utils_spec.js +++ b/spec/frontend/ide/utils_spec.js @@ -46,7 +46,7 @@ describe('WebIDE utils', () => { content: 'SELECT "éêė" from tablename', mimeType: 'application/sql', }), - ).toBeFalsy(); + ).toBe(false); }); it('returns true for ASCII only content for unknown types', () => { @@ -56,7 +56,7 @@ describe('WebIDE utils', () => { content: 'plain text', mimeType: 'application/x-new-type', }), - ).toBeTruthy(); + ).toBe(true); }); it('returns false for non-ASCII content for unknown types', () => { @@ -66,7 +66,7 @@ describe('WebIDE utils', () => { content: '{"éêė":"value"}', mimeType: 'application/octet-stream', }), - ).toBeFalsy(); + ).toBe(false); }); it.each` diff --git a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb index e30a78546af..eb5a37f19f4 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb @@ -45,7 +45,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do before do allow(step).to receive(:workflow_rules_result) .and_return( - double(pass?: true, variables: { 'VAR1' => 'val2' }) + double(pass?: true, variables: { 'VAR1' => 'val2', 'VAR2' => 3 }) ) step.perform! @@ -65,7 +65,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do end it 'saves workflow_rules_result' do - expect(command.workflow_rules_result.variables).to eq({ 'VAR1' => 'val2' }) + expect(command.workflow_rules_result.variables).to eq({ 'VAR1' => 'val2', 'VAR2' => 3 }) end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb index 1e73a7efc65..f7774e199fb 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb @@ -205,6 +205,30 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end end + describe '#rule_variables' do + let(:config) do + { + variables: { VAR1: 11 }, + workflow: { + rules: [{ if: '$CI_PIPELINE_SOURCE', + variables: { SYMBOL: :symbol, STRING: "string", INTEGER: 1 } }, + { when: 'always' }] + }, + rspec: { script: 'rake' } + } + end + + let(:rspec_variables) { command.pipeline_seed.stages[0].statuses[0].variables.to_hash } + + it 'correctly parses rule variables' do + run_chain + + expect(rspec_variables['SYMBOL']).to eq("symbol") + expect(rspec_variables['STRING']).to eq("string") + expect(rspec_variables['INTEGER']).to eq("1") + end + end + context 'N+1 queries' do it 'avoids N+1 queries when calculating variables of jobs', :use_sql_query_cache do warm_up_pipeline, warm_up_command = prepare_pipeline1 diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb index f13b334c10e..fc1055751bd 100644 --- a/spec/lib/gitlab/ci/variables/helpers_spec.rb +++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb @@ -99,6 +99,18 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do it { is_expected.to eq(result) } end + + context 'when variables contain integers and symbols' do + let(:variables) do + { key1: 1, key2: :value2 } + end + + let(:result1) do + { 'key1' => '1', 'key2' => 'value2' } + end + + it { is_expected.to eq(result1) } + end end describe '.inherit_yaml_variables' do diff --git a/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb b/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb index 47a7ae4af4d..1690190320a 100644 --- a/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb +++ b/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb @@ -42,14 +42,13 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' .once .and_call_original - result = nil - expect { result = execute } + expect { execute } .to change { runner_version_14_0_0.reload.status }.from('not_available').to('recommended') .and change { runner_version_14_0_1.reload.status }.from('not_available').to('recommended') .and change { ::Ci::RunnerVersion.find_by(version: '14.0.2')&.status }.from(nil).to('not_available') - expect(result).to eq({ - status: :success, + expect(execute).to be_success + expect(execute.payload).to eq({ total_inserted: 1, # 14.0.2 is inserted total_updated: 3, # 14.0.0, 14.0.1 are updated, and newly inserted 14.0.2's status is calculated total_deleted: 0 @@ -66,13 +65,12 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' end it 'deletes orphan ci_runner_versions entry', :aggregate_failures do - result = nil - expect { result = execute } + expect { execute } .to change { ::Ci::RunnerVersion.find_by_version('14.0.2')&.status }.from('not_available').to(nil) .and not_change { runner_version_14_0_1.reload.status }.from('not_available') - expect(result).to eq({ - status: :success, + expect(execute).to be_success + expect(execute.payload).to eq({ total_inserted: 0, total_updated: 0, total_deleted: 1 # 14.0.2 is deleted @@ -87,11 +85,10 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' end it 'does not modify ci_runner_versions entries', :aggregate_failures do - result = nil - expect { result = execute }.not_to change { runner_version_14_0_1.reload.status }.from('not_available') + expect { execute }.not_to change { runner_version_14_0_1.reload.status }.from('not_available') - expect(result).to eq({ - status: :success, + expect(execute).to be_success + expect(execute.payload).to eq({ total_inserted: 0, total_updated: 0, total_deleted: 0 @@ -106,11 +103,10 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' end it 'makes no changes to ci_runner_versions', :aggregate_failures do - result = nil - expect { result = execute }.not_to change { runner_version_14_0_1.reload.status }.from('not_available') + expect { execute }.not_to change { runner_version_14_0_1.reload.status }.from('not_available') - expect(result).to eq({ - status: :success, + expect(execute).to be_success + expect(execute.payload).to eq({ total_inserted: 0, total_updated: 0, total_deleted: 0 @@ -125,11 +121,10 @@ RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' end it 'does not modify ci_runner_versions entries', :aggregate_failures do - result = nil - expect { result = execute }.not_to change { runner_version_14_0_1.reload.status }.from('not_available') + expect { execute }.not_to change { runner_version_14_0_1.reload.status }.from('not_available') - expect(result).to eq({ - status: :success, + expect(execute).to be_success + expect(execute.payload).to eq({ total_inserted: 0, total_updated: 0, total_deleted: 0 diff --git a/spec/services/ci/runners/unregister_runner_service_spec.rb b/spec/services/ci/runners/unregister_runner_service_spec.rb index df1a0a90067..77fc299e4e1 100644 --- a/spec/services/ci/runners/unregister_runner_service_spec.rb +++ b/spec/services/ci/runners/unregister_runner_service_spec.rb @@ -3,13 +3,16 @@ require 'spec_helper' RSpec.describe ::Ci::Runners::UnregisterRunnerService, '#execute' do - subject { described_class.new(runner, 'some_token').execute } + subject(:execute) { described_class.new(runner, 'some_token').execute } let(:runner) { create(:ci_runner) } it 'destroys runner' do expect(runner).to receive(:destroy).once.and_call_original - expect { subject }.to change { Ci::Runner.count }.by(-1) + + expect do + expect(execute).to be_success + end.to change { Ci::Runner.count }.by(-1) expect(runner[:errors]).to be_nil end end diff --git a/spec/services/import/prepare_service_spec.rb b/spec/services/import/prepare_service_spec.rb new file mode 100644 index 00000000000..0097198f7a9 --- /dev/null +++ b/spec/services/import/prepare_service_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Import::PrepareService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + let(:file) { double } + let(:upload_service) { double } + let(:uploader) { double } + let(:upload) { double } + + let(:service) { described_class.new(project, user, file: file) } + + subject { service.execute } + + context 'when file is uploaded correctly' do + let(:upload_id) { 99 } + + before do + mock_upload + end + + it 'raises NotImplemented error for worker' do + expect { subject }.to raise_error(NotImplementedError) + end + + context 'when a job is enqueued' do + before do + worker = double + + allow(service).to receive(:worker).and_return(worker) + allow(worker).to receive(:perform_async) + end + + it 'raises NotImplemented error for success_message when a job is enqueued' do + expect { subject }.to raise_error(NotImplementedError) + end + + it 'returns a success respnse when a success_message is implemented' do + message = 'It works!' + + allow(service).to receive(:success_message).and_return(message) + + result = subject + + expect(result).to be_success + expect(result.message).to eq(message) + end + end + end + + context 'when file upload fails' do + before do + mock_upload(false) + end + + it 'returns an error message' do + result = subject + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq('File upload error.') + end + end +end diff --git a/spec/services/issues/prepare_import_csv_service_spec.rb b/spec/services/issues/prepare_import_csv_service_spec.rb new file mode 100644 index 00000000000..ded23ee43b9 --- /dev/null +++ b/spec/services/issues/prepare_import_csv_service_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Issues::PrepareImportCsvService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + let(:file) { double } + let(:upload_service) { double } + let(:uploader) { double } + let(:upload) { double } + + let(:subject) do + described_class.new(project, user, file: file).execute + end + + context 'when file is uploaded correctly' do + let(:upload_id) { 99 } + + before do + mock_upload + end + + it 'returns a success message' do + result = subject + + expect(result[:status]).to eq(:success) + expect(result[:message]).to eq("Your issues are being imported. Once finished, you'll get a confirmation email.") + end + + it 'enqueues the ImportRequirementsCsvWorker' do + expect(ImportIssuesCsvWorker).to receive(:perform_async).with(user.id, project.id, upload_id) + + subject + end + end + + context 'when file upload fails' do + before do + mock_upload(false) + end + + it 'returns an error message' do + result = subject + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq('File upload error.') + end + end +end diff --git a/spec/support/shared_contexts/upload_type_check_shared_context.rb b/spec/support/shared_contexts/upload_type_check_shared_context.rb index 60b547c53b5..57b8d7472df 100644 --- a/spec/support/shared_contexts/upload_type_check_shared_context.rb +++ b/spec/support/shared_contexts/upload_type_check_shared_context.rb @@ -16,3 +16,15 @@ RSpec.shared_context 'force content type detection to mime_type' do allow(Gitlab::Utils::MimeType).to receive(:from_io).and_return(mime_type) end end + +def mock_upload(success = true) + allow(UploadService).to receive(:new).with(project, file).and_return(upload_service) + + if success + allow(upload_service).to receive(:execute).and_return(uploader) + allow(uploader).to receive(:upload).and_return(upload) + allow(upload).to receive(:id).and_return(upload_id) + else + allow(upload_service).to receive(:execute).and_return(nil) + end +end diff --git a/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb b/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb index ab310bffe37..1292df62ce5 100644 --- a/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb +++ b/spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Ci::Runners::ReconcileExistingRunnerVersionsCronWorker do it 'executes the service' do expect_next_instance_of(Ci::Runners::ReconcileExistingRunnerVersionsService) do |service| - expect(service).to receive(:execute).and_return({}) + expect(service).to receive(:execute).and_return(ServiceResponse.success) end.exactly(worker_exec_times) perform @@ -30,7 +30,8 @@ RSpec.describe Ci::Runners::ReconcileExistingRunnerVersionsCronWorker do it 'logs the service result' do expect_next_instance_of(Ci::Runners::ReconcileExistingRunnerVersionsService) do |service| - expect(service).to receive(:execute).and_return({ some_job_result_key: 'some_value' }) + expect(service).to receive(:execute) + .and_return(ServiceResponse.success(payload: { some_job_result_key: 'some_value' })) end worker.perform(false) |