diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-31 18:10:43 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-31 18:10:43 +0000 |
commit | 8846ca0ed691f35ceb91dc4c924cb105a611bea4 (patch) | |
tree | 47d7d5f37531c27c5087999fef1335e442ccd95e | |
parent | 2368893df711f330cd210005e616fc3b6003ff31 (diff) | |
download | gitlab-ce-8846ca0ed691f35ceb91dc4c924cb105a611bea4.tar.gz |
Add latest changes from gitlab-org/gitlab@master
46 files changed, 409 insertions, 242 deletions
diff --git a/app/assets/javascripts/blob/components/blob_edit_content.vue b/app/assets/javascripts/blob/components/blob_edit_content.vue index 26ba7b98a39..6293f3bed1c 100644 --- a/app/assets/javascripts/blob/components/blob_edit_content.vue +++ b/app/assets/javascripts/blob/components/blob_edit_content.vue @@ -46,7 +46,7 @@ export default { blobGlobalId: this.fileGlobalId, }); - this.editor.onChangeContent(debounce(this.onFileChange.bind(this), 250)); + this.editor.onDidChangeModelContent(debounce(this.onFileChange.bind(this), 250)); window.requestAnimationFrame(() => { if (!performance.getEntriesByName(SNIPPET_MARK_BLOBS_CONTENT).length) { diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js index 4409d7a33cc..5058ca7122d 100644 --- a/app/assets/javascripts/blob/file_template_mediator.js +++ b/app/assets/javascripts/blob/file_template_mediator.js @@ -1,16 +1,18 @@ import $ from 'jquery'; + import Api from '~/api'; +import toast from '~/vue_shared/plugins/global_toast'; +import { __ } from '~/locale'; +import initPopover from '~/blob/suggest_gitlab_ci_yml'; import { deprecatedCreateFlash as Flash } from '../flash'; + import FileTemplateTypeSelector from './template_selectors/type_selector'; import BlobCiYamlSelector from './template_selectors/ci_yaml_selector'; import DockerfileSelector from './template_selectors/dockerfile_selector'; import GitignoreSelector from './template_selectors/gitignore_selector'; import LicenseSelector from './template_selectors/license_selector'; import MetricsDashboardSelector from './template_selectors/metrics_dashboard_selector'; -import toast from '~/vue_shared/plugins/global_toast'; -import { __ } from '~/locale'; -import initPopover from '~/blob/suggest_gitlab_ci_yml'; export default class FileTemplateMediator { constructor({ editor, currentAction, projectId }) { diff --git a/app/assets/javascripts/blob/utils.js b/app/assets/javascripts/blob/utils.js index 1df4b89a82b..8043c0bbc07 100644 --- a/app/assets/javascripts/blob/utils.js +++ b/app/assets/javascripts/blob/utils.js @@ -6,12 +6,11 @@ export function initEditorLite({ el, ...args }) { alwaysConsumeMouseWheel: false, }, }); - editor.createInstance({ + + return editor.createInstance({ el, ...args, }); - - return editor; } export default () => ({}); diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index e22c9b0d4c4..2a4ab4b8827 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -40,9 +40,10 @@ export default class EditBlob { const MarkdownExtensionPromise = this.options.isMarkdown ? import('~/editor/editor_markdown_ext') : Promise.resolve(false); + const FileTemplateExtensionPromise = import('~/editor/editor_file_template_ext'); - return Promise.all([EditorPromise, MarkdownExtensionPromise]) - .then(([EditorModule, MarkdownExtension]) => { + return Promise.all([EditorPromise, MarkdownExtensionPromise, FileTemplateExtensionPromise]) + .then(([EditorModule, MarkdownExtension, FileTemplateExtension]) => { const EditorLite = EditorModule.default; const editorEl = document.getElementById('editor'); const fileNameEl = @@ -50,18 +51,16 @@ export default class EditBlob { const fileContentEl = document.getElementById('file-content'); const form = document.querySelector('.js-edit-blob-form'); - this.editor = new EditorLite(); + const rootEditor = new EditorLite(); - if (MarkdownExtension) { - this.editor.use(MarkdownExtension.default); - } - - this.editor.createInstance({ + this.editor = rootEditor.createInstance({ el: editorEl, blobPath: fileNameEl.value, blobContent: editorEl.innerText, }); + rootEditor.use([MarkdownExtension.default, FileTemplateExtension.default], this.editor); + fileNameEl.addEventListener('change', () => { this.editor.updateModelLanguage(fileNameEl.value); }); diff --git a/app/assets/javascripts/editor/editor_file_template_ext.js b/app/assets/javascripts/editor/editor_file_template_ext.js new file mode 100644 index 00000000000..343908b831d --- /dev/null +++ b/app/assets/javascripts/editor/editor_file_template_ext.js @@ -0,0 +1,7 @@ +import { Position } from 'monaco-editor'; + +export default { + navigateFileStart() { + this.setPosition(new Position(1, 1)); + }, +}; diff --git a/app/assets/javascripts/editor/editor_lite.js b/app/assets/javascripts/editor/editor_lite.js index d275cf3a205..136a232d9d0 100644 --- a/app/assets/javascripts/editor/editor_lite.js +++ b/app/assets/javascripts/editor/editor_lite.js @@ -1,4 +1,4 @@ -import { editor as monacoEditor, languages as monacoLanguages, Position, Uri } from 'monaco-editor'; +import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monaco-editor'; import { DEFAULT_THEME, themes } from '~/ide/lib/themes'; import languages from '~/ide/lib/languages'; import { defaultEditorOptions } from '~/ide/lib/editor_options'; @@ -73,6 +73,7 @@ export default class Editor { this.instances.splice(index, 1); model.dispose(); }); + instance.updateModelLanguage = path => this.updateModelLanguage(path); // Reference to the model on the editor level will go away in // https://gitlab.com/gitlab-org/gitlab/-/issues/241023 @@ -92,10 +93,6 @@ export default class Editor { delete this.editorEl.dataset.editorLoading; } - onChangeContent(fn) { - return this.model.onDidChangeContent(fn); - } - updateModelLanguage(path) { if (path === this.blobPath) return; this.blobPath = path; @@ -107,46 +104,6 @@ export default class Editor { monacoEditor.setModelLanguage(this.model, id); } - /** - * @deprecated do not use .getValue() directly on the editor. - * This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025 - * Rather use it on the exact instance - */ - getValue() { - return this.instances[0].getValue(); - } - - /** - * @deprecated do not use .setValue() directly on the editor. - * This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025 - * Rather use it on the exact instance - */ - setValue(val) { - this.instances[0].setValue(val); - } - - /** - * @deprecated do not use .focus() directly on the editor. - * This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025 - * Rather use it on the exact instance - */ - focus() { - this.instances[0].focus(); - } - - /** - * @deprecated do not use .updateOptions() directly on the editor. - * This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025 - * Rather use it on the exact instance - */ - updateOptions(options = {}) { - this.instances[0].updateOptions(options); - } - - navigateFileStart() { - this.instances[0].setPosition(new Position(1, 1)); - } - use(exts = [], instance = null) { const extensions = Array.isArray(exts) ? exts : [exts]; if (instance) { diff --git a/app/assets/javascripts/related_issues/constants.js b/app/assets/javascripts/related_issues/constants.js index 1e95bf22ad2..89eae069a24 100644 --- a/app/assets/javascripts/related_issues/constants.js +++ b/app/assets/javascripts/related_issues/constants.js @@ -1,4 +1,4 @@ -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; export const issuableTypesMap = { ISSUE: 'issue', @@ -20,9 +20,21 @@ export const linkedIssueTypesTextMap = { export const autoCompleteTextMap = { true: { - [issuableTypesMap.ISSUE]: __(' or <#issue id>'), - [issuableTypesMap.EPIC]: __(' or <&epic id>'), - [issuableTypesMap.MERGE_REQUEST]: __(' or <!merge request id>'), + [issuableTypesMap.ISSUE]: sprintf( + __(' or %{emphasisStart}#issue id%{emphasisEnd}'), + { emphasisStart: '<', emphasisEnd: '>' }, + false, + ), + [issuableTypesMap.EPIC]: sprintf( + __(' or %{emphasisStart}&epic id%{emphasisEnd}'), + { emphasisStart: '<', emphasisEnd: '>' }, + false, + ), + [issuableTypesMap.MERGE_REQUEST]: sprintf( + __(' or %{emphasisStart}!merge request id%{emphasisEnd}'), + { emphasisStart: '<', emphasisEnd: '>' }, + false, + ), }, false: { [issuableTypesMap.ISSUE]: '', diff --git a/app/controllers/concerns/send_file_upload.rb b/app/controllers/concerns/send_file_upload.rb index 9d4002f5fba..d21d96fd527 100644 --- a/app/controllers/concerns/send_file_upload.rb +++ b/app/controllers/concerns/send_file_upload.rb @@ -53,8 +53,10 @@ module SendFileUpload private def image_scaling_request?(file_upload) - Feature.enabled?(:dynamic_image_resizing, current_user) && - avatar_safe_for_scaling?(file_upload) && valid_image_scaling_width? && current_user + avatar_safe_for_scaling?(file_upload) && + scaling_allowed_by_feature_flags?(file_upload) && + valid_image_scaling_width? && + current_user end def avatar_safe_for_scaling?(file_upload) @@ -68,4 +70,17 @@ module SendFileUpload def valid_image_scaling_width? Avatarable::ALLOWED_IMAGE_SCALER_WIDTHS.include?(params[:width]&.to_i) end + + # We use two separate feature gates to allow image resizing. + # The first, `:dynamic_image_resizing_requester`, based on the content requester. + # Enabling it for the user would allow that user to send resizing requests for any avatar. + # The second, `:dynamic_image_resizing_owner`, based on the content owner. + # Enabling it for the user would allow anyone to send resizing requests against the mentioned user avatar only. + # This flag allows us to operate on trusted data only, more in https://gitlab.com/gitlab-org/gitlab/-/issues/241533. + # Because of this, you need to enable BOTH to serve resized image, + # as you would need at least one allowed requester and at least one allowed avatar. + def scaling_allowed_by_feature_flags?(file_upload) + Feature.enabled?(:dynamic_image_resizing_requester, current_user) && + Feature.enabled?(:dynamic_image_resizing_owner, file_upload.model) + end end diff --git a/app/finders/concerns/merged_at_filter.rb b/app/finders/concerns/merged_at_filter.rb index d2858ba2f88..e92bee3934c 100644 --- a/app/finders/concerns/merged_at_filter.rb +++ b/app/finders/concerns/merged_at_filter.rb @@ -12,7 +12,7 @@ module MergedAtFilter mr_metrics_scope = mr_metrics_scope.merged_before(merged_before) if merged_before.present? scope = items.joins(:metrics).merge(mr_metrics_scope) - scope = target_project_id_filter_on_metrics(scope) if Feature.enabled?(:improved_mr_merged_at_queries) + scope = target_project_id_filter_on_metrics(scope) if Feature.enabled?(:improved_mr_merged_at_queries, default_enabled: true) scope end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/models/project.rb b/app/models/project.rb index 87bb34fb9c2..8073e376c0f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1828,12 +1828,12 @@ class Project < ApplicationRecord end # rubocop: enable CodeReuse/ServiceClass - def mark_pages_as_deployed - ensure_pages_metadatum.update!(deployed: true) + def mark_pages_as_deployed(artifacts_archive: nil) + ensure_pages_metadatum.update!(deployed: true, artifacts_archive: artifacts_archive) end def mark_pages_as_not_deployed - ensure_pages_metadatum.update!(deployed: false) + ensure_pages_metadatum.update!(deployed: false, artifacts_archive: nil) end def write_repository_config(gl_full_path: full_path) diff --git a/app/models/project_pages_metadatum.rb b/app/models/project_pages_metadatum.rb index 1fda388b1ae..8a1db4a9acf 100644 --- a/app/models/project_pages_metadatum.rb +++ b/app/models/project_pages_metadatum.rb @@ -4,6 +4,7 @@ class ProjectPagesMetadatum < ApplicationRecord self.primary_key = :project_id belongs_to :project, inverse_of: :pages_metadatum + belongs_to :artifacts_archive, class_name: 'Ci::JobArtifact' scope :deployed, -> { where(deployed: true) } end diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index 334f5993d15..ea37f2e4ec0 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -52,7 +52,7 @@ module Projects def success @status.success - @project.mark_pages_as_deployed + @project.mark_pages_as_deployed(artifacts_archive: build.job_artifacts_archive) super end diff --git a/changelogs/unreleased/225218-format-conan-package-manager.yml b/changelogs/unreleased/225218-format-conan-package-manager.yml new file mode 100644 index 00000000000..6ae1048d4f8 --- /dev/null +++ b/changelogs/unreleased/225218-format-conan-package-manager.yml @@ -0,0 +1,5 @@ +--- +title: Format Conan package manager in Dependency List +merge_request: 40811 +author: +type: added diff --git a/changelogs/unreleased/225493-Retrieve-SecDash-URL-of-Projects.yml b/changelogs/unreleased/225493-Retrieve-SecDash-URL-of-Projects.yml new file mode 100644 index 00000000000..47f32761592 --- /dev/null +++ b/changelogs/unreleased/225493-Retrieve-SecDash-URL-of-Projects.yml @@ -0,0 +1,6 @@ +--- +title: Retrieve security dashboard URL used on Project Severity status report from + backend +merge_request: 40801 +author: Kev @KevSlashNull +type: changed diff --git a/changelogs/unreleased/238986-add-zip-typ-to-pages-api-using-artifacts-zip-archive.yml b/changelogs/unreleased/238986-add-zip-typ-to-pages-api-using-artifacts-zip-archive.yml new file mode 100644 index 00000000000..48ddc2a60ca --- /dev/null +++ b/changelogs/unreleased/238986-add-zip-typ-to-pages-api-using-artifacts-zip-archive.yml @@ -0,0 +1,5 @@ +--- +title: Save pages build artifact id in pages metadata +merge_request: 40592 +author: +type: added diff --git a/changelogs/unreleased/enable_improved_mr_merged_at_queries_ff_by_default.yml b/changelogs/unreleased/enable_improved_mr_merged_at_queries_ff_by_default.yml new file mode 100644 index 00000000000..115be2994a0 --- /dev/null +++ b/changelogs/unreleased/enable_improved_mr_merged_at_queries_ff_by_default.yml @@ -0,0 +1,5 @@ +--- +title: Improve Productivity Analytics and Merge Request Analytics database queries +merge_request: 40838 +author: +type: performance diff --git a/config/feature_flags/development/dynamic_image_resizing_owner.yml b/config/feature_flags/development/dynamic_image_resizing_owner.yml new file mode 100644 index 00000000000..fd9881fba31 --- /dev/null +++ b/config/feature_flags/development/dynamic_image_resizing_owner.yml @@ -0,0 +1,7 @@ +--- +name: dynamic_image_resizing_owner +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40606 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241533 +group: group::memory +type: development +default_enabled: false diff --git a/config/feature_flags/development/dynamic_image_resizing.yml b/config/feature_flags/development/dynamic_image_resizing_requester.yml index 72547b7736e..e491b14d27d 100644 --- a/config/feature_flags/development/dynamic_image_resizing.yml +++ b/config/feature_flags/development/dynamic_image_resizing_requester.yml @@ -1,7 +1,7 @@ --- -name: dynamic_image_resizing +name: dynamic_image_resizing_requester introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37342 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/233704 group: group::memory type: development -default_enabled: false
\ No newline at end of file +default_enabled: false diff --git a/config/feature_flags/development/improved_mr_merged_at_queries.yml b/config/feature_flags/development/improved_mr_merged_at_queries.yml index 9e717991a7d..bf4c8814540 100644 --- a/config/feature_flags/development/improved_mr_merged_at_queries.yml +++ b/config/feature_flags/development/improved_mr_merged_at_queries.yml @@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39329 rollout_issue_url: group: group::analytics type: development -default_enabled: false +default_enabled: true diff --git a/db/migrate/20200827102234_add_ci_job_artifact_id_to_pages_metadata.rb b/db/migrate/20200827102234_add_ci_job_artifact_id_to_pages_metadata.rb new file mode 100644 index 00000000000..4109c512f14 --- /dev/null +++ b/db/migrate/20200827102234_add_ci_job_artifact_id_to_pages_metadata.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddCiJobArtifactIdToPagesMetadata < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + add_column(:project_pages_metadata, :artifacts_archive_id, :bigint) + end +end diff --git a/db/migrate/20200827104432_add_foreign_key_to_artifacts_archive_id_in_pages_metadata.rb b/db/migrate/20200827104432_add_foreign_key_to_artifacts_archive_id_in_pages_metadata.rb new file mode 100644 index 00000000000..90451ad597a --- /dev/null +++ b/db/migrate/20200827104432_add_foreign_key_to_artifacts_archive_id_in_pages_metadata.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class AddForeignKeyToArtifactsArchiveIdInPagesMetadata < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + INDEX_NAME = "index_project_pages_metadata_on_artifacts_archive_id" + + disable_ddl_transaction! + + def up + add_concurrent_index(:project_pages_metadata, :artifacts_archive_id, name: INDEX_NAME) + add_concurrent_foreign_key(:project_pages_metadata, :ci_job_artifacts, column: :artifacts_archive_id, on_delete: :nullify) + end + + def down + remove_foreign_key_if_exists(:project_pages_metadata, :ci_job_artifacts, column: :artifacts_archive_id) + remove_concurrent_index_by_name(:project_pages_metadata, INDEX_NAME) + end +end diff --git a/db/schema_migrations/20200827102234 b/db/schema_migrations/20200827102234 new file mode 100644 index 00000000000..8f5656a92c6 --- /dev/null +++ b/db/schema_migrations/20200827102234 @@ -0,0 +1 @@ +d38ef8ccd627e70adf0dd9ac8161235e21afccbc59f1e8d95f379f66eb84630e
\ No newline at end of file diff --git a/db/schema_migrations/20200827104432 b/db/schema_migrations/20200827104432 new file mode 100644 index 00000000000..351974f884b --- /dev/null +++ b/db/schema_migrations/20200827104432 @@ -0,0 +1 @@ +99d95dea0dbb10bcaca5515c144c7fcd1e365e69be5eded223379bf61df69bc3
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 1951b9385a7..00eae5f4f43 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -14508,7 +14508,8 @@ ALTER SEQUENCE public.project_mirror_data_id_seq OWNED BY public.project_mirror_ CREATE TABLE public.project_pages_metadata ( project_id bigint NOT NULL, - deployed boolean DEFAULT false NOT NULL + deployed boolean DEFAULT false NOT NULL, + artifacts_archive_id bigint ); CREATE TABLE public.project_repositories ( @@ -20464,6 +20465,8 @@ CREATE UNIQUE INDEX index_project_mirror_data_on_project_id ON public.project_mi CREATE INDEX index_project_mirror_data_on_status ON public.project_mirror_data USING btree (status); +CREATE INDEX index_project_pages_metadata_on_artifacts_archive_id ON public.project_pages_metadata USING btree (artifacts_archive_id); + CREATE UNIQUE INDEX index_project_pages_metadata_on_project_id ON public.project_pages_metadata USING btree (project_id); CREATE INDEX index_project_pages_metadata_on_project_id_and_deployed_is_true ON public.project_pages_metadata USING btree (project_id) WHERE (deployed = true); @@ -21659,6 +21662,9 @@ ALTER TABLE ONLY public.merge_requests ALTER TABLE ONLY public.ci_builds ADD CONSTRAINT fk_6661f4f0e8 FOREIGN KEY (resource_group_id) REFERENCES public.ci_resource_groups(id) ON DELETE SET NULL; +ALTER TABLE ONLY public.project_pages_metadata + ADD CONSTRAINT fk_69366a119e FOREIGN KEY (artifacts_archive_id) REFERENCES public.ci_job_artifacts(id) ON DELETE SET NULL; + ALTER TABLE ONLY public.application_settings ADD CONSTRAINT fk_693b8795e4 FOREIGN KEY (push_rule_id) REFERENCES public.push_rules(id) ON DELETE SET NULL; diff --git a/doc/api/groups.md b/doc/api/groups.md index 21b46457123..45c1814a4c2 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -941,6 +941,7 @@ GET /groups/:id/hooks/:hook_id "job_events": true, "pipeline_events": true, "wiki_page_events": true, + "deployment_events": true, "enable_ssl_verification": true, "created_at": "2012-10-12T17:04:47Z" } @@ -968,6 +969,7 @@ POST /groups/:id/hooks | `job_events` | boolean | no | Trigger hook on job events | | `pipeline_events` | boolean | no | Trigger hook on pipeline events | | `wiki_page_events` | boolean | no | Trigger hook on wiki events | +| `deployment_events` | boolean | no | Trigger hook on deployment events | | `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook | | `token` | string | no | Secret token to validate received payloads; this will not be returned in the response | @@ -994,6 +996,7 @@ PUT /groups/:id/hooks/:hook_id | `job_events` | boolean | no | Trigger hook on job events | | `pipeline_events` | boolean | no | Trigger hook on pipeline events | | `wiki_events` | boolean | no | Trigger hook on wiki events | +| `deployment_events` | boolean | no | Trigger hook on deployment events | | `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook | | `token` | string | no | Secret token to validate received payloads; this will not be returned in the response | diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 9eb90d38457..ef0eb0b1a77 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -142,6 +142,8 @@ The chosen OmniAuth provider is now active and can be used to sign in to GitLab ## Automatically Link Existing Users to OmniAuth Users +> [Introduced in GitLab 13.4.](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36664) + You can automatically link OmniAuth users with existing GitLab users if their email addresses match by adding the following setting: **For Omnibus installations** diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 6b14f93735b..e8dbfb243f7 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -61,6 +61,7 @@ The following languages and dependency managers are supported: | Language (package managers) | Supported files | Scan tool(s) | |----------------------------- | --------------- | ------------ | | C# .NET ([NuGet](https://www.nuget.org/) 4.9+) | [`packages.lock.json`](https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#enabling-lock-file) | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | +| C/C++ ([Conan](https://conan.io/)) | [`conan.lock`](https://docs.conan.io/en/latest/versioning/lockfiles.html) | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | | Java ([Gradle](https://gradle.org/), [Maven](https://maven.apache.org/)) | `build.gradle`, `build.gradle.kts`, `pom.xml` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | | JavaScript ([npm](https://www.npmjs.com/), [yarn](https://classic.yarnpkg.com/en/)) | `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [Retire.js](https://retirejs.github.io/retire.js/) | | Go ([Golang](https://golang.org/)) | `go.sum` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | diff --git a/doc/user/application_security/vulnerabilities/img/vulnerability_page_v13_1.png b/doc/user/application_security/vulnerabilities/img/vulnerability_page_v13_1.png Binary files differdeleted file mode 100644 index 30a7195e1ab..00000000000 --- a/doc/user/application_security/vulnerabilities/img/vulnerability_page_v13_1.png +++ /dev/null diff --git a/doc/user/application_security/vulnerabilities/index.md b/doc/user/application_security/vulnerabilities/index.md index ffec4bf336d..07c201f5642 100644 --- a/doc/user/application_security/vulnerabilities/index.md +++ b/doc/user/application_security/vulnerabilities/index.md @@ -9,10 +9,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13561) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0. -Each security vulnerability in the [Security Dashboard](../security_dashboard/index.md#project-security-dashboard) has its own standalone -page. +Each security vulnerability in a project's [Security Dashboard](../security_dashboard/index.md#project-security-dashboard) has an individual page which includes: -![Vulnerability page](img/vulnerability_page_v13_1.png) +- Details of the vulnerability. +- The status of the vulnerability within the project. +- Available actions for the vulnerability. On the vulnerability page, you can interact with the vulnerability in several different ways: diff --git a/lib/backup/database.rb b/lib/backup/database.rb index d4c1ce260e4..1bc32546abf 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -71,7 +71,7 @@ module Backup end report_success(success) - abort Backup::Error, 'Restore failed' unless success + abort 'Restore failed' unless success end protected diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml index e87f0f28d01..401109d595d 100644 --- a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml @@ -80,7 +80,7 @@ apifuzzer_fuzz: -p 8000:8000 \ -p 514:514 \ --restart=no \ - registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing-src:${FUZZAPI_VERSION}-engine + registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing:${FUZZAPI_VERSION}-engine # # Start target container - | diff --git a/lib/gitlab/i18n/html_todo.yml b/lib/gitlab/i18n/html_todo.yml index bfd96ba8579..91e01f8a0b8 100644 --- a/lib/gitlab/i18n/html_todo.yml +++ b/lib/gitlab/i18n/html_todo.yml @@ -16,67 +16,6 @@ # why this change has been made. # -" or <!merge request id>": - translations: - - " ወይም <!merge request id>" - - " ou <!merge request id>" - - " または <!merge request id>" - - "或 <!合併請求 id>" - - " или <!merge request id>" - - "或<!merge request id>" - - " або <!merge request id>" - - " oder <!merge request id>" - - " o <!merge request id>" - - " 또는 <!merge request id>" - - " o <!merge request id>" - - " veya <!merge request id>" - - " neu <!merge request id>" - - " neu <#issue id>" -" or <#issue id>": - translations: - - "或 <#issue id>" - - " ወይም ‹#issue id›" - - " ou <identificación #issue>" - - " ou <#issue id>" - - " または <#課題 ID>" - - " o <#issue id>" - - "或 <#議題 id>" - - " ou <#issue id>" - - " или <#issue id>" - - "或 <#issue id>" - - " або <#issue id>" - - " oder <#issue id>" - - " o <#issue id>" - - " 또는 <#issue id>" - - " ou <#issue id>" - - " o <#issue id>" - - " veya <#issue id>" - - " neu <#issue id>" -" or <&epic id>": - translations: - - " ወይም <&epic id>" - - " または <&エピックID>" - - " 或 <#史詩 id>" - - " или <&epic id>" - - " 或<#epic id>" - - " або <&epic id>" - - " oder <&epic id>" - - " o <&epic id>" - - " veya <&epic id>" - - " neu <#epic id>" - - " 또는 <&epic id>" -"< 1 hour": - translations: - - "1 時間未満" - - "< 1 小時" - - "< 1 часа" - - "< 1小时" - - "< 1 години" - - "< 1 hora" - - "< 1 saat" - - "< 1 Stunde" - - "< 1시간" - # # Strings below are fixed in the source code but the translations are still present in CrowdIn so the # locale files will fail the linter. They can be deleted after next CrowdIn sync, likely in: @@ -313,3 +252,63 @@ - "Vous êtes sur le point de d’activer la confidentialité. Cela signifie que seuls les membres de l’équipe avec <strong>au moins un accès en tant que rapporteur</strong> seront en mesure de voir et de laisser des commentaires sur le ticket." - "Va a activar la confidencialidad. Esto significa que solo los miembros del equipo con como mínimo,<strong>acceso como Reporter</strong> podrán ver y dejar comentarios sobre la incidencia." - "あなたは非公開設定をオンにしようとしています。これは、最低でも<strong>報告権限</strong>を持ったチームメンバーのみが課題を表示したりコメントを残したりすることができるようになるということです。" +" or <!merge request id>": + translations: + - " ወይም <!merge request id>" + - " ou <!merge request id>" + - " または <!merge request id>" + - "或 <!合併請求 id>" + - " или <!merge request id>" + - "或<!merge request id>" + - " або <!merge request id>" + - " oder <!merge request id>" + - " o <!merge request id>" + - " 또는 <!merge request id>" + - " o <!merge request id>" + - " veya <!merge request id>" + - " neu <!merge request id>" + - " neu <#issue id>" +" or <#issue id>": + translations: + - "或 <#issue id>" + - " ወይም ‹#issue id›" + - " ou <identificación #issue>" + - " ou <#issue id>" + - " または <#課題 ID>" + - " o <#issue id>" + - "或 <#議題 id>" + - " ou <#issue id>" + - " или <#issue id>" + - "或 <#issue id>" + - " або <#issue id>" + - " oder <#issue id>" + - " o <#issue id>" + - " 또는 <#issue id>" + - " ou <#issue id>" + - " o <#issue id>" + - " veya <#issue id>" + - " neu <#issue id>" +" or <&epic id>": + translations: + - " ወይም <&epic id>" + - " または <&エピックID>" + - " 或 <#史詩 id>" + - " или <&epic id>" + - " 或<#epic id>" + - " або <&epic id>" + - " oder <&epic id>" + - " o <&epic id>" + - " veya <&epic id>" + - " neu <#epic id>" + - " 또는 <&epic id>" +"< 1 hour": + translations: + - "1 時間未満" + - "< 1 小時" + - "< 1 часа" + - "< 1小时" + - "< 1 години" + - "< 1 hora" + - "< 1 saat" + - "< 1 Stunde" + - "< 1시간" diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb index f68128c25a9..de252d5c36f 100644 --- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb +++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb @@ -53,8 +53,10 @@ module Gitlab Gitlab::Redis::HLL.count(keys: keys) end + # @param category [String] the category name + # @return [Array<String>] list of event names for given category def events_for_category(category) - known_events.select { |event| event[:category] == category }.map { |event| event[:name] } + known_events.select { |event| event[:category] == category.to_s }.map { |event| event[:name] } end private diff --git a/lib/gitlab/usage_data_counters/known_events.yml b/lib/gitlab/usage_data_counters/known_events.yml index cee942cbbca..4d5910d2a41 100644 --- a/lib/gitlab/usage_data_counters/known_events.yml +++ b/lib/gitlab/usage_data_counters/known_events.yml @@ -74,6 +74,14 @@ category: analytics redis_slot: analytics aggregation: weekly +- name: g_analytics_merge_request + category: analytics + redis_slot: analytics + aggregation: weekly +- name: p_analytics_merge_request + category: analytics + redis_slot: analytics + aggregation: weekly - name: g_edit_by_web_ide category: ide_edit redis_slot: edit diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 85e76c9137b..78d6a63b850 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -56,13 +56,13 @@ msgstr[1] "" msgid " or " msgstr "" -msgid " or <!merge request id>" +msgid " or %{emphasisStart}!merge request id%{emphasisEnd}" msgstr "" -msgid " or <#issue id>" +msgid " or %{emphasisStart}#issue id%{emphasisEnd}" msgstr "" -msgid " or <&epic id>" +msgid " or %{emphasisStart}&epic id%{emphasisEnd}" msgstr "" msgid " or references (e.g. path/to/project!merge_request_id)" @@ -824,6 +824,9 @@ msgstr "" msgid "%{webhooks_link_start}%{webhook_type}%{link_end} enable you to send notifications to web applications in response to events in a group or project. We recommend using an %{integrations_link_start}integration%{link_end} in preference to a webhook." msgstr "" +msgid "< 1 hour" +msgstr "" + msgid "<no name set>" msgstr "" @@ -1093,9 +1096,6 @@ msgstr "" msgid ":%{startLine} to %{endLine}" msgstr "" -msgid "< 1 hour" -msgstr "" - msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need." msgstr "" diff --git a/package.json b/package.json index 55bd4db72f0..f5d00f3e904 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", "@gitlab/svgs": "1.161.0", - "@gitlab/ui": "20.11.0", + "@gitlab/ui": "20.12.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-1", "@sentry/browser": "^5.10.2", diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb index 7828d1fb91e..9adec42842c 100644 --- a/spec/controllers/concerns/send_file_upload_spec.rb +++ b/spec/controllers/concerns/send_file_upload_spec.rb @@ -49,12 +49,13 @@ RSpec.describe SendFileUpload do end shared_examples 'handles image resize requests' do + let(:headers) { double } + let(:image_requester) { build(:user) } + let(:image_owner) { build(:user) } let(:params) do { attachment: 'avatar.png' } end - let(:headers) { double } - before do allow(uploader).to receive(:image_safe_for_scaling?).and_return(true) allow(uploader).to receive(:mounted_as).and_return(:avatar) @@ -64,81 +65,110 @@ RSpec.describe SendFileUpload do # local or remote files allow(controller).to receive(:send_file) allow(controller).to receive(:redirect_to) + + allow(controller).to receive(:current_user).and_return(image_requester) + allow(uploader).to receive(:model).and_return(image_owner) end - context 'when feature is enabled for current user' do - let(:user) { build(:user) } + context 'when boths FFs are enabled' do + before do + stub_feature_flags(dynamic_image_resizing_requester: image_requester) + stub_feature_flags(dynamic_image_resizing_owner: image_owner) + end + it_behaves_like 'handles image resize requests allowed by FFs' + end + + context 'when only FF based on content requester is enabled for current user' do before do - stub_feature_flags(dynamic_image_resizing: user) - allow(controller).to receive(:current_user).and_return(user) + stub_feature_flags(dynamic_image_resizing_requester: image_requester) + stub_feature_flags(dynamic_image_resizing_owner: false) end - context 'with valid width parameter' do - it 'renders OK with workhorse command header' do - expect(controller).not_to receive(:send_file) - expect(controller).to receive(:params).at_least(:once).and_return(width: '64') - expect(controller).to receive(:head).with(:ok) - expect(Gitlab::Workhorse).to receive(:send_scaled_image).with(a_string_matching('^(/.+|https://.+)'), 64, 'image/png').and_return([ - Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux" - ]) - expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux") - - subject - end + it_behaves_like 'bypasses image resize requests not allowed by FFs' + end + + context 'when only FF based on content owner is enabled for requested avatar owner' do + before do + stub_feature_flags(dynamic_image_resizing_requester: false) + stub_feature_flags(dynamic_image_resizing_owner: image_owner) end - context 'with missing width parameter' do - it 'does not write workhorse command header' do - expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) + it_behaves_like 'bypasses image resize requests not allowed by FFs' + end - subject - end + context 'when both FFs are disabled' do + before do + stub_feature_flags(dynamic_image_resizing_requester: false) + stub_feature_flags(dynamic_image_resizing_owner: false) end - context 'with invalid width parameter' do - it 'does not write workhorse command header' do - expect(controller).to receive(:params).at_least(:once).and_return(width: 'not a number') - expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) + it_behaves_like 'bypasses image resize requests not allowed by FFs' + end + end + + shared_examples 'bypasses image resize requests not allowed by FFs' do + it 'does not write workhorse command header' do + expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) - subject - end + subject + end + end + + shared_examples 'handles image resize requests allowed by FFs' do + context 'with valid width parameter' do + it 'renders OK with workhorse command header' do + expect(controller).not_to receive(:send_file) + expect(controller).to receive(:params).at_least(:once).and_return(width: '64') + expect(controller).to receive(:head).with(:ok) + + expect(Gitlab::Workhorse).to receive(:send_scaled_image).with(a_string_matching('^(/.+|https://.+)'), 64, 'image/png').and_return([ + Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux" + ]) + expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux") + + subject end + end - context 'with width that is not allowed' do - it 'does not write workhorse command header' do - expect(controller).to receive(:params).at_least(:once).and_return(width: '63') - expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) + context 'with missing width parameter' do + it 'does not write workhorse command header' do + expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) - subject - end + subject end + end - context 'when image file is not an avatar' do - it 'does not write workhorse command header' do - expect(uploader).to receive(:mounted_as).and_return(nil) # FileUploader is not mounted - expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) + context 'with invalid width parameter' do + it 'does not write workhorse command header' do + expect(controller).to receive(:params).at_least(:once).and_return(width: 'not a number') + expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) - subject - end + subject end + end - context 'when image file type is not considered safe for scaling' do - it 'does not write workhorse command header' do - expect(uploader).to receive(:image_safe_for_scaling?).and_return(false) - expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) + context 'with width that is not allowed' do + it 'does not write workhorse command header' do + expect(controller).to receive(:params).at_least(:once).and_return(width: '63') + expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) - subject - end + subject end end - context 'when feature is disabled' do - before do - stub_feature_flags(dynamic_image_resizing: false) + context 'when image file is not an avatar' do + it 'does not write workhorse command header' do + expect(uploader).to receive(:mounted_as).and_return(nil) # FileUploader is not mounted + expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) + + subject end + end + context 'when image file type is not considered safe for scaling' do it 'does not write workhorse command header' do + expect(uploader).to receive(:image_safe_for_scaling?).and_return(false) expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/) subject diff --git a/spec/frontend/blob/components/blob_edit_content_spec.js b/spec/frontend/blob/components/blob_edit_content_spec.js index a01ac751a61..dbed086a552 100644 --- a/spec/frontend/blob/components/blob_edit_content_spec.js +++ b/spec/frontend/blob/components/blob_edit_content_spec.js @@ -2,12 +2,14 @@ import { shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; import BlobEditContent from '~/blob/components/blob_edit_content.vue'; import * as utils from '~/blob/utils'; -import Editor from '~/editor/editor_lite'; jest.mock('~/editor/editor_lite'); describe('Blob Header Editing', () => { let wrapper; + const onDidChangeModelContent = jest.fn(); + const updateModelLanguage = jest.fn(); + const getValue = jest.fn(); const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; const fileName = 'lorem.txt'; const fileGlobalId = 'snippet_777'; @@ -24,7 +26,12 @@ describe('Blob Header Editing', () => { } beforeEach(() => { - jest.spyOn(utils, 'initEditorLite'); + jest.spyOn(utils, 'initEditorLite').mockImplementation(() => ({ + onDidChangeModelContent, + updateModelLanguage, + getValue, + dispose: jest.fn(), + })); createComponent(); }); @@ -34,8 +41,8 @@ describe('Blob Header Editing', () => { }); const triggerChangeContent = val => { - jest.spyOn(Editor.prototype, 'getValue').mockReturnValue(val); - const [cb] = Editor.prototype.onChangeContent.mock.calls[0]; + getValue.mockReturnValue(val); + const [cb] = onDidChangeModelContent.mock.calls[0]; cb(); @@ -79,12 +86,12 @@ describe('Blob Header Editing', () => { }); return nextTick().then(() => { - expect(Editor.prototype.updateModelLanguage).toHaveBeenCalledWith(newFileName); + expect(updateModelLanguage).toHaveBeenCalledWith(newFileName); }); }); it('registers callback with editor onChangeContent', () => { - expect(Editor.prototype.onChangeContent).toHaveBeenCalledWith(expect.any(Function)); + expect(onDidChangeModelContent).toHaveBeenCalledWith(expect.any(Function)); }); it('emits input event when the blob content is changed', () => { diff --git a/spec/frontend/blob_edit/edit_blob_spec.js b/spec/frontend/blob_edit/edit_blob_spec.js index 9642b55b9b4..8f92e8498b9 100644 --- a/spec/frontend/blob_edit/edit_blob_spec.js +++ b/spec/frontend/blob_edit/edit_blob_spec.js @@ -1,15 +1,18 @@ import EditBlob from '~/blob_edit/edit_blob'; import EditorLite from '~/editor/editor_lite'; import MarkdownExtension from '~/editor/editor_markdown_ext'; +import FileTemplateExtension from '~/editor/editor_file_template_ext'; jest.mock('~/editor/editor_lite'); jest.mock('~/editor/editor_markdown_ext'); describe('Blob Editing', () => { + const mockInstance = 'foo'; beforeEach(() => { setFixtures( - `<div class="js-edit-blob-form"><div id="file_path"></div><div id="iditor"></div><input id="file-content"></div>`, + `<div class="js-edit-blob-form"><div id="file_path"></div><div id="editor"></div><input id="file-content"></div>`, ); + jest.spyOn(EditorLite.prototype, 'createInstance').mockReturnValue(mockInstance); }); const initEditor = (isMarkdown = false) => { @@ -19,13 +22,29 @@ describe('Blob Editing', () => { }); }; - it('does not load MarkdownExtension by default', async () => { + it('loads FileTemplateExtension by default', async () => { await initEditor(); - expect(EditorLite.prototype.use).not.toHaveBeenCalled(); + expect(EditorLite.prototype.use).toHaveBeenCalledWith( + expect.arrayContaining([FileTemplateExtension]), + mockInstance, + ); }); - it('loads MarkdownExtension only for the markdown files', async () => { - await initEditor(true); - expect(EditorLite.prototype.use).toHaveBeenCalledWith(MarkdownExtension); + describe('Markdown', () => { + it('does not load MarkdownExtension by default', async () => { + await initEditor(); + expect(EditorLite.prototype.use).not.toHaveBeenCalledWith( + expect.arrayContaining([MarkdownExtension]), + mockInstance, + ); + }); + + it('loads MarkdownExtension only for the markdown files', async () => { + await initEditor(true); + expect(EditorLite.prototype.use).toHaveBeenCalledWith( + [MarkdownExtension, FileTemplateExtension], + mockInstance, + ); + }); }); }); diff --git a/spec/frontend/editor/editor_markdown_ext_spec.js b/spec/frontend/editor/editor_markdown_ext_spec.js index a049e075dc0..7720708277c 100644 --- a/spec/frontend/editor/editor_markdown_ext_spec.js +++ b/spec/frontend/editor/editor_markdown_ext_spec.js @@ -78,18 +78,18 @@ describe('Markdown Extension for Editor Lite', () => { selectSecondString(); instance.replaceSelectedText(expectedStr); - expect(editor.getValue()).toBe(`${firstLine}\n${expectedStr}\n${thirdLine}`); + expect(instance.getValue()).toBe(`${firstLine}\n${expectedStr}\n${thirdLine}`); }); it('prepends the supplied text if no text is selected', () => { instance.replaceSelectedText(expectedStr); - expect(editor.getValue()).toBe(`${expectedStr}${firstLine}\n${secondLine}\n${thirdLine}`); + expect(instance.getValue()).toBe(`${expectedStr}${firstLine}\n${secondLine}\n${thirdLine}`); }); it('replaces selection with empty string if no text is supplied', () => { selectSecondString(); instance.replaceSelectedText(); - expect(editor.getValue()).toBe(`${firstLine}\n\n${thirdLine}`); + expect(instance.getValue()).toBe(`${firstLine}\n\n${thirdLine}`); }); it('puts cursor at the end of the new string and collapses selection by default', () => { diff --git a/spec/frontend/snippet/snippet_bundle_spec.js b/spec/frontend/snippet/snippet_bundle_spec.js index ad69a91fe89..208d2fea804 100644 --- a/spec/frontend/snippet/snippet_bundle_spec.js +++ b/spec/frontend/snippet/snippet_bundle_spec.js @@ -15,11 +15,13 @@ describe('Snippet editor', () => { const updatedMockContent = 'New Foo Bar'; const mockEditor = { - createInstance: jest.fn(), updateModelLanguage: jest.fn(), getValue: jest.fn().mockReturnValueOnce(updatedMockContent), }; - Editor.mockImplementation(() => mockEditor); + const createInstance = jest.fn().mockImplementation(() => ({ ...mockEditor })); + Editor.mockImplementation(() => ({ + createInstance, + })); function setUpFixture(name, content) { setHTMLFixture(` @@ -56,7 +58,7 @@ describe('Snippet editor', () => { }); it('correctly initializes Editor', () => { - expect(mockEditor.createInstance).toHaveBeenCalledWith({ + expect(createInstance).toHaveBeenCalledWith({ el: editorEl, blobPath: mockName, blobContent: mockContent, diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb index 44320e7ed89..036e75ba724 100644 --- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb @@ -43,6 +43,12 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s Timecop.freeze(reference_time) { example.run } end + describe '.events_for_category' do + it 'gets the event names for given category' do + expect(described_class.events_for_category(:analytics)).to contain_exactly(weekly_event, daily_event) + end + end + describe '.track_event' do it "raise error if metrics don't have same aggregation" do expect { described_class.track_event(entity1, different_aggregation, Date.current) } .to raise_error(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownAggregation) diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 52a8548dea2..f54c4724d74 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -1036,6 +1036,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do 'p_analytics_repo' => 123, 'i_analytics_cohorts' => 123, 'i_analytics_dev_ops_score' => 123, + 'p_analytics_merge_request' => 123, + 'g_analytics_merge_request' => 123, 'analytics_unique_visits_for_any_target' => 543, 'analytics_unique_visits_for_any_target_monthly' => 987 } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f589589af8f..2c5121eb8f8 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -5831,32 +5831,57 @@ RSpec.describe Project do end end - context 'pages deployed' do + describe '#mark_pages_as_deployed' do let(:project) { create(:project) } + let(:artifacts_archive) { create(:ci_job_artifact, project: project) } - { - mark_pages_as_deployed: true, - mark_pages_as_not_deployed: false - }.each do |method_name, flag| - describe method_name do - it "creates new record and sets deployed to #{flag} if none exists yet" do - project.pages_metadatum.destroy! - project.reload + it "works when artifacts_archive is missing" do + project.mark_pages_as_deployed - project.send(method_name) + expect(project.pages_metadatum.reload.deployed).to eq(true) + end - expect(project.pages_metadatum.reload.deployed).to eq(flag) - end + it "creates new record and sets deployed to true if none exists yet" do + project.pages_metadatum.destroy! + project.reload - it "updates the existing record and sets deployed to #{flag}" do - pages_metadatum = project.pages_metadatum - pages_metadatum.update!(deployed: !flag) + project.mark_pages_as_deployed(artifacts_archive: artifacts_archive) - expect { project.send(method_name) }.to change { - pages_metadatum.reload.deployed - }.from(!flag).to(flag) - end - end + expect(project.pages_metadatum.reload.deployed).to eq(true) + end + + it "updates the existing record and sets deployed to true and records artifact archive" do + pages_metadatum = project.pages_metadatum + pages_metadatum.update!(deployed: false) + + expect do + project.mark_pages_as_deployed(artifacts_archive: artifacts_archive) + end.to change { pages_metadatum.reload.deployed }.from(false).to(true) + .and change { pages_metadatum.reload.artifacts_archive }.from(nil).to(artifacts_archive) + end + end + + describe '#mark_pages_as_not_deployed' do + let(:project) { create(:project) } + let(:artifacts_archive) { create(:ci_job_artifact, project: project) } + + it "creates new record and sets deployed to false if none exists yet" do + project.pages_metadatum.destroy! + project.reload + + project.mark_pages_as_not_deployed + + expect(project.pages_metadatum.reload.deployed).to eq(false) + end + + it "updates the existing record and sets deployed to false and clears artifacts_archive" do + pages_metadatum = project.pages_metadatum + pages_metadatum.update!(deployed: true, artifacts_archive: artifacts_archive) + + expect do + project.mark_pages_as_not_deployed + end.to change { pages_metadatum.reload.deployed }.from(true).to(false) + .and change { pages_metadatum.reload.artifacts_archive }.from(artifacts_archive).to(nil) end end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 374ce4f4ce2..bfb3cbb0131 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -29,8 +29,9 @@ RSpec.describe Projects::UpdatePagesService do context 'for new artifacts' do context "for a valid job" do + let!(:artifacts_archive) { create(:ci_job_artifact, file: file, job: build) } + before do - create(:ci_job_artifact, file: file, job: build) create(:ci_job_artifact, file_type: :metadata, file_format: :gzip, file: metadata, job: build) build.reload @@ -49,6 +50,7 @@ RSpec.describe Projects::UpdatePagesService do expect(project.pages_deployed?).to be_falsey expect(execute).to eq(:success) expect(project.pages_metadatum).to be_deployed + expect(project.pages_metadatum.artifacts_archive).to eq(artifacts_archive) expect(project.pages_deployed?).to be_truthy # Check that all expected files are extracted diff --git a/yarn.lock b/yarn.lock index 69a37601beb..09b1dd7f50e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -848,10 +848,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.161.0.tgz#661e8d19862dfba0e4c558e2eb6d64b402c1453e" integrity sha512-qsbboEICn08ZoEoAX/TuYygsFaXlzsCY+CfmdOzqvJbOdfHhVXmrJBxd2hP2qqjTZm2PkbRRmn+03+ce1jvatQ== -"@gitlab/ui@20.11.0": - version "20.11.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-20.11.0.tgz#6570ca518195b05e14861df5ea6a319d4dbe2298" - integrity sha512-vFR/xFx/D7ndRDDoqHQ8GH/YU4lCE3P3eXR3mlpNIMxgak3MuS9w9tfu78z4fRT6gTWuBZEkeoZnjZBgQewqKQ== +"@gitlab/ui@20.12.0": + version "20.12.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-20.12.0.tgz#694cfe9793b8dd9f24e16373321bf25bd9b4f9c0" + integrity sha512-5QgWFjOoWsTbMq7LqE7w1hmgSE4N8Z08vDmAg+HNsn50mtPQeIRKofYmyvfXCpRoL7e0a/HHfLU0HqctZLILGQ== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |