diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-30 18:08:09 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-30 18:08:09 +0300 |
commit | 538fff823de57d1ba5317961aa43091de9dc007f (patch) | |
tree | c741665b338cc0d51ce5f73f5671e5eee8e69349 | |
parent | 3692e9f8a23386c627942ca2a9edd8c00af7e904 (diff) |
Add latest changes from gitlab-org/gitlab@master
41 files changed, 526 insertions, 232 deletions
diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md index fd4ea7af569..5bd8f7166bb 100644 --- a/CHANGELOG-EE.md +++ b/CHANGELOG-EE.md @@ -1,5 +1,13 @@ Please view this file on the master branch, on stable branches it's out of date. +## 12.3.2 + +### Security (2 changes) + +- Hide approvers if a rule has any hidden groups. +- Prevent IDOR when adding groups to protected environments. + + ## 12.3.1 - No changes. @@ -187,6 +195,15 @@ Please view this file on the master branch, on stable branches it's out of date. - Fixes style-lint errors and warnings for EE builds.scss file. +## 12.2.6 + +### Security (3 changes) + +- Hide approvers if a rule has any hidden groups. +- Fix Gitaly SearchBlobs flag RPC injection [Gitaly v1.59.3]. +- Prevent IDOR when adding groups to protected environments. + + ## 12.2.5 ### Security (1 change) @@ -439,6 +456,16 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix alignment of activity dropdown in epic tabs; add counter to discussion tab. +## 12.1.12 + +### Security (4 changes) + +- Hide approvers if a rule has any hidden groups. +- Fix Gitaly SearchBlobs flag RPC injection [Gitaly v1.53.4]. +- Prevent IDOR when adding groups to protected environments. +- Upgrade mermaid to prevent XSS. + + ## 12.1.10 - No changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a4c2fab541..c2ffec09cb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,18 @@ entry. ## 12.3.2 -### Security (10 changes) +### Security (12 changes) - Fix Gitaly SearchBlobs flag RPC injection. - Add a policy check for system notes that may not be visible due to cross references to private items. - Display only participants that user has permission to see on milestone page. - Do not disclose project milestones on group milestones page when project milestones access is disabled in project settings. +- Check permissions before showing head pipeline blocking merge requests. - Fix new project path being disclosed through unsubscribe link of issue/merge requests. - Prevent bypassing email verification using Salesforce. - Do not show resource label events referencing not accessible labels. - Cancel all running CI jobs triggered by the user who is just blocked. +- Fix Gitaly SearchBlobs flag RPC injection. - Only render fixed number of mermaid blocks. - Prevent GitLab accounts takeover if SAML is configured. @@ -299,11 +301,12 @@ entry. ## 12.2.6 -### Security (10 changes) +### Security (11 changes) - Add a policy check for system notes that may not be visible due to cross references to private items. - Display only participants that user has permission to see on milestone page. - Do not disclose project milestones on group milestones page when project milestones access is disabled in project settings. +- Check permissions before showing head pipeline blocking merge requests. - Fix new project path being disclosed through unsubscribe link of issue/merge requests. - Prevent bypassing email verification using Salesforce. - Do not show resource label events referencing not accessible labels. @@ -633,11 +636,12 @@ entry. ## 12.1.12 -### Security (11 changes) +### Security (12 changes) - Add a policy check for system notes that may not be visible due to cross references to private items. - Display only participants that user has permission to see on milestone page. - Do not disclose project milestones on group milestones page when project milestones access is disabled in project settings. +- Check permissions before showing head pipeline blocking merge requests. - Fix new project path being disclosed through unsubscribe link of issue/merge requests. - Prevent bypassing email verification using Salesforce. - Do not show resource label events referencing not accessible labels. diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 7f554c99669..2acd92069ca 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -104,6 +104,13 @@ export default { helpLink() { return boardsStore.scopedLabels.helpLink; }, + validIssueWeight() { + if (_.isNumber(this.issue.weight)) { + return this.issue.weight >= 0; + } + + return false; + }, }, methods: { isIndexLessThanlimit(index) { @@ -212,7 +219,7 @@ export default { <issue-due-date v-if="issue.dueDate" :date="issue.dueDate" /> <issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" /> <issue-card-weight - v-if="issue.weight" + v-if="validIssueWeight" :weight="issue.weight" @click="filterByWeight(issue.weight)" /> diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 76705b4410c..3007b5f1518 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -72,7 +72,7 @@ class Projects::CommitsController < Projects::ApplicationController @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) end - @commits = @commits.with_pipeline_status + @commits = @commits.with_latest_pipeline(@ref) @commits = set_commits_for_rendering(@commits) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 2f73fccabcf..7ddff9c1893 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # Get commits from repository # or from cache if already merged @commits = - set_commits_for_rendering(@merge_request.commits.with_pipeline_status) + set_commits_for_rendering(@merge_request.commits.with_latest_pipeline) render json: { html: view_to_html_string('projects/merge_requests/_commits') } end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 144df676304..1f34a483071 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -64,7 +64,7 @@ module CiStatusHelper def ci_icon_for_status(status, size: 16) if detailed_status?(status) - return sprite_icon(status.icon) + return sprite_icon(status.icon, size: size) end icon_name = @@ -96,23 +96,29 @@ module CiStatusHelper sprite_icon(icon_name, size: size) end + def ci_icon_class_for_status(status) + group = detailed_status?(status) ? status.group : status.dasherize + + "ci-status-icon-#{group}" + end + def pipeline_status_cache_key(pipeline_status) "pipeline-status/#{pipeline_status.sha}-#{pipeline_status.status}" end - def render_commit_status(commit, ref: nil, tooltip_placement: 'left') + def render_commit_status(commit, status, ref: nil, tooltip_placement: 'left') project = commit.project path = pipelines_project_commit_path(project, commit, ref: ref) render_status_with_link( - commit.status(ref), + status, path, tooltip_placement: tooltip_placement, icon_size: 24) end def render_status_with_link(status, path = nil, type: _('pipeline'), tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) - klass = "ci-status-link ci-status-icon-#{status.dasherize} d-inline-flex #{cssclass}" + klass = "ci-status-link #{ci_icon_class_for_status(status)} d-inline-flex #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" data = { toggle: 'tooltip', placement: tooltip_placement, container: container } @@ -127,6 +133,7 @@ module CiStatusHelper def detailed_status?(status) status.respond_to?(:text) && + status.respond_to?(:group) && status.respond_to?(:label) && status.respond_to?(:icon) end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index b22a87e7cef..9a96429d3a9 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -281,16 +281,16 @@ module Ci end end - # Returns a Hash containing the latest pipeline status for every given + # Returns a Hash containing the latest pipeline for every given # commit. # - # The keys of this Hash are the commit SHAs, the values the statuses. + # The keys of this Hash are the commit SHAs, the values the pipelines. # - # commits - The list of commit SHAs to get the status for. + # commits - The list of commit SHAs to get the pipelines for. # ref - The ref to scope the data to (e.g. "master"). If the ref is not - # given we simply get the latest status for the commits, regardless - # of what refs their pipelines belong to. - def self.latest_status_per_commit(commits, ref = nil) + # given we simply get the latest pipelines for the commits, regardless + # of what refs the pipelines belong to. + def self.latest_pipeline_per_commit(commits, ref = nil) p1 = arel_table p2 = arel_table.alias @@ -304,15 +304,14 @@ module Ci cond = cond.and(p1[:ref].eq(p2[:ref])) if ref join = p1.join(p2, Arel::Nodes::OuterJoin).on(cond) - relation = select(:sha, :status) - .where(sha: commits) + relation = where(sha: commits) .where(p2[:id].eq(nil)) .joins(join.join_sources) relation = relation.where(ref: ref) if ref - relation.each_with_object({}) do |row, hash| - hash[row[:sha]] = row[:status] + relation.each_with_object({}) do |pipeline, hash| + hash[pipeline.sha] = pipeline end end diff --git a/app/models/commit.rb b/app/models/commit.rb index a442f607fbf..60a60f191d6 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -119,10 +119,22 @@ class Commit @raw = raw_commit @project = project - @statuses = {} @gpg_commit = Gitlab::Gpg::Commit.new(self) if project end + delegate \ + :pipelines, + :last_pipeline, + :latest_pipeline, + :latest_pipeline_for_project, + :set_latest_pipeline_for_ref, + :status, + to: :with_pipeline + + def with_pipeline + @with_pipeline ||= CommitWithPipeline.new(self) + end + def id raw.id end @@ -301,30 +313,6 @@ class Commit ) end - def pipelines - project.ci_pipelines.where(sha: sha) - end - - def last_pipeline - strong_memoize(:last_pipeline) do - pipelines.last - end - end - - def status(ref = nil) - return @statuses[ref] if @statuses.key?(ref) - - @statuses[ref] = status_for_project(ref, project) - end - - def status_for_project(ref, pipeline_project) - pipeline_project.ci_pipelines.latest_status_per_commit(id, ref)[id] - end - - def set_status_for_ref(ref, status) - @statuses[ref] = status - end - def signature return @signature if defined?(@signature) diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb index e8df46e1cc3..6b303c52283 100644 --- a/app/models/commit_collection.rb +++ b/app/models/commit_collection.rb @@ -34,6 +34,20 @@ class CommitCollection end end + # Returns the collection with the latest pipeline for every commit pre-set. + # + # Setting the pipeline for each commit ahead of time removes the need for running + # a query for every commit we're displaying. + def with_latest_pipeline(ref = nil) + pipelines = project.ci_pipelines.latest_pipeline_per_commit(map(&:id), ref) + + each do |commit| + commit.set_latest_pipeline_for_ref(ref, pipelines[commit.id]) + end + + self + end + def unenriched commits.reject(&:gitaly_commit?) end @@ -65,20 +79,6 @@ class CommitCollection self end - # Sets the pipeline status for every commit. - # - # Setting this status ahead of time removes the need for running a query for - # every commit we're displaying. - def with_pipeline_status - statuses = project.ci_pipelines.latest_status_per_commit(map(&:id), ref) - - each do |commit| - commit.set_status_for_ref(ref, statuses[commit.id]) - end - - self - end - def respond_to_missing?(message, inc_private = false) commits.respond_to?(message, inc_private) end diff --git a/app/models/commit_with_pipeline.rb b/app/models/commit_with_pipeline.rb new file mode 100644 index 00000000000..f382ae8f55a --- /dev/null +++ b/app/models/commit_with_pipeline.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class CommitWithPipeline < SimpleDelegator + include Presentable + + def initialize(commit) + @latest_pipelines = {} + super(commit) + end + + def pipelines + project.ci_pipelines.where(sha: sha) + end + + def last_pipeline + strong_memoize(:last_pipeline) do + pipelines.last + end + end + + def latest_pipeline(ref = nil) + @latest_pipelines.fetch(ref) do |ref| + @latest_pipelines[ref] = latest_pipeline_for_project(ref, project) + end + end + + def latest_pipeline_for_project(ref, pipeline_project) + pipeline_project.ci_pipelines.latest_pipeline_per_commit(id, ref)[id] + end + + def set_latest_pipeline_for_ref(ref, pipeline) + @latest_pipelines[ref] = pipeline + end + + def status(ref = nil) + latest_pipeline(ref)&.status + end +end diff --git a/app/presenters/commit_presenter.rb b/app/presenters/commit_presenter.rb index fc9853733c1..f5b1e45c0e9 100644 --- a/app/presenters/commit_presenter.rb +++ b/app/presenters/commit_presenter.rb @@ -6,11 +6,15 @@ class CommitPresenter < Gitlab::View::Presenter::Delegated presents :commit def status_for(ref) - can?(current_user, :read_commit_status, commit.project) && commit.status(ref) + return unless can?(current_user, :read_commit_status, commit.project) + + commit.latest_pipeline(ref)&.detailed_status(current_user) end def any_pipelines? - can?(current_user, :read_pipeline, commit.project) && commit.pipelines.any? + return false unless can?(current_user, :read_pipeline, commit.project) + + commit.pipelines.any? end def web_url diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb index a94e32478ce..ae3f1c6bbf5 100644 --- a/app/serializers/commit_entity.rb +++ b/app/serializers/commit_entity.rb @@ -35,8 +35,8 @@ class CommitEntity < API::Entities::Commit pipeline_project = options[:pipeline_project] || commit.project next unless pipeline_ref && pipeline_project - status = commit.status_for_project(pipeline_ref, pipeline_project) - next unless status + pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project) + next unless pipeline&.status pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref) end diff --git a/app/services/search/snippet_service.rb b/app/services/search/snippet_service.rb index 7c6c6878400..3c8847d3c18 100644 --- a/app/services/search/snippet_service.rb +++ b/app/services/search/snippet_service.rb @@ -9,9 +9,7 @@ module Search end def execute - snippets = SnippetsFinder.new(current_user).execute - - Gitlab::SnippetSearchResults.new(snippets, params[:search]) + Gitlab::SnippetSearchResults.new(current_user, params[:search]) end def scope diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 2c78e74be2f..0f913d11be1 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -6,7 +6,8 @@ - merge_request = local_assigns.fetch(:merge_request, nil) - project = local_assigns.fetch(:project) { merge_request&.project } - ref = local_assigns.fetch(:ref) { merge_request&.source_branch } -- commit_status = commit.present(current_user: current_user).status_for(ref) +- commit = commit.present(current_user: current_user) +- commit_status = commit.status_for(ref) - link = commit_path(project, commit, merge_request: merge_request) @@ -48,7 +49,7 @@ = render partial: 'projects/commit/ajax_signature', locals: { commit: commit } - if commit_status - = render_commit_status(commit, ref: ref) + = render_commit_status(commit, commit_status, ref: ref) .js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } } diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index bcce7cb52fb..5432607f82f 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -63,7 +63,7 @@ - if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project) - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref) %span.icon-wrapper.pipeline-status - = render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path + = render 'ci/status/icon', status: project.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path - if project.archived %span.d-flex.icon-wrapper.badge.badge-warning archived diff --git a/changelogs/unreleased/13321-show-issue-weight-when-zero.yml b/changelogs/unreleased/13321-show-issue-weight-when-zero.yml new file mode 100644 index 00000000000..422245548d6 --- /dev/null +++ b/changelogs/unreleased/13321-show-issue-weight-when-zero.yml @@ -0,0 +1,5 @@ +--- +title: Show issue weight when weight is 0 +merge_request: 17329 +author: briankabiro +type: fixed diff --git a/changelogs/unreleased/backfill-releases-updated-at.yml b/changelogs/unreleased/backfill-releases-updated-at.yml new file mode 100644 index 00000000000..dfaaf8cd41e --- /dev/null +++ b/changelogs/unreleased/backfill-releases-updated-at.yml @@ -0,0 +1,5 @@ +--- +title: Backfill releases table updated_at column and add not null constraints to created_at and updated_at +merge_request: 17400 +author: +type: fixed diff --git a/changelogs/unreleased/eb-fix-ci-status-indicator-for-warnings.yml b/changelogs/unreleased/eb-fix-ci-status-indicator-for-warnings.yml new file mode 100644 index 00000000000..efc3238a267 --- /dev/null +++ b/changelogs/unreleased/eb-fix-ci-status-indicator-for-warnings.yml @@ -0,0 +1,5 @@ +--- +title: Show correct CI indicator when build succeeded with warnings. +merge_request: 17034 +author: +type: fixed diff --git a/changelogs/unreleased/fj-32643-limit-snippets-search-count.yml b/changelogs/unreleased/fj-32643-limit-snippets-search-count.yml new file mode 100644 index 00000000000..8b5382f0a70 --- /dev/null +++ b/changelogs/unreleased/fj-32643-limit-snippets-search-count.yml @@ -0,0 +1,5 @@ +--- +title: Limit snippets search count +merge_request: 17585 +author: +type: performance diff --git a/changelogs/unreleased/jramsay-clarify-git-lfs-push-rule-behavior.yml b/changelogs/unreleased/jramsay-clarify-git-lfs-push-rule-behavior.yml new file mode 100644 index 00000000000..ca85114e5ac --- /dev/null +++ b/changelogs/unreleased/jramsay-clarify-git-lfs-push-rule-behavior.yml @@ -0,0 +1,5 @@ +--- +title: Document Git LFS and max file size interaction +merge_request: 17609 +author: +type: other diff --git a/changelogs/unreleased/security-13338-fix-head-pipeline-leak.yml b/changelogs/unreleased/security-13338-fix-head-pipeline-leak.yml new file mode 100644 index 00000000000..1091a302aba --- /dev/null +++ b/changelogs/unreleased/security-13338-fix-head-pipeline-leak.yml @@ -0,0 +1,5 @@ +--- +title: Check permissions before showing head pipeline blocking merge requests +merge_request: +author: +type: security diff --git a/changelogs/unreleased/12-3-stable.yml b/changelogs/unreleased/security-gitaly-1-65-1.yml index e532a8aba9f..e532a8aba9f 100644 --- a/changelogs/unreleased/12-3-stable.yml +++ b/changelogs/unreleased/security-gitaly-1-65-1.yml diff --git a/db/migrate/20190920194925_backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps.rb b/db/migrate/20190920194925_backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps.rb new file mode 100644 index 00000000000..f8f1c6c231b --- /dev/null +++ b/db/migrate/20190920194925_backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class BackfillReleasesTableUpdatedAtAndAddNotNullConstraintsToTimestamps < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + change_column_null(:releases, :created_at, false, Time.zone.now) + + update_column_in_batches(:releases, :updated_at, Arel.sql('created_at')) do |table, query| + query.where(table[:updated_at].eq(nil)) + end + + change_column_null(:releases, :updated_at, false, Time.zone.now) + end + + def down + change_column_null(:releases, :updated_at, true) + change_column_null(:releases, :created_at, true) + end +end diff --git a/db/schema.rb b/db/schema.rb index be774169517..ef52143da73 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -3137,8 +3137,8 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do t.string "tag" t.text "description" t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.text "description_html" t.integer "cached_markdown_version" t.integer "author_id" diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md index 08484fb4187..b652319ef57 100644 --- a/doc/push_rules/push_rules.md +++ b/doc/push_rules/push_rules.md @@ -74,7 +74,7 @@ The following options are available. | Restrict by branch name | **Starter** 9.3 | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. | | Restrict by commit author's email | **Starter** 7.10 | Only commit author's email that match this regular expression are allowed to be pushed. Leave empty to allow any email. | | Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression are not allowed to be pushed. Leave empty to allow any filenames. | -| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. | +| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. | TIP: **Tip:** GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [GoLang regex tester](https://regex-golang.appspot.com). diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb index ac3b219e0c7..6763914287a 100644 --- a/lib/gitlab/snippet_search_results.rb +++ b/lib/gitlab/snippet_search_results.rb @@ -4,19 +4,19 @@ module Gitlab class SnippetSearchResults < SearchResults include SnippetsHelper - attr_reader :limit_snippets + attr_reader :current_user - def initialize(limit_snippets, query) - @limit_snippets = limit_snippets + def initialize(current_user, query) + @current_user = current_user @query = query end def objects(scope, page = nil) case scope when 'snippet_titles' - snippet_titles.page(page).per(per_page) + paginated_objects(snippet_titles, page) when 'snippet_blobs' - snippet_blobs.page(page).per(per_page) + paginated_objects(snippet_blobs, page) else super(scope, nil, false) end @@ -25,38 +25,47 @@ module Gitlab def formatted_count(scope) case scope when 'snippet_titles' - snippet_titles_count.to_s + formatted_limited_count(limited_snippet_titles_count) when 'snippet_blobs' - snippet_blobs_count.to_s + formatted_limited_count(limited_snippet_blobs_count) else super end end - def snippet_titles_count - @snippet_titles_count ||= snippet_titles.count + def limited_snippet_titles_count + @limited_snippet_titles_count ||= limited_count(snippet_titles) end - def snippet_blobs_count - @snippet_blobs_count ||= snippet_blobs.count + def limited_snippet_blobs_count + @limited_snippet_blobs_count ||= limited_count(snippet_blobs) end private # rubocop: disable CodeReuse/ActiveRecord - def snippet_titles - limit_snippets.search(query).order('updated_at DESC').includes(:author) + def snippets + SnippetsFinder.new(current_user) + .execute + .includes(:author) + .reorder(updated_at: :desc) end # rubocop: enable CodeReuse/ActiveRecord - # rubocop: disable CodeReuse/ActiveRecord + def snippet_titles + snippets.search(query) + end + def snippet_blobs - limit_snippets.search_code(query).order('updated_at DESC').includes(:author) + snippets.search_code(query) end - # rubocop: enable CodeReuse/ActiveRecord def default_scope 'snippet_blobs' end + + def paginated_objects(relation, page) + relation.page(page).per(per_page) + end end end diff --git a/package.json b/package.json index f6af9b9b609..a1c4dcfefc7 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@babel/plugin-syntax-import-meta": "^7.2.0", "@babel/preset-env": "^7.6.2", "@gitlab/svgs": "^1.75.0", - "@gitlab/ui": "5.26.1", + "@gitlab/ui": "5.26.2", "@gitlab/visual-review-tools": "1.0.3", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index a12fc8b18ed..6f96da60a31 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -3,7 +3,6 @@ require 'spec_helper' describe 'Import/Export - project import integration test', :js do - include Select2Helper include GitHelpers let(:user) { create(:user) } @@ -31,7 +30,6 @@ describe 'Import/Export - project import integration test', :js do it 'user imports an exported project successfully' do visit new_project_path - select2(namespace.id, from: '#project_namespace_id') fill_in :project_name, with: project_name, visible: true click_import_project_tab click_link 'GitLab export' @@ -78,7 +76,6 @@ describe 'Import/Export - project import integration test', :js do visit new_project_path - select2(user.namespace.id, from: '#project_namespace_id') fill_in :project_name, with: project.name, visible: true click_import_project_tab click_link 'GitLab export' diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index 8a20911cc66..9b5e8afa4ef 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -42,6 +42,7 @@ describe('Issue card component', () => { assignees: [], reference_path: '#1', real_path: '/test/1', + weight: 1, }); component = new Vue({ @@ -287,8 +288,17 @@ describe('Issue card component', () => { }); describe('weights', () => { - it('not shows weight component', () => { - expect(component.$el.querySelector('.board-card-weight')).toBeNull(); + it('shows weight component is greater than 0', () => { + expect(component.$el.querySelector('.board-card-weight')).not.toBeNull(); + }); + + it('shows weight component when weight is 0', done => { + component.issue.weight = 0; + + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-card-weight')).not.toBeNull(); + done(); + }); }); }); }); diff --git a/spec/lib/gitlab/snippet_search_results_spec.rb b/spec/lib/gitlab/snippet_search_results_spec.rb index d3353b76c15..47f26fdebe2 100644 --- a/spec/lib/gitlab/snippet_search_results_spec.rb +++ b/spec/lib/gitlab/snippet_search_results_spec.rb @@ -6,18 +6,17 @@ describe Gitlab::SnippetSearchResults do include SearchHelpers let!(:snippet) { create(:snippet, content: 'foo', file_name: 'foo') } - - let(:results) { described_class.new(Snippet.all, 'foo') } + let(:results) { described_class.new(snippet.author, 'foo') } describe '#snippet_titles_count' do it 'returns the amount of matched snippet titles' do - expect(results.snippet_titles_count).to eq(1) + expect(results.limited_snippet_titles_count).to eq(1) end end describe '#snippet_blobs_count' do it 'returns the amount of matched snippet blobs' do - expect(results.snippet_blobs_count).to eq(1) + expect(results.limited_snippet_blobs_count).to eq(1) end end @@ -25,10 +24,10 @@ describe Gitlab::SnippetSearchResults do using RSpec::Parameterized::TableSyntax where(:scope, :count_method, :expected) do - 'snippet_titles' | :snippet_titles_count | '1234' - 'snippet_blobs' | :snippet_blobs_count | '1234' - 'projects' | :limited_projects_count | max_limited_count - 'unknown' | nil | nil + 'snippet_titles' | :limited_snippet_titles_count | max_limited_count + 'snippet_blobs' | :limited_snippet_blobs_count | max_limited_count + 'projects' | :limited_projects_count | max_limited_count + 'unknown' | nil | nil end with_them do diff --git a/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb b/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb new file mode 100644 index 00000000000..3ca7af8ea37 --- /dev/null +++ b/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20190920194925_backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps.rb') + +describe BackfillReleasesTableUpdatedAtAndAddNotNullConstraintsToTimestamps, :migration do + let(:releases) { table(:releases) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + + subject(:migration) { described_class.new } + + it 'fills null updated_at rows with the value of created_at' do + created_at_a = Time.zone.parse('2014-03-11T04:30:00Z') + created_at_b = Time.zone.parse('2019-09-10T12:00:00Z') + namespace = namespaces.create(name: 'foo', path: 'foo') + project = projects.create!(namespace_id: namespace.id) + release_a = releases.create!(project_id: project.id, + released_at: Time.zone.parse('2014-12-10T06:00:00Z'), + created_at: created_at_a) + release_b = releases.create!(project_id: project.id, + released_at: Time.zone.parse('2019-09-11T06:00:00Z'), + created_at: created_at_b) + release_a.update!(updated_at: nil) + release_b.update!(updated_at: nil) + + disable_migrations_output { migrate! } + + release_a.reload + release_b.reload + expect(release_a.updated_at).to eq(created_at_a) + expect(release_b.updated_at).to eq(created_at_b) + end + + it 'does not change updated_at columns with a value' do + created_at_a = Time.zone.parse('2014-03-11T04:30:00Z') + updated_at_a = Time.zone.parse('2015-01-16T10:00:00Z') + created_at_b = Time.zone.parse('2019-09-10T12:00:00Z') + namespace = namespaces.create(name: 'foo', path: 'foo') + project = projects.create!(namespace_id: namespace.id) + release_a = releases.create!(project_id: project.id, + released_at: Time.zone.parse('2014-12-10T06:00:00Z'), + created_at: created_at_a, + updated_at: updated_at_a) + release_b = releases.create!(project_id: project.id, + released_at: Time.zone.parse('2019-09-11T06:00:00Z'), + created_at: created_at_b) + release_b.update!(updated_at: nil) + + disable_migrations_output { migrate! } + + release_a.reload + release_b.reload + expect(release_a.updated_at).to eq(updated_at_a) + expect(release_b.updated_at).to eq(created_at_b) + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index a00d93ac4ba..3c625784132 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1966,40 +1966,57 @@ describe Ci::Pipeline, :mailer do end end - describe '.latest_status_per_commit' do + describe '.latest_pipeline_per_commit' do let(:project) { create(:project) } - before do - pairs = [ - %w[success ref1 123], - %w[manual master 123], - %w[failed ref 456] - ] - - pairs.each do |(status, ref, sha)| - create( - :ci_empty_pipeline, - status: status, - ref: ref, - sha: sha, - project: project - ) - end + let!(:commit_123_ref_master) do + create( + :ci_empty_pipeline, + status: 'success', + ref: 'master', + sha: '123', + project: project + ) + end + let!(:commit_123_ref_develop) do + create( + :ci_empty_pipeline, + status: 'success', + ref: 'develop', + sha: '123', + project: project + ) + end + let!(:commit_456_ref_test) do + create( + :ci_empty_pipeline, + status: 'success', + ref: 'test', + sha: '456', + project: project + ) end context 'without a ref' do - it 'returns a Hash containing the latest status per commit for all refs' do - expect(described_class.latest_status_per_commit(%w[123 456])) - .to eq({ '123' => 'manual', '456' => 'failed' }) + it 'returns a Hash containing the latest pipeline per commit for all refs' do + result = described_class.latest_pipeline_per_commit(%w[123 456]) + + expect(result).to match( + '123' => commit_123_ref_develop, + '456' => commit_456_ref_test + ) end - it 'only includes the status of the given commit SHAs' do - expect(described_class.latest_status_per_commit(%w[123])) - .to eq({ '123' => 'manual' }) + it 'only includes the latest pipeline of the given commit SHAs' do + result = described_class.latest_pipeline_per_commit(%w[123]) + + expect(result).to match( + '123' => commit_123_ref_develop + ) end context 'when there are two pipelines for a ref and SHA' do - it 'returns the status of the latest pipeline' do + let!(:commit_123_ref_master_latest) do create( :ci_empty_pipeline, status: 'failed', @@ -2007,17 +2024,25 @@ describe Ci::Pipeline, :mailer do sha: '123', project: project ) + end + + it 'returns the latest pipeline' do + result = described_class.latest_pipeline_per_commit(%w[123]) - expect(described_class.latest_status_per_commit(%w[123])) - .to eq({ '123' => 'failed' }) + expect(result).to match( + '123' => commit_123_ref_master_latest + ) end end end context 'with a ref' do it 'only includes the pipelines for the given ref' do - expect(described_class.latest_status_per_commit(%w[123 456], 'master')) - .to eq({ '123' => 'manual' }) + result = described_class.latest_pipeline_per_commit(%w[123 456], 'master') + + expect(result).to match( + '123' => commit_123_ref_master + ) end end end diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb index 0bdf83fa90f..a8957bbfdd0 100644 --- a/spec/models/commit_collection_spec.rb +++ b/spec/models/commit_collection_spec.rb @@ -51,6 +51,30 @@ describe CommitCollection do end end + describe '#with_latest_pipeline' do + let!(:pipeline) do + create( + :ci_empty_pipeline, + ref: 'master', + sha: commit.id, + status: 'success', + project: project + ) + end + let(:collection) { described_class.new(project, [commit]) } + + it 'sets the latest pipeline for every commit so no additional queries are necessary' do + commits = collection.with_latest_pipeline('master') + + recorder = ActiveRecord::QueryRecorder.new do + expect(commits.map { |c| c.latest_pipeline('master') }) + .to eq([pipeline]) + end + + expect(recorder.count).to be_zero + end + end + describe 'enrichment methods' do let(:gitaly_commit) { commit } let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) } @@ -128,27 +152,6 @@ describe CommitCollection do end end - describe '#with_pipeline_status' do - it 'sets the pipeline status for every commit so no additional queries are necessary' do - create( - :ci_empty_pipeline, - ref: 'master', - sha: commit.id, - status: 'success', - project: project - ) - - collection = described_class.new(project, [commit]) - collection.with_pipeline_status - - recorder = ActiveRecord::QueryRecorder.new do - expect(commit.status).to eq('success') - end - - expect(recorder.count).to be_zero - end - end - describe '#respond_to_missing?' do it 'returns true when the underlying Array responds to the message' do collection = described_class.new(project, []) diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 5ef824b9950..6e511c9e4ec 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -462,78 +462,6 @@ eos end end - describe '#last_pipeline' do - let!(:first_pipeline) do - create(:ci_empty_pipeline, - project: project, - sha: commit.sha, - status: 'success') - end - let!(:second_pipeline) do - create(:ci_empty_pipeline, - project: project, - sha: commit.sha, - status: 'success') - end - - it 'returns last pipeline' do - expect(commit.last_pipeline).to eq second_pipeline - end - end - - describe '#status' do - context 'without ref argument' do - before do - %w[success failed created pending].each do |status| - create(:ci_empty_pipeline, - project: project, - sha: commit.sha, - status: status) - end - end - - it 'gives compound status from latest pipelines' do - expect(commit.status).to eq(Ci::Pipeline.latest_status) - expect(commit.status).to eq('pending') - end - end - - context 'when a particular ref is specified' do - let!(:pipeline_from_master) do - create(:ci_empty_pipeline, - project: project, - sha: commit.sha, - ref: 'master', - status: 'failed') - end - - let!(:pipeline_from_fix) do - create(:ci_empty_pipeline, - project: project, - sha: commit.sha, - ref: 'fix', - status: 'success') - end - - it 'gives pipelines from a particular branch' do - expect(commit.status('master')).to eq(pipeline_from_master.status) - expect(commit.status('fix')).to eq(pipeline_from_fix.status) - end - - it 'gives compound status from latest pipelines if ref is nil' do - expect(commit.status(nil)).to eq(pipeline_from_fix.status) - end - end - end - - describe '#set_status_for_ref' do - it 'sets the status for a given reference' do - commit.set_status_for_ref('master', 'failed') - - expect(commit.status('master')).to eq('failed') - end - end - describe '#participants' do let(:user1) { build(:user) } let(:user2) { build(:user) } diff --git a/spec/models/commit_with_pipeline_spec.rb b/spec/models/commit_with_pipeline_spec.rb new file mode 100644 index 00000000000..e0bb29fec7b --- /dev/null +++ b/spec/models/commit_with_pipeline_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe CommitWithPipeline do + let(:project) { create(:project, :public, :repository) } + let(:commit) { described_class.new(project.commit) } + + describe '#last_pipeline' do + let!(:first_pipeline) do + create(:ci_empty_pipeline, + project: project, + sha: commit.sha, + status: 'success') + end + let!(:second_pipeline) do + create(:ci_empty_pipeline, + project: project, + sha: commit.sha, + status: 'success') + end + + it 'returns last pipeline' do + expect(commit.last_pipeline).to eq second_pipeline + end + end + + describe '#latest_pipeline' do + let(:pipeline) { double } + + shared_examples_for 'fetching latest pipeline' do |ref| + it 'returns the latest pipeline for the project' do + expect(commit) + .to receive(:latest_pipeline_for_project) + .with(ref, project) + .and_return(pipeline) + + expect(result).to eq(pipeline) + end + + it "returns the memoized pipeline for the key of #{ref}" do + commit.set_latest_pipeline_for_ref(ref, pipeline) + + expect(commit) + .not_to receive(:latest_pipeline_for_project) + + expect(result).to eq(pipeline) + end + end + + context 'without ref argument' do + let(:result) { commit.latest_pipeline } + + it_behaves_like 'fetching latest pipeline', nil + end + + context 'when a particular ref is specified' do + let(:result) { commit.latest_pipeline('master') } + + it_behaves_like 'fetching latest pipeline', 'master' + end + end + + describe '#latest_pipeline_for_project' do + let(:project_pipelines) { double } + let(:pipeline_project) { double } + let(:pipeline) { double } + let(:ref) { 'master' } + let(:result) { commit.latest_pipeline_for_project(ref, pipeline_project) } + + before do + allow(pipeline_project).to receive(:ci_pipelines).and_return(project_pipelines) + end + + it 'returns the latest pipeline of the commit for the given ref and project' do + expect(project_pipelines) + .to receive(:latest_pipeline_per_commit) + .with(commit.id, ref) + .and_return(commit.id => pipeline) + + expect(result).to eq(pipeline) + end + end + + describe '#set_latest_pipeline_for_ref' do + let(:pipeline) { double } + + it 'sets the latest pipeline for a given reference' do + commit.set_latest_pipeline_for_ref('master', pipeline) + + expect(commit.latest_pipeline('master')).to eq(pipeline) + end + end + + describe "#status" do + it 'returns the status of the latest pipeline for the given ref' do + expect(commit) + .to receive(:latest_pipeline) + .with('master') + .and_return(double(status: 'success')) + + expect(commit.status('master')).to eq('success') + end + + it 'returns nil when latest pipeline is not present for the given ref' do + expect(commit) + .to receive(:latest_pipeline) + .with('master') + .and_return(nil) + + expect(commit.status('master')).to eq(nil) + end + + it 'returns the status of the latest pipeline when no ref is given' do + expect(commit) + .to receive(:latest_pipeline) + .with(nil) + .and_return(double(status: 'success')) + + expect(commit.status).to eq('success') + end + end +end diff --git a/spec/presenters/commit_presenter_spec.rb b/spec/presenters/commit_presenter_spec.rb index 4a0d3a28c32..58179e9a337 100644 --- a/spec/presenters/commit_presenter_spec.rb +++ b/spec/presenters/commit_presenter_spec.rb @@ -17,15 +17,19 @@ describe CommitPresenter do end it 'returns commit status for ref' do - expect(commit).to receive(:status).with('ref').and_return('test') + pipeline = double + status = double - expect(subject).to eq('test') + expect(commit).to receive(:latest_pipeline).with('ref').and_return(pipeline) + expect(pipeline).to receive(:detailed_status).with(user).and_return(status) + + expect(subject).to eq(status) end end context 'when user can not read_commit_status' do - it 'is false' do - is_expected.to eq(false) + it 'is nil' do + is_expected.to eq(nil) end end end diff --git a/spec/support/helpers/select2_helper.rb b/spec/support/helpers/select2_helper.rb index 9c42c2b0d8b..38bf34bdd61 100644 --- a/spec/support/helpers/select2_helper.rb +++ b/spec/support/helpers/select2_helper.rb @@ -24,7 +24,7 @@ module Select2Helper selector = options.fetch(:from) - first(selector, visible: false) + ensure_select2_loaded(selector) if options[:multiple] execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');") @@ -34,14 +34,24 @@ module Select2Helper end def open_select2(selector) + ensure_select2_loaded(selector) + execute_script("$('#{selector}').select2('open');") end def close_select2(selector) + ensure_select2_loaded(selector) + execute_script("$('#{selector}').select2('close');") end def scroll_select2_to_bottom(selector) evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');" end + + private + + def ensure_select2_loaded(selector) + first(selector, visible: :all).sibling('.select2-container') + end end diff --git a/spec/support/omniauth_strategy.rb b/spec/support/omniauth_strategy.rb index eefa04bd9dd..23907b8e450 100644 --- a/spec/support/omniauth_strategy.rb +++ b/spec/support/omniauth_strategy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StrategyHelpers include Rack::Test::Methods include ActionDispatch::Assertions::ResponseAssertions diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore index 259148fa18f..259148fa18f 100755..100644 --- a/vendor/gitignore/C++.gitignore +++ b/vendor/gitignore/C++.gitignore diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore index a1c2a238a96..a1c2a238a96 100755..100644 --- a/vendor/gitignore/Java.gitignore +++ b/vendor/gitignore/Java.gitignore diff --git a/yarn.lock b/yarn.lock index 59bdbd7a2d2..4d7d62851be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -995,10 +995,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.75.0.tgz#93f9e6bdef78dd84ac88d8273711dc1f25e4e5ac" integrity sha512-hOCfF73++yG+KTYxaQNMkbDUg0XKije41g6XR2dgj7466rzZmebG/nt6pUXonmlqy/NLGaRUPBKs0zuM7tcLhA== -"@gitlab/ui@5.26.1": - version "5.26.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.26.1.tgz#1662af9be444239bcf9570ea9a4c31aef4639a00" - integrity sha512-PtOuAQWkShA5O78Gy16BtBsic1O9jl7dc07jusgYL5rmIYw/cRZiI92/RZwkelJahdbJpaD9YulsjgsejDIp8g== +"@gitlab/ui@5.26.2": + version "5.26.2" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.26.2.tgz#b1474152e91a7f208f4c454a83b4f2f492afce57" + integrity sha512-atRTd7C2rby1vWQNAT2aokGHTAFcNtQsIhKmIC0Q1phnsnyWHVqT/xURr9cAiBpGznPooNVlQDldLOBqSoKcHA== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.2.1" |