diff options
61 files changed, 784 insertions, 182 deletions
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue index 81d96333f7a..ca4d8da2482 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue @@ -192,7 +192,7 @@ export default { <gl-button v-if="canRenderPipelineButton" block - class="gl-mt-3 gl-mb-0 gl-md-display-none" + class="gl-mt-3 gl-mb-3 gl-md-display-none" variant="success" data-testid="run_pipeline_button_mobile" :loading="state.isRunningMergeRequestPipeline" diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index ed852895f10..3eabc10cd22 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -85,7 +85,7 @@ export default { this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader); this.service - .getFolderContent(folder.folder_path) + .getFolderContent(folder.folder_path, folder.state) .then((response) => this.store.setfolderContent(folder, response.data.environments)) .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) .catch(() => { diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js index 122c8f84a2c..dbd82a3c985 100644 --- a/app/assets/javascripts/environments/services/environments_service.js +++ b/app/assets/javascripts/environments/services/environments_service.js @@ -21,7 +21,7 @@ export default class EnvironmentsService { return axios.delete(endpoint, {}); } - getFolderContent(folderUrl) { - return axios.get(`${folderUrl}.json?per_page=${this.folderResults}`); + getFolderContent(folderUrl, scope) { + return axios.get(`${folderUrl}.json?per_page=${this.folderResults}&scope=${scope}`); } } diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue index 09ca1fbe6c6..6a963616224 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue @@ -292,7 +292,7 @@ export default { failure.text }}</gl-alert> <div class="gl-mb-3"> - <h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3> + <h3>{{ s__('PipelineCharts|CI/CD Analytics') }}</h3> </div> <h4 class="gl-my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4> <div class="row"> diff --git a/app/graphql/types/notes/position_type_enum.rb b/app/graphql/types/notes/position_type_enum.rb index abdb2cfc804..9939f6511ce 100644 --- a/app/graphql/types/notes/position_type_enum.rb +++ b/app/graphql/types/notes/position_type_enum.rb @@ -6,8 +6,8 @@ module Types graphql_name 'DiffPositionType' description 'Type of file the position refers to' - value 'text' - value 'image' + value 'text', description: "A text file" + value 'image', description: "An image" end end end diff --git a/app/helpers/analytics/navbar_helper.rb b/app/helpers/analytics/navbar_helper.rb index bc0b5e7c74f..33a5028cdf1 100644 --- a/app/helpers/analytics/navbar_helper.rb +++ b/app/helpers/analytics/navbar_helper.rb @@ -58,7 +58,7 @@ module Analytics return unless project.feature_available?(:builds, current_user) || !project.empty_repo? navbar_sub_item( - title: _('CI / CD'), + title: _('CI/CD'), path: 'pipelines#charts', link: charts_project_pipelines_path(project) ) diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml index 508d8a45255..574750d5f57 100644 --- a/app/views/groups/settings/ci_cd/show.html.haml +++ b/app/views/groups/settings/ci_cd/show.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title _("CI / CD Settings") -- page_title _("CI / CD") +- breadcrumb_title _("CI/CD Settings") +- page_title _("CI/CD") - expanded = expanded_by_default? - general_expanded = @group.errors.empty? ? expanded : true diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 9b5db5b6bb4..7350db64462 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -171,9 +171,9 @@ = _('Repository') = nav_link(controller: :ci_cd) do - = link_to group_settings_ci_cd_path(@group), title: _('CI / CD') do + = link_to group_settings_ci_cd_path(@group), title: _('CI/CD') do %span - = _('CI / CD') + = _('CI/CD') = render 'groups/sidebar/packages_settings' diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 9de0a90b950..4c331dbd69d 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -179,13 +179,13 @@ .nav-icon-container = sprite_icon('rocket') %span.nav-item-name#js-onboarding-pipelines-link - = _('CI / CD') + = _('CI/CD') %ul.sidebar-sub-level-items = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts, :test_cases, :pipeline_editor], html_options: { class: "fly-out-top-item" }) do = link_to project_pipelines_path(@project) do %strong.fly-out-top-item-name - = _('CI / CD') + = _('CI/CD') %li.divider.fly-out-top-item - if project_nav_tab? :pipelines = nav_link(path: ['pipelines#index', 'pipelines#show']) do @@ -418,9 +418,9 @@ = _('Repository') - if !@project.archived? && @project.feature_available?(:builds, current_user) = nav_link(controller: :ci_cd) do - = link_to project_settings_ci_cd_path(@project), title: _('CI / CD') do + = link_to project_settings_ci_cd_path(@project), title: _('CI/CD') do %span - = _('CI / CD') + = _('CI/CD') - if settings_operations_available? = nav_link(controller: [:operations]) do = link_to project_settings_operations_path(@project), title: _('Operations'), data: { qa_selector: 'operations_settings_link' } do diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml index ff728ab2fb3..139f6e3c94d 100644 --- a/app/views/projects/pipelines/charts.html.haml +++ b/app/views/projects/pipelines/charts.html.haml @@ -1,4 +1,4 @@ -- page_title _('CI / CD Analytics') +- page_title _('CI/CD Analytics') #js-project-pipelines-charts-app{ data: { project_path: @project.full_path, should_render_deployment_frequency_charts: should_render_deployment_frequency_charts.to_s } } diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index e7926202579..cca980b5359 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -1,6 +1,6 @@ - @content_class = "limit-container-width" unless fluid_layout -- page_title _("CI / CD Settings") -- page_title _("CI / CD") +- page_title _("CI/CD Settings") +- page_title _("CI/CD") - expanded = expanded_by_default? - general_expanded = @project.errors.empty? ? expanded : true diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index fbd61123078..51483df19d7 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -13,13 +13,6 @@ .user-profile .cover-block.user-cover-block{ class: [('border-bottom' if profile_tabs.empty?)] } = render layout: 'users/cover_controls' do - - if current_user && current_user.id != @user.id - - if current_user.following?(@user) - = link_to user_unfollow_path(@user, :json) , class: link_classes + 'btn gl-button btn-default', method: :post do - = _('Unfollow') - - else - = link_to user_follow_path(@user, :json) , class: link_classes + 'btn gl-button btn-default', method: :post do - = _('Follow') - if @user == current_user = link_to profile_path, class: link_classes + 'btn gl-button btn-default btn-icon has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do @@ -41,6 +34,13 @@ = link_to [:admin, @user], class: link_classes + 'btn gl-button btn-default btn-icon', title: s_('UserProfile|View user in admin area'), data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('user') + - if current_user && current_user.id != @user.id + - if current_user.following?(@user) + = link_to user_unfollow_path(@user, :json) , class: link_classes + 'btn gl-button btn-default', method: :post do + = _('Unfollow') + - else + = link_to user_follow_path(@user, :json) , class: link_classes + 'btn gl-button btn-confirm', method: :post do + = _('Follow') .profile-header{ class: [('with-no-profile-tabs' if profile_tabs.empty?)] } .avatar-holder diff --git a/changelogs/unreleased/273295-fy21q4-foundations-kr2-audit-and-update-buttons-on-userscontroller.yml b/changelogs/unreleased/273295-fy21q4-foundations-kr2-audit-and-update-buttons-on-userscontroller.yml new file mode 100644 index 00000000000..72872c90b16 --- /dev/null +++ b/changelogs/unreleased/273295-fy21q4-foundations-kr2-audit-and-update-buttons-on-userscontroller.yml @@ -0,0 +1,5 @@ +--- +title: Reorder user profile actions and use the confirm variant for the follow button +merge_request: 55999 +author: +type: other diff --git a/changelogs/unreleased/322128-token-used-field.yml b/changelogs/unreleased/322128-token-used-field.yml new file mode 100644 index 00000000000..91738da7046 --- /dev/null +++ b/changelogs/unreleased/322128-token-used-field.yml @@ -0,0 +1,5 @@ +--- +title: Add last_used_at field to cluster agent token +merge_request: 56023 +author: +type: changed diff --git a/changelogs/unreleased/chore-bump-swagger-ui-dist.yml b/changelogs/unreleased/chore-bump-swagger-ui-dist.yml new file mode 100644 index 00000000000..0b609c8ab3b --- /dev/null +++ b/changelogs/unreleased/chore-bump-swagger-ui-dist.yml @@ -0,0 +1,5 @@ +--- +title: Bump swagger-ui-dist to 3.44.1 +merge_request: 55310 +author: Roger Meier +type: changed diff --git a/changelogs/unreleased/dictionary-md-generated-links.yml b/changelogs/unreleased/dictionary-md-generated-links.yml new file mode 100644 index 00000000000..4a5a221127f --- /dev/null +++ b/changelogs/unreleased/dictionary-md-generated-links.yml @@ -0,0 +1,5 @@ +--- +title: Fixed vestigial Anchor links in doc/development/usage_ping/dictionary.md +merge_request: 55874 +author: Raimund Hook +type: fixed diff --git a/changelogs/unreleased/expand-stopped-env.yml b/changelogs/unreleased/expand-stopped-env.yml new file mode 100644 index 00000000000..87b47b59b7e --- /dev/null +++ b/changelogs/unreleased/expand-stopped-env.yml @@ -0,0 +1,5 @@ +--- +title: Expand nested stopped environments +merge_request: 55676 +author: +type: changed diff --git a/changelogs/unreleased/georgekoltsov-bulk-import-migrate-milestones.yml b/changelogs/unreleased/georgekoltsov-bulk-import-migrate-milestones.yml new file mode 100644 index 00000000000..2418c12dad8 --- /dev/null +++ b/changelogs/unreleased/georgekoltsov-bulk-import-migrate-milestones.yml @@ -0,0 +1,5 @@ +--- +title: Migrate group milestones when using Bulk Import +merge_request: 55981 +author: +type: added diff --git a/changelogs/unreleased/pb-store-bare-class-name-for-batched-migrations.yml b/changelogs/unreleased/pb-store-bare-class-name-for-batched-migrations.yml new file mode 100644 index 00000000000..7c27d4ae9a2 --- /dev/null +++ b/changelogs/unreleased/pb-store-bare-class-name-for-batched-migrations.yml @@ -0,0 +1,6 @@ +--- +title: Change the default batch_class_name for batched_background_migrations to the + unqualified class name +merge_request: 56036 +author: +type: changed diff --git a/changelogs/unreleased/update-cicd-naming.yml b/changelogs/unreleased/update-cicd-naming.yml new file mode 100644 index 00000000000..1fc24217393 --- /dev/null +++ b/changelogs/unreleased/update-cicd-naming.yml @@ -0,0 +1,5 @@ +--- +title: Update UI text to CI/CD from CI / CD +merge_request: 56070 +author: +type: other diff --git a/db/migrate/20210308190413_change_batched_background_migrations_batch_class_name_default.rb b/db/migrate/20210308190413_change_batched_background_migrations_batch_class_name_default.rb new file mode 100644 index 00000000000..f841c1e9bd5 --- /dev/null +++ b/db/migrate/20210308190413_change_batched_background_migrations_batch_class_name_default.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class ChangeBatchedBackgroundMigrationsBatchClassNameDefault < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + change_column_default :batched_background_migrations, :batch_class_name, + from: 'Gitlab::Database::BackgroundMigration::PrimaryKeyBatchingStrategy', to: 'PrimaryKeyBatchingStrategy' + end +end diff --git a/db/migrate/20210309181019_add_last_used_at_to_cluster_agent_token.rb b/db/migrate/20210309181019_add_last_used_at_to_cluster_agent_token.rb new file mode 100644 index 00000000000..2a29ab374e5 --- /dev/null +++ b/db/migrate/20210309181019_add_last_used_at_to_cluster_agent_token.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddLastUsedAtToClusterAgentToken < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + add_column :cluster_agent_tokens, :last_used_at, :datetime_with_timezone + end +end diff --git a/db/schema_migrations/20210308190413 b/db/schema_migrations/20210308190413 new file mode 100644 index 00000000000..9ddde51b57e --- /dev/null +++ b/db/schema_migrations/20210308190413 @@ -0,0 +1 @@ +cc131cf37f2af8f0f58c7fa6e5055e88a3b2ed413862c155b0d18383aba06058
\ No newline at end of file diff --git a/db/schema_migrations/20210309181019 b/db/schema_migrations/20210309181019 new file mode 100644 index 00000000000..3055268acc8 --- /dev/null +++ b/db/schema_migrations/20210309181019 @@ -0,0 +1 @@ +a31b85b8ab0db2ad4daa5f2c15eacae97432e75e0e0a28f10d81f6a5aa94c8e0
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 63f0f42c360..a7e3a82899b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9876,7 +9876,7 @@ CREATE TABLE batched_background_migrations ( "interval" smallint NOT NULL, status smallint DEFAULT 0 NOT NULL, job_class_name text NOT NULL, - batch_class_name text DEFAULT 'Gitlab::Database::BackgroundMigration::PrimaryKeyBatchingStrategy'::text NOT NULL, + batch_class_name text DEFAULT 'PrimaryKeyBatchingStrategy'::text NOT NULL, table_name text NOT NULL, column_name text NOT NULL, job_arguments jsonb DEFAULT '"[]"'::jsonb NOT NULL, @@ -11156,6 +11156,7 @@ CREATE TABLE cluster_agent_tokens ( created_by_user_id bigint, description text, name text, + last_used_at timestamp with time zone, CONSTRAINT check_2b79dbb315 CHECK ((char_length(name) <= 255)), CONSTRAINT check_4e4ec5070a CHECK ((char_length(description) <= 1024)), CONSTRAINT check_c60daed227 CHECK ((char_length(token_encrypted) <= 255)) diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md index d73d4ed5d94..f08285e74cd 100644 --- a/doc/administration/packages/container_registry.md +++ b/doc/administration/packages/container_registry.md @@ -1063,6 +1063,70 @@ encounter this error. Administrators can increase the token duration in **Admin area > Settings > CI/CD > Container Registry > Authorization token duration (minutes)**. +### Docker login attempt fails with: 'token signed by untrusted key' + +[Registry relies on GitLab to validate credentials](https://docs.gitlab.com/omnibus/architecture/registry/). +If the registry fails to authenticate valid login attempts, you get the following error message: + +```shell +# docker login gitlab.company.com:4567 +Username: user +Password: +Error response from daemon: login attempt to https://gitlab.company.com:4567/v2/ failed with status: 401 Unauthorized +``` + +And more specifically, this appears in the `/var/log/gitlab/registry/current` log file: + +```plaintext +level=info msg="token signed by untrusted key with ID: "TOKE:NL6Q:7PW6:EXAM:PLET:OKEN:BG27:RCIB:D2S3:EXAM:PLET:OKEN"" +level=warning msg="error authorizing context: invalid token" go.version=go1.12.7 http.request.host="gitlab.company.com:4567" http.request.id=74613829-2655-4f96-8991-1c9fe33869b8 http.request.method=GET http.request.remoteaddr=10.72.11.20 http.request.uri="/v2/" http.request.useragent="docker/19.03.2 go/go1.12.8 git-commit/6a30dfc kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.2 \(linux\))" +``` + +GitLab uses the contents of the certificate key pair's two sides to encrypt the authentication token +for the Registry. This message means that those contents do not align. + +Check which files are in use: + +- `grep -A6 'auth:' /var/opt/gitlab/registry/config.yml` + + ```yaml + ## Container Registry Certificate + auth: + token: + realm: https://gitlab.my.net/jwt/auth + service: container_registry + issuer: omnibus-gitlab-issuer + --> rootcertbundle: /var/opt/gitlab/registry/gitlab-registry.crt + autoredirect: false + ``` + +- `grep -A9 'Container Registry' /var/opt/gitlab/gitlab-rails/etc/gitlab.yml` + + ```yaml + ## Container Registry Key + registry: + enabled: true + host: gitlab.company.com + port: 4567 + api_url: http://127.0.0.1:5000 # internal address to the registry, will be used by GitLab to directly communicate with API + path: /var/opt/gitlab/gitlab-rails/shared/registry + --> key: /var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key + issuer: omnibus-gitlab-issuer + notification_secret: + ``` + +The output of these `openssl` commands should match, proving that the cert-key pair is a match: + +```shell +openssl x509 -noout -modulus -in /var/opt/gitlab/registry/gitlab-registry.crt | openssl sha256 +openssl rsa -noout -modulus -in /var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key | openssl sha256 +``` + +If the two pieces of the certificate do not align, remove the files and run `gitlab-ctl reconfigure` +to regenerate the pair. If you have overridden the automatically generated self-signed pair with +your own certificates and have made sure that their contents align, you can delete the 'registry' +section in your `/etc/gitlab/gitlab-secrets.json` and run `gitlab-ctl reconfigure`. + ### AWS S3 with the GitLab registry error when pushing large images When using AWS S3 with the GitLab registry, an error may occur when pushing diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index d16d1449a16..1a0db78846e 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -5386,8 +5386,8 @@ Type of file the position refers to. | Value | Description | | ----- | ----------- | -| `image` | | -| `text` | | +| `image` | An image | +| `text` | A text file | ### `EntryType` diff --git a/doc/api/projects.md b/doc/api/projects.md index 88111394619..46997a1e8ae 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -45,8 +45,8 @@ GET /projects | `archived` | boolean | **{dotted-circle}** No | Limit by archived status. | | `id_after` | integer | **{dotted-circle}** No | Limit results to projects with IDs greater than the specified ID. | | `id_before` | integer | **{dotted-circle}** No | Limit results to projects with IDs less than the specified ID. | -| `last_activity_after` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ | -| `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ | +| `last_activity_after` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity after specified time. Format: ISO 8601 `YYYY-MM-DDTHH:MM:SSZ` | +| `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity before specified time. Format: ISO 8601 `YYYY-MM-DDTHH:MM:SSZ` | | `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. | | `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). | | `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. `repository_size`, `storage_size`, `packages_size` or `wiki_size` fields are only allowed for admins. Default is `created_at`. | @@ -1005,10 +1005,13 @@ If the project is a fork, and you provide a valid token to authenticate, the } ``` +### Templates for issues and merge requests **(PREMIUM)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55718) in GitLab 13.10. + Users of [GitLab Premium or higher](https://about.gitlab.com/pricing/) -can also see the `issues_template` and `merge_requests_template` parameters: -[introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55718) -in GitLab 13.10. +can also see the `issues_template` and `merge_requests_template` parameters for managing +[issue and merge request description templates](../user/project/description_templates.md). ```json { @@ -1110,7 +1113,7 @@ POST /projects | Attribute | Type | Required | Description | |-------------------------------------------------------------|---------|------------------------|-------------| | `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. | -| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` | | `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. | | `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. | | `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). | @@ -1184,7 +1187,7 @@ POST /projects/user/:user_id | Attribute | Type | Required | Description | |-------------------------------------------------------------|---------|------------------------|-------------| | `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. | -| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` | | `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. | | `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. | | `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). | @@ -1257,7 +1260,7 @@ PUT /projects/:id | Attribute | Type | Required | Description | |-------------------------------------------------------------|----------------|------------------------|-------------| | `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. | -| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` | | `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. | | `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. | | `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual`, or `timed_incremental`). | @@ -1317,8 +1320,8 @@ PUT /projects/:id | `visibility` | string | **{dotted-circle}** No | See [project visibility level](#project-visibility-level). | | `wiki_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. | | `wiki_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable wiki for this project. Use `wiki_access_level` instead. | -| `issues_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Issues. Description is parsed with GitLab Flavored Markdown. | -| `merge_requests_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Merge Requests. Description is parsed with GitLab Flavored Markdown. | +| `issues_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Issues. Description is parsed with GitLab Flavored Markdown. See [Templates for issues and merge requests](#templates-for-issues-and-merge-requests). | +| `merge_requests_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Merge Requests. Description is parsed with GitLab Flavored Markdown. See [Templates for issues and merge requests](#templates-for-issues-and-merge-requests). | ## Fork project @@ -2537,12 +2540,6 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla Read more in the [Project Badges](project_badges.md) documentation. -## Issue and merge request description templates - -The non-default [issue and merge request description templates](../user/project/description_templates.md) -are managed inside the project's repository. So you can manage them with the API -through the [Repositories API](repositories.md) and the [Repository Files API](repository_files.md). - ## Download snapshot of a Git repository > Introduced in GitLab 10.7 diff --git a/doc/development/performance.md b/doc/development/performance.md index f2e3bcf2877..e93dc26e4fc 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -347,13 +347,112 @@ example, you can find which tests take longest to run or which execute the most queries. This can be handy for optimizing our tests or identifying performance issues in our code. -## Memory profiling +## Memory optimization -We can use two approaches, often in combination, to track down memory issues: +We can use a set of different techniques, often in combination, to track down memory issues: - Leaving the code intact and wrapping a profiler around it. +- Use memory allocation counters for requests and services. - Monitor memory usage of the process while disabling/enabling different parts of the code we suspect could be problematic. +### Memory allocations + +Ruby shipped with GitLab includes a special patch to allow [tracing memory allocations](https://gitlab.com/gitlab-org/gitlab/-/issues/296530). +This patch is available by default for +[Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4948), +[CNG](https://gitlab.com/gitlab-org/build/CNG/-/merge_requests/591), +[GitLab CI](https://gitlab.com/gitlab-org/gitlab-build-images/-/merge_requests/355), +[GCK](https://gitlab.com/gitlab-org/gitlab-compose-kit/-/merge_requests/149) +and can additionally be enabled for [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/advanced.md#apply-custom-patches-for-ruby). + +This patch provides a set of 3 metrics that makes it easier to understand efficiency of memory usage for a given codepath: + +- `mem_objects`: the number of objects allocated. +- `mem_bytes`: the number of bytes allocated by malloc. +- `mem_mallocs`: the number of malloc allocations. + +The number of objects and bytes allocated impact how often GC cycles happen. +Fewer objects allocations result in a significantly more responsive application. + +It is advised that web server requests do not allocate more than `100k mem_objects` +and `100M mem_bytes`. You can view the current usage on [GitLab.com](https://log.gprd.gitlab.net/goto/3a9678bb595e3f89a0c7b5c61bcc47b9). + +#### Checking memory pressure of own code + +There are two ways of measuring your own code: + +1. Review `api_json.log`, `development_json.log`, `sidekiq.log` that includes memory allocation counters. +1. Use `Gitlab::Memory::Instrumentation.with_memory_allocations` for a given codeblock and log it. +1. Use [Measuring module](service_measurement.md) + +```json +{"time":"2021-02-15T11:20:40.821Z","severity":"INFO","duration_s":0.27412,"db_duration_s":0.05755,"view_duration_s":0.21657,"status":201,"method":"POST","path":"/api/v4/projects/user/1","mem_objects":86705,"mem_bytes":4277179,"mem_mallocs":22693,"correlation_id":"...} +``` + +#### Different types of allocations + +The `mem_*` values represent different aspects of how objects and memory are allocated in Ruby: + +- The following example will create around of `1000` of `mem_objects` since strings + can be frozen, and while the underlying string object remains the same, we still need to allocate 1000 references to this string: + + ```ruby + Gitlab::Memory::Instrumentation.with_memory_allocations do + 1_000.times { '0123456789' } + end + + => {:mem_objects=>1001, :mem_bytes=>0, :mem_mallocs=>0} + ``` + +- The following example will create around of `1000` of `mem_objects`, as strings are created dynamically. + Each of them will not allocate additional memory, as they fit into Ruby slot of 40 bytes: + + ```ruby + Gitlab::Memory::Instrumentation.with_memory_allocations do + s = '0' + 1_000.times { s * 23 } + end + + => {:mem_objects=>1002, :mem_bytes=>0, :mem_mallocs=>0} + ``` + +- The following example will create around of `1000` of `mem_objects`, as strings are created dynamically. + Each of them will allocate additional memory as strings are larger than Ruby slot of 40 bytes: + + ```ruby + Gitlab::Memory::Instrumentation.with_memory_allocations do + s = '0' + 1_000.times { s * 24 } + end + + => {:mem_objects=>1002, :mem_bytes=>32000, :mem_mallocs=>1000} + ``` + +- The following example will allocate over 40kB of data, and perform only a single memory allocation. + The existing object will be reallocated/resized on subsequent iterations: + + ```ruby + Gitlab::Memory::Instrumentation.with_memory_allocations do + str = '' + append = '0123456789012345678901234567890123456789' # 40 bytes + 1_000.times { str.concat(append) } + end + => {:mem_objects=>3, :mem_bytes=>49152, :mem_mallocs=>1} + ``` + +- The following example will create over 1k of objects, perform over 1k of allocations, each time mutating the object. + This does result in copying a lot of data and perform a lot of memory allocations + (as represented by `mem_bytes` counter) indicating very inefficient method of appending string: + + ```ruby + Gitlab::Memory::Instrumentation.with_memory_allocations do + str = '' + append = '0123456789012345678901234567890123456789' # 40 bytes + 1_000.times { str += append } + end + => {:mem_objects=>1003, :mem_bytes=>21968752, :mem_mallocs=>1000} + ``` + ### Using Memory Profiler We can use `memory_profiler` for profiling. diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index ad95e041c2f..b19a4ff426c 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -2818,7 +2818,7 @@ Tiers: `free`, `premium`, `ultimate` ### `counts.ldap_group_links` -Number of groups that are synced via LDAP group sync `https://docs.gitlab.com/ee/user/group/index.html#manage-group-memberships-via-ldap-starter-only` +Number of groups that are synced via LDAP group sync `https://docs.gitlab.com/ee/user/group/index.html#manage-group-memberships-via-ldap` [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216174822_ldap_group_links.yml) @@ -2830,7 +2830,7 @@ Tiers: `premium`, `ultimate` ### `counts.ldap_keys` -Number of keys synced as part of LDAP `https://docs.gitlab.com/ee/administration/auth/ldap/#ldap-sync-configuration-settings-starter-only` +Number of keys synced as part of LDAP `https://docs.gitlab.com/ee/administration/auth/ldap/#ldap-sync-configuration-settings` [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216174824_ldap_keys.yml) @@ -14158,7 +14158,7 @@ Tiers: `free` ### `usage_activity_by_stage.manage.ldap_admin_sync_enabled` -Has the instance configured LDAP Admin Sync `https://docs.gitlab.com/ee/administration/auth/ldap/#administrator-sync-starter-only`? +Has the instance configured LDAP Admin Sync `https://docs.gitlab.com/ee/administration/auth/ldap/#administrator-sync`? [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/settings/20210216180811_ldap_admin_sync_enabled.yml) @@ -14170,7 +14170,7 @@ Tiers: ### `usage_activity_by_stage.manage.ldap_group_sync_enabled` -Has the instance configured LDAP Group Sync `https://docs.gitlab.com/ee/administration/auth/ldap/#group-sync-starter-only`? +Has the instance configured LDAP Group Sync `https://docs.gitlab.com/ee/administration/auth/ldap/#group-sync`? [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/settings/20210216180809_ldap_group_sync_enabled.yml) @@ -14194,7 +14194,7 @@ Tiers: `free` ### `usage_activity_by_stage.manage.ldap_servers` -Number of LDAP servers configured for the instance `https://docs.gitlab.com/ee/administration/auth/ldap/#multiple-ldap-servers-starter-only` +Number of LDAP servers configured for the instance `https://docs.gitlab.com/ee/administration/auth/ldap/#multiple-ldap-servers` [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216180807_ldap_servers.yml) diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md index ff0db7efd4e..3c824cb5286 100644 --- a/doc/gitlab-basics/start-using-git.md +++ b/doc/gitlab-basics/start-using-git.md @@ -158,6 +158,7 @@ A **project** in GitLab is what holds a repository, which holds your files. Often, the word "repository" is shortened to "repo". <!-- vale gitlab.Spelling = YES --> <!-- vale gitlab.SubstitutionWarning = YES --> + ### Fork When you want to copy someone else's repository, you [**fork**](../user/project/repository/forking_workflow.md#creating-a-fork) @@ -175,7 +176,7 @@ To create a copy of a remote repository's files on your computer, you can either **download** or **clone**. If you download, you cannot sync it with the remote repository on GitLab. -Cloning a repository is the same as downloading, except it preserves the Git connection +[Cloning](#clone-a-repository) a repository is the same as downloading, except it preserves the Git connection with the remote repository. This allows you to modify the files locally and upload the changes to the remote repository on GitLab. @@ -210,11 +211,13 @@ to GitLab, see how to [convert a local folder into a Git repository](#convert-a- ### Clone a repository To start working locally on an existing remote repository, clone it with the -command `git clone <repository path>`. You can either clone it via [HTTPS](#clone-via-https) or [SSH](#clone-via-ssh), according to your preferred [authentication method](#git-authentication-methods). +command `git clone <repository path>`. You can either clone it via [HTTPS](#clone-via-https) +or [SSH](#clone-via-ssh), according to your preferred [authentication method](#git-authentication-methods). You can find both paths (HTTPS and SSH) by navigating to your project's landing page and clicking **Clone**. GitLab prompts you with both paths, from which you can copy -and paste in your command line. +and paste in your command line. You can also +[clone and open directly in Visual Studio Code](../user/project/repository/index.md#clone-and-open-in-apple-xcode). For example, considering our [sample project](https://gitlab.com/gitlab-tests/sample-project/): diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md index 42439d27c83..7a7523d7c14 100644 --- a/doc/user/group/import/index.md +++ b/doc/user/group/import/index.md @@ -49,6 +49,14 @@ The following resources are migrated to the target instance: - parent epic ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/297459)) - emoji award ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/297466)) - events ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/297465)) +- Milestones ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292427)) + - title + - description + - state (active / closed) + - start date + - due date + - created at + - updated at Any other items are **not** migrated. diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md index 19796de0f51..2fd05ac1748 100644 --- a/doc/user/packages/package_registry/index.md +++ b/doc/user/packages/package_registry/index.md @@ -4,7 +4,7 @@ group: Package 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 --- -# Package Registry +# Package Registry **(FREE)** > [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) to GitLab Free in 13.3. diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md index 307313ffb49..e3fa7ca6dc0 100644 --- a/doc/user/project/description_templates.md +++ b/doc/user/project/description_templates.md @@ -146,6 +146,10 @@ After you add the description, hit **Save changes** for the settings to take effect. Now, every time a new merge request or issue is created, it is pre-filled with the text you entered in the template(s). +[GitLab versions 13.10 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/885) +provide `issues_template` and `merge_requests_template` attributes in the +[Projects API](../../api/projects.md) to help you keep your templates up to date. + ## Description template example We make use of description templates for issues and merge requests in the GitLab project. diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md index 11b7f2445e7..eb426a9f126 100644 --- a/doc/user/project/import/github.md +++ b/doc/user/project/import/github.md @@ -49,8 +49,9 @@ The steps you take depend on whether you are importing from GitHub.com or GitHub [GitHub Rake task](../../../administration/raketasks/github_import.md) to import projects without the constraints of a [Sidekiq](../../../development/sidekiq_style_guide.md) worker. - If you're importing from GitHub Enterprise to your self-managed GitLab instance, you must first enable - [GitHub integration](../../../integration/github.md). However, you cannot import projects from GitHub Enterprise to GitLab.com. -- If you're importing from GitHub.com to your self-managed GitLab instance, you do not need to set up GitHub integration. + [GitHub integration](../../../integration/github.md). + - To import projects from GitHub Enterprise to GitLab.com, use the [Import API](../../../api/import.md). +- If you're importing from GitHub.com to your self-managed GitLab instance, you do not need to set up GitHub integration. You can use the [Import API](../../../api/import.md). ## How it works diff --git a/doc/user/snippets.md b/doc/user/snippets.md index 4750adeb1a3..30da6eceaf4 100644 --- a/doc/user/snippets.md +++ b/doc/user/snippets.md @@ -166,8 +166,8 @@ There are two main ways of how you can discover snippets in GitLab. For exploring all snippets that are visible to you, you can go to the Snippets dashboard of your GitLab instance via the top navigation. For GitLab.com you can -navigate to an [overview]((https://gitlab.com/dashboard/snippets)) that shows snippets -you created and allows you to explore all snippets. +visit [GitLab Snippets](http://snippets.gitlab.com/) or navigate to an [overview]((https://gitlab.com/dashboard/snippets)) that shows snippets +you created and allows you to explore all snippets. To discover snippets that belong to a specific project, navigate to the Snippets page via the left side navigation on the project page. diff --git a/lib/bulk_imports/groups/graphql/get_milestones_query.rb b/lib/bulk_imports/groups/graphql/get_milestones_query.rb new file mode 100644 index 00000000000..2ade87e6fa0 --- /dev/null +++ b/lib/bulk_imports/groups/graphql/get_milestones_query.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module BulkImports + module Groups + module Graphql + module GetMilestonesQuery + extend self + + def to_s + <<-'GRAPHQL' + query ($full_path: ID!, $cursor: String) { + group(fullPath: $full_path) { + milestones(first: 100, after: $cursor, includeDescendants: false) { + page_info: pageInfo { + end_cursor: endCursor + has_next_page: hasNextPage + } + nodes { + title + description + state + start_date: startDate + due_date: dueDate + created_at: createdAt + updated_at: updatedAt + } + } + } + } + GRAPHQL + end + + def variables(context) + { + full_path: context.entity.source_full_path, + cursor: context.entity.next_page_for(:milestones) + } + end + + def base_path + %w[data group milestones] + end + + def data_path + base_path << 'nodes' + end + + def page_info_path + base_path << 'page_info' + end + end + end + end +end diff --git a/lib/bulk_imports/groups/pipelines/milestones_pipeline.rb b/lib/bulk_imports/groups/pipelines/milestones_pipeline.rb new file mode 100644 index 00000000000..8497162e0e7 --- /dev/null +++ b/lib/bulk_imports/groups/pipelines/milestones_pipeline.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module BulkImports + module Groups + module Pipelines + class MilestonesPipeline + include Pipeline + + extractor BulkImports::Common::Extractors::GraphqlExtractor, + query: BulkImports::Groups::Graphql::GetMilestonesQuery + + transformer Common::Transformers::ProhibitedAttributesTransformer + + def load(context, data) + return unless data + + raise ::BulkImports::Pipeline::NotAllowedError unless authorized? + + context.group.milestones.create!(data) + end + + def after_run(extracted_data) + context.entity.update_tracker_for( + relation: :milestones, + has_next_page: extracted_data.has_next_page?, + next_page: extracted_data.next_page + ) + + if extracted_data.has_next_page? + run + end + end + + private + + def authorized? + context.current_user.can?(:admin_milestone, context.group) + end + end + end + end +end diff --git a/lib/bulk_imports/importers/group_importer.rb b/lib/bulk_imports/importers/group_importer.rb index f967b7ad7ab..f016b552fd4 100644 --- a/lib/bulk_imports/importers/group_importer.rb +++ b/lib/bulk_imports/importers/group_importer.rb @@ -24,7 +24,8 @@ module BulkImports BulkImports::Groups::Pipelines::GroupPipeline, BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline, BulkImports::Groups::Pipelines::MembersPipeline, - BulkImports::Groups::Pipelines::LabelsPipeline + BulkImports::Groups::Pipelines::LabelsPipeline, + BulkImports::Groups::Pipelines::MilestonesPipeline ] end end diff --git a/lib/gitlab/database/background_migration/primary_key_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb index 46af1371dad..80693728e86 100644 --- a/lib/gitlab/database/background_migration/primary_key_batching_strategy.rb +++ b/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb @@ -1,11 +1,22 @@ # frozen_string_literal: true module Gitlab - module Database - module BackgroundMigration + module BackgroundMigration + module BatchingStrategies + # Generic batching class for use with a BatchedBackgroundMigration. + # Batches over the given table and column combination, returning the MIN() and MAX() + # values for the next batch as an array. + # + # If no more batches exist in the table, returns nil. class PrimaryKeyBatchingStrategy include Gitlab::Database::DynamicModelHelpers + # Finds and returns the next batch in the table. + # + # table_name - The table to batch over + # column_name - The column to batch over + # batch_min_value - The minimum value which the next batch will start at + # batch_size - The size of the next batch def next_batch(table_name, column_name, batch_min_value:, batch_size:) model_class = define_batchable_model(table_name) diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index dbb48a81030..d3f030c3b36 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -99,10 +99,18 @@ module Gitlab initial_config end + def find_sha(project) + branches = project&.repository&.branches || [] + + unless branches.empty? + project.repository.root_ref_sha + end + end + def build_context(project:, sha:, user:, parent_pipeline:) Config::External::Context.new( project: project, - sha: sha || project&.repository&.root_ref_sha, + sha: sha || find_sha(project), user: user, parent_pipeline: parent_pipeline, variables: project&.predefined_variables&.to_runner_variables) diff --git a/lib/gitlab/ci/lint.rb b/lib/gitlab/ci/lint.rb index 364e67db02b..4a7c11ee26e 100644 --- a/lib/gitlab/ci/lint.rb +++ b/lib/gitlab/ci/lint.rb @@ -21,7 +21,7 @@ module Gitlab def initialize(project:, current_user:, sha: nil) @project = project @current_user = current_user - @sha = sha || project.repository.commit.sha + @sha = sha || project.repository.commit&.sha end def validate(content, dry_run: false) diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb index 316a6dafeea..0c9add9b355 100644 --- a/lib/gitlab/database/background_migration/batched_migration.rb +++ b/lib/gitlab/database/background_migration/batched_migration.rb @@ -4,6 +4,9 @@ module Gitlab module Database module BackgroundMigration class BatchedMigration < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord + JOB_CLASS_MODULE = 'Gitlab::BackgroundMigration' + BATCH_CLASS_MODULE = "#{JOB_CLASS_MODULE}::BatchingStrategies".freeze + self.table_name = :batched_background_migrations has_many :batched_jobs, foreign_key: :batched_background_migration_id @@ -20,10 +23,6 @@ module Gitlab finished: 3 } - def self.remove_toplevel_prefix(name) - name&.sub(/\A::/, '') - end - def interval_elapsed? last_job.nil? || last_job.created_at <= Time.current - interval end @@ -37,19 +36,19 @@ module Gitlab end def job_class - job_class_name.constantize + "#{JOB_CLASS_MODULE}::#{job_class_name}".constantize end def batch_class - batch_class_name.constantize + "#{BATCH_CLASS_MODULE}::#{batch_class_name}".constantize end def job_class_name=(class_name) - write_attribute(:job_class_name, self.class.remove_toplevel_prefix(class_name)) + write_attribute(:job_class_name, class_name.demodulize) end def batch_class_name=(class_name) - write_attribute(:batch_class_name, self.class.remove_toplevel_prefix(class_name)) + write_attribute(:batch_class_name, class_name.demodulize) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 3350b35020e..c931c4a3676 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5207,15 +5207,6 @@ msgstr "" msgid "CHANGELOG" msgstr "" -msgid "CI / CD" -msgstr "" - -msgid "CI / CD Analytics" -msgstr "" - -msgid "CI / CD Settings" -msgstr "" - msgid "CI Lint" msgstr "" @@ -5234,6 +5225,12 @@ msgstr "" msgid "CI/CD" msgstr "" +msgid "CI/CD Analytics" +msgstr "" + +msgid "CI/CD Settings" +msgstr "" + msgid "CI/CD configuration" msgstr "" @@ -6277,6 +6274,21 @@ msgstr "" msgid "Closes this %{quick_action_target}." msgstr "" +msgid "Cloud License" +msgstr "" + +msgid "CloudLicense|Activate" +msgstr "" + +msgid "CloudLicense|Paste your activation code" +msgstr "" + +msgid "CloudLicense|Paste your activation code below" +msgstr "" + +msgid "CloudLicense|This instance is currently using the Core plan." +msgstr "" + msgid "Cluster" msgstr "" @@ -22141,7 +22153,7 @@ msgstr "" msgid "PipelineCharts|An unknown error occurred while processing CI/CD analytics." msgstr "" -msgid "PipelineCharts|CI / CD Analytics" +msgid "PipelineCharts|CI/CD Analytics" msgstr "" msgid "PipelineCharts|Failed:" diff --git a/package.json b/package.json index 42c1d8d0395..21fb234ddd7 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "sql.js": "^0.4.0", "string-hash": "1.1.3", "style-loader": "^1.3.0", - "swagger-ui-dist": "^3.43.0", + "swagger-ui-dist": "^3.44.1", "three": "^0.84.0", "three-orbit-controls": "^82.1.0", "three-stl-loader": "^1.0.4", diff --git a/qa/qa/page/project/sub_menus/settings.rb b/qa/qa/page/project/sub_menus/settings.rb index 47274c8db54..b5058bacccd 100644 --- a/qa/qa/page/project/sub_menus/settings.rb +++ b/qa/qa/page/project/sub_menus/settings.rb @@ -25,7 +25,7 @@ module QA def go_to_ci_cd_settings hover_settings do within_submenu do - click_link('CI / CD') + click_link('CI/CD') end end end diff --git a/spec/factories/gitlab/database/background_migration/batched_migrations.rb b/spec/factories/gitlab/database/background_migration/batched_migrations.rb index 03ae2e664f0..b45f6ff037b 100644 --- a/spec/factories/gitlab/database/background_migration/batched_migrations.rb +++ b/spec/factories/gitlab/database/background_migration/batched_migrations.rb @@ -6,7 +6,7 @@ FactoryBot.define do batch_size { 5 } sub_batch_size { 1 } interval { 2.minutes } - job_class_name { 'Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob' } + job_class_name { 'CopyColumnUsingBackgroundMigrationJob' } table_name { :events } column_name { :id } end diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb index 8001ce0f454..86fe59f003f 100644 --- a/spec/features/projects/active_tabs_spec.rb +++ b/spec/features/projects/active_tabs_spec.rb @@ -132,13 +132,13 @@ RSpec.describe 'Project active tab' do it_behaves_like 'page has active sub tab', _('Value Stream') end - context 'on project Analytics/"CI / CD"' do + context 'on project Analytics/"CI/CD"' do before do - click_tab(_('CI / CD')) + click_tab(_('CI/CD')) end it_behaves_like 'page has active tab', _('Analytics') - it_behaves_like 'page has active sub tab', _('CI / CD') + it_behaves_like 'page has active sub tab', _('CI/CD') end end end diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 27167f95104..de7ff1c473d 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -429,37 +429,67 @@ RSpec.describe 'Environments page', :js do end describe 'environments folders' do - before do - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-1', - state: :available) - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-2', - state: :available) - end + describe 'available environments' do + before do + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-1', + state: :available) + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-2', + state: :available) + end - it 'users unfurls an environment folder' do - visit_environments(project) + it 'users unfurls an environment folder' do + visit_environments(project) - expect(page).not_to have_content 'review-1' - expect(page).not_to have_content 'review-2' - expect(page).to have_content 'staging 2' + expect(page).not_to have_content 'review-1' + expect(page).not_to have_content 'review-2' + expect(page).to have_content 'staging 2' - within('.folder-row') do - find('.folder-name', text: 'staging').click - end + within('.folder-row') do + find('.folder-name', text: 'staging').click + end - expect(page).to have_content 'review-1' - expect(page).to have_content 'review-2' - within('.ci-table') do - within('[data-qa-selector="environment_item"]', text: 'review-1') do - expect(find('.js-auto-stop').text).not_to be_empty + expect(page).to have_content 'review-1' + expect(page).to have_content 'review-2' + within('.ci-table') do + within('[data-qa-selector="environment_item"]', text: 'review-1') do + expect(find('.js-auto-stop').text).not_to be_empty + end + within('[data-qa-selector="environment_item"]', text: 'review-2') do + expect(find('.js-auto-stop').text).not_to be_empty + end end - within('[data-qa-selector="environment_item"]', text: 'review-2') do - expect(find('.js-auto-stop').text).not_to be_empty + end + end + + describe 'stopped environments' do + before do + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-1', + state: :stopped) + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-2', + state: :stopped) + end + + it 'users unfurls an environment folder' do + visit_environments(project, scope: 'stopped') + + expect(page).not_to have_content 'review-1' + expect(page).not_to have_content 'review-2' + expect(page).to have_content 'staging 2' + + within('.folder-row') do + find('.folder-name', text: 'staging').click end + + expect(page).to have_content 'review-1' + expect(page).to have_content 'review-2' end end end diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb index 616c5065c07..e5ba6b503cc 100644 --- a/spec/features/projects/user_sees_sidebar_spec.rb +++ b/spec/features/projects/user_sees_sidebar_spec.rb @@ -201,7 +201,7 @@ RSpec.describe 'Projects > User sees sidebar' do expect(page).to have_content 'Operations' expect(page).not_to have_content 'Repository' - expect(page).not_to have_content 'CI / CD' + expect(page).not_to have_content 'CI/CD' expect(page).not_to have_content 'Merge Requests' end end @@ -213,7 +213,7 @@ RSpec.describe 'Projects > User sees sidebar' do visit project_path(project) within('.nav-sidebar') do - expect(page).to have_content 'CI / CD' + expect(page).to have_content 'CI/CD' end end diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb index 13ae035e8ef..f97c8d820e3 100644 --- a/spec/features/projects/user_uses_shortcuts_spec.rb +++ b/spec/features/projects/user_uses_shortcuts_spec.rb @@ -155,12 +155,12 @@ RSpec.describe 'User uses shortcuts', :js do end end - context 'when navigating to the CI / CD pages' do + context 'when navigating to the CI/CD pages' do it 'redirects to the Jobs page' do find('body').native.send_key('g') find('body').native.send_key('j') - expect(page).to have_active_navigation('CI / CD') + expect(page).to have_active_navigation('CI/CD') expect(page).to have_active_sub_navigation('Jobs') end end diff --git a/spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb new file mode 100644 index 00000000000..a38505fbf85 --- /dev/null +++ b/spec/lib/bulk_imports/groups/graphql/get_milestones_query_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BulkImports::Groups::Graphql::GetMilestonesQuery do + it 'has a valid query' do + entity = create(:bulk_import_entity) + context = BulkImports::Pipeline::Context.new(entity) + + query = GraphQL::Query.new( + GitlabSchema, + described_class.to_s, + variables: described_class.variables(context) + ) + result = GitlabSchema.static_validator.validate(query) + + expect(result[:errors]).to be_empty + end + + describe '#data_path' do + it 'returns data path' do + expected = %w[data group milestones nodes] + + expect(described_class.data_path).to eq(expected) + end + end + + describe '#page_info_path' do + it 'returns pagination information path' do + expected = %w[data group milestones page_info] + + expect(described_class.page_info_path).to eq(expected) + end + end +end diff --git a/spec/lib/bulk_imports/groups/pipelines/milestones_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/milestones_pipeline_spec.rb new file mode 100644 index 00000000000..f0c34c65257 --- /dev/null +++ b/spec/lib/bulk_imports/groups/pipelines/milestones_pipeline_spec.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BulkImports::Groups::Pipelines::MilestonesPipeline do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:cursor) { 'cursor' } + let_it_be(:timestamp) { Time.new(2020, 01, 01).utc } + let_it_be(:bulk_import) { create(:bulk_import, user: user) } + + let(:entity) do + create( + :bulk_import_entity, + bulk_import: bulk_import, + source_full_path: 'source/full/path', + destination_name: 'My Destination Group', + destination_namespace: group.full_path, + group: group + ) + end + + let(:context) { BulkImports::Pipeline::Context.new(entity) } + + subject { described_class.new(context) } + + def milestone_data(title) + { + 'title' => title, + 'description' => 'desc', + 'state' => 'closed', + 'start_date' => '2020-10-21', + 'due_date' => '2020-10-22', + 'created_at' => timestamp.to_s, + 'updated_at' => timestamp.to_s + } + end + + def extracted_data(title:, has_next_page:, cursor: nil) + page_info = { + 'end_cursor' => cursor, + 'has_next_page' => has_next_page + } + + BulkImports::Pipeline::ExtractedData.new(data: [milestone_data(title)], page_info: page_info) + end + + before do + group.add_owner(user) + end + + describe '#run' do + it 'imports group milestones' do + first_page = extracted_data(title: 'milestone1', has_next_page: true, cursor: cursor) + last_page = extracted_data(title: 'milestone2', has_next_page: false) + + allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor| + allow(extractor) + .to receive(:extract) + .and_return(first_page, last_page) + end + + expect { subject.run }.to change(Milestone, :count).by(2) + + expect(group.milestones.pluck(:title)).to contain_exactly('milestone1', 'milestone2') + + milestone = group.milestones.last + + expect(milestone.description).to eq('desc') + expect(milestone.state).to eq('closed') + expect(milestone.start_date.to_s).to eq('2020-10-21') + expect(milestone.due_date.to_s).to eq('2020-10-22') + expect(milestone.created_at).to eq(timestamp) + expect(milestone.updated_at).to eq(timestamp) + end + end + + describe '#after_run' do + context 'when extracted data has next page' do + it 'updates tracker information and runs pipeline again' do + data = extracted_data(title: 'milestone', has_next_page: true, cursor: cursor) + + expect(subject).to receive(:run) + + subject.after_run(data) + + tracker = entity.trackers.find_by(relation: :milestones) + + expect(tracker.has_next_page).to eq(true) + expect(tracker.next_page).to eq(cursor) + end + end + + context 'when extracted data has no next page' do + it 'updates tracker information and does not run pipeline' do + data = extracted_data(title: 'milestone', has_next_page: false) + + expect(subject).not_to receive(:run) + + subject.after_run(data) + + tracker = entity.trackers.find_by(relation: :milestones) + + expect(tracker.has_next_page).to eq(false) + expect(tracker.next_page).to be_nil + end + end + end + + describe '#load' do + it 'creates the milestone' do + data = milestone_data('milestone') + + expect { subject.load(context, data) }.to change(Milestone, :count).by(1) + end + + context 'when user is not authorized to create the milestone' do + before do + allow(user).to receive(:can?).with(:admin_milestone, group).and_return(false) + end + + it 'raises NotAllowedError' do + data = extracted_data(title: 'milestone', has_next_page: false) + + expect { subject.load(context, data) }.to raise_error(::BulkImports::Pipeline::NotAllowedError) + end + end + end + + describe 'pipeline parts' do + it { expect(described_class).to include_module(BulkImports::Pipeline) } + it { expect(described_class).to include_module(BulkImports::Pipeline::Runner) } + + it 'has extractors' do + expect(described_class.get_extractor) + .to eq( + klass: BulkImports::Common::Extractors::GraphqlExtractor, + options: { + query: BulkImports::Groups::Graphql::GetMilestonesQuery + } + ) + end + + it 'has transformers' do + expect(described_class.transformers) + .to contain_exactly( + { klass: BulkImports::Common::Transformers::ProhibitedAttributesTransformer, options: nil } + ) + end + end +end diff --git a/spec/lib/bulk_imports/importers/group_importer_spec.rb b/spec/lib/bulk_imports/importers/group_importer_spec.rb index b4fdb7b5e5b..820a0e03c3d 100644 --- a/spec/lib/bulk_imports/importers/group_importer_spec.rb +++ b/spec/lib/bulk_imports/importers/group_importer_spec.rb @@ -22,6 +22,7 @@ RSpec.describe BulkImports::Importers::GroupImporter do expect_to_run_pipeline BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline, context: context expect_to_run_pipeline BulkImports::Groups::Pipelines::MembersPipeline, context: context expect_to_run_pipeline BulkImports::Groups::Pipelines::LabelsPipeline, context: context + expect_to_run_pipeline BulkImports::Groups::Pipelines::MilestonesPipeline, context: context if Gitlab.ee? expect_to_run_pipeline('EE::BulkImports::Groups::Pipelines::EpicsPipeline'.constantize, context: context) diff --git a/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb new file mode 100644 index 00000000000..8febe850e04 --- /dev/null +++ b/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy, '#next_batch' do + let(:batching_strategy) { described_class.new } + let(:namespaces) { table(:namespaces) } + + let!(:namespace1) { namespaces.create!(name: 'batchtest1', path: 'batch-test1') } + let!(:namespace2) { namespaces.create!(name: 'batchtest2', path: 'batch-test2') } + let!(:namespace3) { namespaces.create!(name: 'batchtest3', path: 'batch-test3') } + let!(:namespace4) { namespaces.create!(name: 'batchtest4', path: 'batch-test4') } + + context 'when starting on the first batch' do + it 'returns the bounds of the next batch' do + batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace1.id, batch_size: 3) + + expect(batch_bounds).to eq([namespace1.id, namespace3.id]) + end + end + + context 'when additional batches remain' do + it 'returns the bounds of the next batch' do + batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace2.id, batch_size: 3) + + expect(batch_bounds).to eq([namespace2.id, namespace4.id]) + end + end + + context 'when on the final batch' do + it 'returns the bounds of the next batch' do + batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3) + + expect(batch_bounds).to eq([namespace4.id, namespace4.id]) + end + end + + context 'when no additional batches remain' do + it 'returns nil' do + batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id + 1, batch_size: 1) + + expect(batch_bounds).to be_nil + end + end +end diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb index df0abaae721..f4a939e7c1f 100644 --- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb +++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb @@ -122,7 +122,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m end describe '#batch_class' do - let(:batch_class) { Gitlab::Database::BackgroundMigration::PrimaryKeyBatchingStrategy} + let(:batch_class) { Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy} let(:batched_migration) { build(:batched_background_migration) } it 'returns the class of the batch strategy for the migration' do @@ -130,31 +130,31 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m end end - shared_examples_for 'an attr_writer that normalizes assigned class names' do |attribute_name| + shared_examples_for 'an attr_writer that demodulizes assigned class names' do |attribute_name| let(:batched_migration) { build(:batched_background_migration) } - context 'when the toplevel namespace prefix exists' do - it 'removes the leading prefix' do + context 'when a module name exists' do + it 'removes the module name' do batched_migration.public_send(:"#{attribute_name}=", '::Foo::Bar') - expect(batched_migration[attribute_name]).to eq('Foo::Bar') + expect(batched_migration[attribute_name]).to eq('Bar') end end - context 'when the toplevel namespace prefix does not exist' do + context 'when a module name does not exist' do it 'does not change the given class name' do - batched_migration.public_send(:"#{attribute_name}=", '::Foo::Bar') + batched_migration.public_send(:"#{attribute_name}=", 'Bar') - expect(batched_migration[attribute_name]).to eq('Foo::Bar') + expect(batched_migration[attribute_name]).to eq('Bar') end end end describe '#job_class_name=' do - it_behaves_like 'an attr_writer that normalizes assigned class names', :job_class_name + it_behaves_like 'an attr_writer that demodulizes assigned class names', :job_class_name end describe '#batch_class_name=' do - it_behaves_like 'an attr_writer that normalizes assigned class names', :batch_class_name + it_behaves_like 'an attr_writer that demodulizes assigned class names', :batch_class_name end end diff --git a/spec/lib/gitlab/database/background_migration/primary_key_batching_strategy_spec.rb b/spec/lib/gitlab/database/background_migration/primary_key_batching_strategy_spec.rb deleted file mode 100644 index 6baf6f319cf..00000000000 --- a/spec/lib/gitlab/database/background_migration/primary_key_batching_strategy_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Database::BackgroundMigration::PrimaryKeyBatchingStrategy, '#next_batch' do - let(:batching_strategy) { described_class.new } - - let_it_be(:event1) { create(:event) } - let_it_be(:event2) { create(:event) } - let_it_be(:event3) { create(:event) } - let_it_be(:event4) { create(:event) } - - context 'when starting on the first batch' do - it 'returns the bounds of the next batch' do - batch_bounds = batching_strategy.next_batch(:events, :id, batch_min_value: event1.id, batch_size: 3) - - expect(batch_bounds).to eq([event1.id, event3.id]) - end - end - - context 'when additional batches remain' do - it 'returns the bounds of the next batch' do - batch_bounds = batching_strategy.next_batch(:events, :id, batch_min_value: event2.id, batch_size: 3) - - expect(batch_bounds).to eq([event2.id, event4.id]) - end - end - - context 'when on the final batch' do - it 'returns the bounds of the next batch' do - batch_bounds = batching_strategy.next_batch(:events, :id, batch_min_value: event4.id, batch_size: 3) - - expect(batch_bounds).to eq([event4.id, event4.id]) - end - end - - context 'when no additional batches remain' do - it 'returns nil' do - batch_bounds = batching_strategy.next_batch(:events, :id, batch_min_value: event4.id + 1, batch_size: 1) - - expect(batch_bounds).to be_nil - end - end -end diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb index b5bf697e9e3..cf8cac773f5 100644 --- a/spec/requests/api/lint_spec.rb +++ b/spec/requests/api/lint_spec.rb @@ -406,6 +406,24 @@ RSpec.describe API::Lint do end end + context 'with an empty repository' do + let_it_be(:empty_project) { create(:project_empty_repo) } + let_it_be(:yaml_content) do + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + end + + before do + empty_project.add_developer(api_user) + end + + it 'passes validation without errors' do + post api("/projects/#{empty_project.id}/ci/lint", api_user), params: { content: yaml_content } + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['valid']).to eq(true) + expect(json_response['errors']).to eq([]) + end + end + context 'when unauthenticated' do let_it_be(:api_user) { nil } diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb index 566cc3d3827..671c0cdf79c 100644 --- a/spec/support/shared_contexts/navbar_structure_context.rb +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -5,7 +5,7 @@ RSpec.shared_context 'project navbar structure' do { nav_item: _('Analytics'), nav_sub_items: [ - _('CI / CD'), + _('CI/CD'), (_('Code Review') if Gitlab.ee?), (_('Merge Request') if Gitlab.ee?), _('Repository'), @@ -63,7 +63,7 @@ RSpec.shared_context 'project navbar structure' do nav_sub_items: [] }, { - nav_item: _('CI / CD'), + nav_item: _('CI/CD'), nav_sub_items: [ _('Pipelines'), s_('Pipelines|Editor'), @@ -111,7 +111,7 @@ RSpec.shared_context 'project navbar structure' do _('Webhooks'), _('Access Tokens'), _('Repository'), - _('CI / CD'), + _('CI/CD'), _('Operations') ].compact } @@ -138,7 +138,7 @@ RSpec.shared_context 'group navbar structure' do _('Integrations'), _('Projects'), _('Repository'), - _('CI / CD'), + _('CI/CD'), _('Packages & Registries'), _('Webhooks') ] diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb index e34d8b91b38..99d7dfc8acb 100644 --- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb @@ -204,7 +204,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do it 'does not show the ci/cd settings tab' do render - expect(rendered).not_to have_link('CI / CD', href: project_settings_ci_cd_path(project)) + expect(rendered).not_to have_link('CI/CD', href: project_settings_ci_cd_path(project)) end end @@ -214,7 +214,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do it 'shows the ci/cd settings tab' do render - expect(rendered).to have_link('CI / CD', href: project_settings_ci_cd_path(project)) + expect(rendered).to have_link('CI/CD', href: project_settings_ci_cd_path(project)) end end end diff --git a/yarn.lock b/yarn.lock index a7b083c33c0..3b5e8d7dcb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11412,10 +11412,10 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= -swagger-ui-dist@^3.43.0: - version "3.43.0" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.43.0.tgz#b064a2cec1d27776f9a124bc70423cfa0bbc0d3f" - integrity sha512-PtE+g23bNbYv8qqAVoPBqNQth8hU5Sl5ZsQ7gHXlO5jlCt31dVTiKI9ArHIT1b23ZzUYTnKsFgPYYFoiWyNCAw== +swagger-ui-dist@^3.44.1: + version "3.44.1" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.44.1.tgz#757385a79698b8ef7045287be585671db4e4a252" + integrity sha512-N0u+aN55bp53RRwi/wFbEbkQxcHqZ445ShZR/Ct1Jg+XCMxYtocrsGavh7kdNKw5+6Rs4QDD6GzUMiT28Z1u3Q== symbol-observable@^1.0.2: version "1.2.0" |