diff options
46 files changed, 361 insertions, 107 deletions
diff --git a/app/assets/javascripts/clusters.js b/app/assets/javascripts/clusters.js index b36df96a4d3..60c7f3752bb 100644 --- a/app/assets/javascripts/clusters.js +++ b/app/assets/javascripts/clusters.js @@ -1,6 +1,7 @@ /* globals Flash */ import Visibility from 'visibilityjs'; import axios from 'axios'; +import setAxiosCsrfToken from './lib/utils/axios_utils'; import Poll from './lib/utils/poll'; import { s__ } from './locale'; import './flash'; @@ -16,6 +17,7 @@ import './flash'; class ClusterService { constructor(options = {}) { this.options = options; + setAxiosCsrfToken(); } fetchData() { return axios.get(this.options.endpoint); diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js new file mode 100644 index 00000000000..45bff245827 --- /dev/null +++ b/app/assets/javascripts/lib/utils/axios_utils.js @@ -0,0 +1,6 @@ +import axios from 'axios'; +import csrf from './csrf'; + +export default function setAxiosCsrfToken() { + axios.defaults.headers.common[csrf.headerKey] = csrf.token; +} diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js index 8635ccece6e..d34a21b37e1 100644 --- a/app/assets/javascripts/settings_panels.js +++ b/app/assets/javascripts/settings_panels.js @@ -1,34 +1,26 @@ -function expandSectionParent($section, $content) { - $section.addClass('expanded'); - $content.off('animationend.expandSectionParent'); -} - function expandSection($section) { $section.find('.js-settings-toggle').text('Collapse'); - - const $content = $section.find('.settings-content'); - $content.addClass('expanded').off('scroll.expandSection').scrollTop(0); - - if ($content.hasClass('no-animate')) { - expandSectionParent($section, $content); - } else { - $content.on('animationend.expandSectionParent', () => expandSectionParent($section, $content)); + $section.find('.settings-content').off('scroll.expandSection').scrollTop(0); + $section.addClass('expanded'); + if (!$section.hasClass('no-animate')) { + $section.addClass('animating') + .one('animationend.animateSection', () => $section.removeClass('animating')); } } function closeSection($section) { $section.find('.js-settings-toggle').text('Expand'); - - const $content = $section.find('.settings-content'); - $content.removeClass('expanded').on('scroll.expandSection', () => expandSection($section)); - + $section.find('.settings-content').on('scroll.expandSection', () => expandSection($section)); $section.removeClass('expanded'); + if (!$section.hasClass('no-animate')) { + $section.addClass('animating') + .one('animationend.animateSection', () => $section.removeClass('animating')); + } } function toggleSection($section) { - const $content = $section.find('.settings-content'); - $content.removeClass('no-animate'); - if ($content.hasClass('expanded')) { + $section.removeClass('no-animate'); + if ($section.hasClass('expanded')) { closeSection($section); } else { expandSection($section); @@ -39,10 +31,19 @@ export default function initSettingsPanels() { $('.settings').each((i, elm) => { const $section = $(elm); $section.on('click.toggleSection', '.js-settings-toggle', () => toggleSection($section)); - $section.find('.settings-content:not(.expanded)').on('scroll.expandSection', () => expandSection($section)); + + if (!$section.hasClass('expanded')) { + $section.find('.settings-content').on('scroll.expandSection', () => { + $section.removeClass('no-animate'); + expandSection($section); + }); + } }); if (location.hash) { - expandSection($(location.hash)); + const $target = $(location.hash); + if ($target.length && $target.hasClass('.settings')) { + expandSection($target); + } } } diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 41a6ba2023a..968a94c68cf 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -23,15 +23,14 @@ } .settings { - overflow: hidden; border-bottom: 1px solid $gray-darker; &:first-of-type { margin-top: 10px; } - &.expanded { - overflow: visible; + &.animating { + overflow: hidden; } } @@ -56,14 +55,18 @@ overflow-y: scroll; padding-right: 110px; animation: collapseMaxHeight 300ms ease-out; + // Keep the section from expanding when we scroll over it + pointer-events: none; - &.expanded { + .settings.expanded & { max-height: none; overflow-y: visible; animation: expandMaxHeight 300ms ease-in; + // Reset and allow clicks again when expanded + pointer-events: auto; } - &.no-animate { + .settings.no-animate & { animation: none; } diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb index 2b6afaa6233..738afd612f0 100644 --- a/app/controllers/concerns/lfs_request.rb +++ b/app/controllers/concerns/lfs_request.rb @@ -94,10 +94,9 @@ module LfsRequest @storage_project ||= begin result = project - loop do - break unless result.forked? - result = result.forked_from_project - end + # TODO: Make this go to the fork_network root immeadiatly + # dependant on the discussion in: https://gitlab.com/gitlab-org/gitlab-ce/issues/39769 + result = result.fork_source while result.forked? result end diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index 1126f706393..fb9c942d302 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -4,6 +4,7 @@ module NotesActions included do before_action :set_polling_interval_header, only: [:index] + before_action :noteable, only: :index before_action :authorize_admin_note!, only: [:update, :destroy] before_action :note_project, only: [:create] end @@ -188,7 +189,7 @@ module NotesActions end def noteable - @noteable ||= notes_finder.target + @noteable ||= notes_finder.target || render_404 end def last_fetched_at diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 20e050195ea..d21c01cc27f 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -97,7 +97,15 @@ module ProjectsHelper def remove_fork_project_message(project) _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") % - { forked_from_project: @project.forked_from_project.name_with_namespace } + { forked_from_project: fork_source_name(project) } + end + + def fork_source_name(project) + if @project.fork_source + @project.fork_source.full_name + else + @project.fork_network&.deleted_root_project_name + end end def project_nav_tabs @@ -127,8 +135,8 @@ module ProjectsHelper def can_change_visibility_level?(project, current_user) return false unless can?(current_user, :change_visibility_level, project) - if project.forked? - project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE + if project.fork_source + project.fork_source.visibility_level > Gitlab::VisibilityLevel::PRIVATE else true end diff --git a/app/models/environment.rb b/app/models/environment.rb index e613d21add6..8d6b0a32c13 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -110,7 +110,7 @@ class Environment < ActiveRecord::Base end def ref_path - "refs/#{Repository::REF_ENVIRONMENTS}/#{generate_slug}" + "refs/#{Repository::REF_ENVIRONMENTS}/#{slug}" end def formatted_external_url @@ -164,6 +164,10 @@ class Environment < ActiveRecord::Base end end + def slug + super.presence || generate_slug + end + # An environment name is not necessarily suitable for use in URLs, DNS # or other third-party contexts, so provide a slugified version. A slug has # the following properties: diff --git a/app/models/fork_network.rb b/app/models/fork_network.rb index 218e37a5312..7f1728e8c77 100644 --- a/app/models/fork_network.rb +++ b/app/models/fork_network.rb @@ -12,4 +12,8 @@ class ForkNetwork < ActiveRecord::Base def find_forks_in(other_projects) projects.where(id: other_projects) end + + def merge_requests + MergeRequest.where(target_project: projects) + end end diff --git a/app/models/project.rb b/app/models/project.rb index 984ef015f4a..4ee6db9211f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1017,6 +1017,10 @@ class Project < ActiveRecord::Base !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) end + def fork_source + forked_from_project || fork_network&.root_project + end + def personal? !group end diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb index a02eee4961b..6b3939aeba5 100644 --- a/app/services/metrics_service.rb +++ b/app/services/metrics_service.rb @@ -6,8 +6,7 @@ class MetricsService Gitlab::HealthChecks::Redis::RedisCheck, Gitlab::HealthChecks::Redis::CacheCheck, Gitlab::HealthChecks::Redis::QueuesCheck, - Gitlab::HealthChecks::Redis::SharedStateCheck, - Gitlab::HealthChecks::FsShardsCheck + Gitlab::HealthChecks::Redis::SharedStateCheck ].freeze def prometheus_metrics_text diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb index 2b82e5732e4..c499f384426 100644 --- a/app/services/projects/unlink_fork_service.rb +++ b/app/services/projects/unlink_fork_service.rb @@ -3,18 +3,24 @@ module Projects def execute return unless @project.forked? - @project.forked_from_project.lfs_objects.find_each do |lfs_object| - lfs_object.projects << @project + if fork_source = @project.fork_source + fork_source.lfs_objects.find_each do |lfs_object| + lfs_object.projects << @project + end + + refresh_forks_count(fork_source) end - merge_requests = @project.forked_from_project.merge_requests.opened.from_project(@project) + merge_requests = @project.fork_network + .merge_requests + .opened + .where.not(target_project: @project) + .from_project(@project) merge_requests.each do |mr| ::MergeRequests::CloseService.new(@project, @current_user).execute(mr) end - refresh_forks_count(@project.forked_from_project) - @project.fork_network_member.destroy @project.forked_project_link.destroy end diff --git a/app/views/admin/hook_logs/_index.html.haml b/app/views/admin/hook_logs/_index.html.haml index 7dd9943190f..91a8c0c62fe 100644 --- a/app/views/admin/hook_logs/_index.html.haml +++ b/app/views/admin/hook_logs/_index.html.haml @@ -24,7 +24,7 @@ %td = truncate(hook_log.url, length: 50) %td.light - #{number_with_precision(hook_log.execution_duration, precision: 2)} ms + #{number_with_precision(hook_log.execution_duration, precision: 2)} sec %td.light = time_ago_with_tooltip(hook_log.created_at) %td diff --git a/app/views/projects/_export.html.haml b/app/views/projects/_export.html.haml index 623d3bc91c6..c5b1897c492 100644 --- a/app/views/projects/_export.html.haml +++ b/app/views/projects/_export.html.haml @@ -3,7 +3,7 @@ - project = local_assigns.fetch(:project) - expanded = Rails.env.test? -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Export project @@ -11,7 +11,7 @@ = expanded ? 'Collapse' : 'Expand' %p Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content .bs-callout.bs-callout-info %p.append-bottom-0 %p diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 619b632918e..1d644dda177 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,6 +1,5 @@ - empty_repo = @project.empty_repo? - fork_network = @project.fork_network -- forked_from_project = @project.forked_from_project || fork_network&.root_project .project-home-panel.text-center{ class: ("empty-project" if empty_repo) } .limit-container-width{ class: container_class } .avatar-container.s70.project-avatar @@ -16,13 +15,13 @@ - if @project.forked? %p - - if forked_from_project + - if @project.fork_source #{ s_('ForkedFromProjectPath|Forked from') } - = link_to project_path(forked_from_project) do - = forked_from_project.full_name + = link_to project_path(@project.fork_source) do + = fork_source_name(@project) - else - deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)') - = deleted_message % { project_name: fork_network.deleted_root_project_name } + = deleted_message % { project_name: fork_source_name(@project) } .project-repo-buttons .count-buttons diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml index 45985a5ecef..e75ae87e771 100644 --- a/app/views/projects/deploy_keys/_index.html.haml +++ b/app/views/projects/deploy_keys/_index.html.haml @@ -1,5 +1,5 @@ - expanded = Rails.env.test? -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Deploy Keys @@ -7,7 +7,7 @@ = expanded ? 'Collapse' : 'Expand' %p Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content %h5.prepend-top-0 Create a new deploy key for this project = render @deploy_keys.form_partial_path diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 893e536e289..5ebeae5c35f 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -4,7 +4,7 @@ - expanded = Rails.env.test? .project-edit-container - %section.settings.general-settings + %section.settings.general-settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 General project settings @@ -12,7 +12,7 @@ = expanded ? 'Collapse' : 'Expand' %p Update your project name, description, avatar, and other general settings. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content .project-edit-errors = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f| %fieldset @@ -61,7 +61,7 @@ = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" = f.submit 'Save changes', class: "btn btn-save" - %section.settings.sharing-permissions + %section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Permissions @@ -69,13 +69,13 @@ = expanded ? 'Collapse' : 'Expand' %p Enable or disable certain project features and choose access levels. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f| %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project) .js-project-permissions-form = f.submit 'Save changes', class: "btn btn-save" - %section.settings.merge-requests-feature{ class: ("hidden" if @project.project_feature.send(:merge_requests_access_level) == 0) } + %section.settings.merge-requests-feature.no-animate{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] } .settings-header %h4 Merge request settings @@ -83,14 +83,14 @@ = expanded ? 'Collapse' : 'Expand' %p Customize your merge request restrictions. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f| = render 'merge_request_settings', form: f = f.submit 'Save changes', class: "btn btn-save" = render 'export', project: @project - %section.settings.advanced-settings + %section.settings.advanced-settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Advanced settings @@ -98,7 +98,7 @@ = expanded ? 'Collapse' : 'Expand' %p Perform advanced options such as housekeeping, archiving, renaming, transferring, or removing your project. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content .sub-section %h4 Housekeeping %p @@ -173,7 +173,10 @@ %p This will remove the fork relationship to source project = succeed "." do - = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) + - if @project.fork_source + = link_to(fork_source_name(@project), project_path(@project.fork_source)) + - else + = fork_source_name(@project) = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| %p %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. diff --git a/app/views/projects/hook_logs/_index.html.haml b/app/views/projects/hook_logs/_index.html.haml index 05b06cfc8b2..8096d9530c3 100644 --- a/app/views/projects/hook_logs/_index.html.haml +++ b/app/views/projects/hook_logs/_index.html.haml @@ -24,7 +24,7 @@ %td = truncate(hook_log.url, length: 50) %td.light - #{number_with_precision(hook_log.execution_duration, precision: 2)} ms + #{number_with_precision(hook_log.execution_duration, precision: 2)} sec %td.light = time_ago_with_tooltip(hook_log.created_at) %td diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/projects/protected_branches/shared/_index.html.haml index 6a47cbdf724..ba7d98228c3 100644 --- a/app/views/projects/protected_branches/shared/_index.html.haml +++ b/app/views/projects/protected_branches/shared/_index.html.haml @@ -1,6 +1,6 @@ - expanded = Rails.env.test? -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Protected Branches @@ -8,7 +8,7 @@ = expanded ? 'Collapse' : 'Expand' %p Keep stable branches secure and force developers to use merge requests. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content %p By default, protected branches are designed to: %ul diff --git a/app/views/projects/protected_tags/shared/_index.html.haml b/app/views/projects/protected_tags/shared/_index.html.haml index c07bd454ff6..e764a37bbd7 100644 --- a/app/views/projects/protected_tags/shared/_index.html.haml +++ b/app/views/projects/protected_tags/shared/_index.html.haml @@ -1,6 +1,6 @@ - expanded = Rails.env.test? -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Protected Tags @@ -8,7 +8,7 @@ = expanded ? 'Collapse' : 'Expand' %p Limit access to creating and updating tags. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content %p By default, protected tags are designed to: %ul diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 62455d0d40d..664a4554692 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -4,7 +4,7 @@ - expanded = Rails.env.test? -%section.settings#js-general-pipeline-settings +%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 General pipelines settings @@ -12,10 +12,10 @@ = expanded ? 'Collapse' : 'Expand' %p Update your CI/CD configuration, like job timeout or Auto DevOps. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = render 'projects/pipelines_settings/show' -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Runners settings @@ -23,10 +23,10 @@ = expanded ? 'Collapse' : 'Expand' %p Register and see your runners for this project. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = render 'projects/runners/index' -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Secret variables @@ -35,10 +35,10 @@ = expanded ? 'Collapse' : 'Expand' %p = render "ci/variables/content" - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = render 'ci/variables/index' -%section.settings +%section.settings.no-animate{ class: ('expanded' if expanded) } .settings-header %h4 Pipeline triggers @@ -48,5 +48,5 @@ Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions. - .settings-content.no-animate{ class: ('expanded' if expanded) } + .settings-content = render 'projects/triggers/index' diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml index af6a499fadb..c80b179d525 100644 --- a/app/views/shared/hook_logs/_content.html.haml +++ b/app/views/shared/hook_logs/_content.html.haml @@ -11,7 +11,7 @@ = hook_log.trigger.singularize.titleize %p %strong Elapsed time: - #{number_with_precision(hook_log.execution_duration, precision: 2)} ms + #{number_with_precision(hook_log.execution_duration, precision: 2)} sec %p %strong Request time: = time_ago_with_tooltip(hook_log.created_at) diff --git a/changelogs/unreleased/39704_fix_webhooks_log_time.yml b/changelogs/unreleased/39704_fix_webhooks_log_time.yml new file mode 100644 index 00000000000..1234663e66b --- /dev/null +++ b/changelogs/unreleased/39704_fix_webhooks_log_time.yml @@ -0,0 +1,5 @@ +--- +title: Fix webhooks recent deliveries +merge_request: 15146 +author: Alexander Randa (@randaalex) +type: fixed diff --git a/changelogs/unreleased/bvl-unlink-fixes.yml b/changelogs/unreleased/bvl-unlink-fixes.yml new file mode 100644 index 00000000000..685d78f479d --- /dev/null +++ b/changelogs/unreleased/bvl-unlink-fixes.yml @@ -0,0 +1,5 @@ +--- +title: Fix issues with forked projects of which the source was deleted +merge_request: 15150 +author: +type: fixed diff --git a/changelogs/unreleased/fix_diff_parsing.yml b/changelogs/unreleased/fix_diff_parsing.yml new file mode 100644 index 00000000000..7a26b4f9ff5 --- /dev/null +++ b/changelogs/unreleased/fix_diff_parsing.yml @@ -0,0 +1,5 @@ +--- +title: Fix diff parser so it tolerates to diff special markers in the content +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix_migration_that_adds_ff_merge_field.yml b/changelogs/unreleased/fix_migration_that_adds_ff_merge_field.yml new file mode 100644 index 00000000000..a1685497331 --- /dev/null +++ b/changelogs/unreleased/fix_migration_that_adds_ff_merge_field.yml @@ -0,0 +1,5 @@ +--- +title: Fix a migration that adds merge_requests_ff_only_enabled column to MR table +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/issue_39176.yml b/changelogs/unreleased/issue_39176.yml new file mode 100644 index 00000000000..6255b51c094 --- /dev/null +++ b/changelogs/unreleased/issue_39176.yml @@ -0,0 +1,5 @@ +--- +title: Render 404 when polling commit notes without having permissions +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/pawel-disable_nfs_metrics_checks_39730.yml b/changelogs/unreleased/pawel-disable_nfs_metrics_checks_39730.yml new file mode 100644 index 00000000000..556d7d069d3 --- /dev/null +++ b/changelogs/unreleased/pawel-disable_nfs_metrics_checks_39730.yml @@ -0,0 +1,5 @@ +--- +title: Remove Filesystem check metrics that use too much CPU to handle requests +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/sh-fix-environment-slug-generation.yml b/changelogs/unreleased/sh-fix-environment-slug-generation.yml new file mode 100644 index 00000000000..8a9c670c52c --- /dev/null +++ b/changelogs/unreleased/sh-fix-environment-slug-generation.yml @@ -0,0 +1,5 @@ +--- +title: Avoid regenerating the ref path for the environment +merge_request: +author: +type: fixed diff --git a/db/migrate/20150827121444_add_fast_forward_option_to_project.rb b/db/migrate/20150827121444_add_fast_forward_option_to_project.rb index 6f22641077d..35df121519e 100644 --- a/db/migrate/20150827121444_add_fast_forward_option_to_project.rb +++ b/db/migrate/20150827121444_add_fast_forward_option_to_project.rb @@ -8,7 +8,11 @@ class AddFastForwardOptionToProject < ActiveRecord::Migration disable_ddl_transaction! def up - add_column_with_default(:projects, :merge_requests_ff_only_enabled, :boolean, default: false) + # We put condition here because of a mistake we made a couple of years ago + # see https://gitlab.com/gitlab-org/gitlab-ce/issues/39382#note_45716103 + unless column_exists?(:projects, :merge_requests_ff_only_enabled) + add_column_with_default(:projects, :merge_requests_ff_only_enabled, :boolean, default: false) + end end def down diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 73568757aaa..bdd416b8372 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -43,7 +43,7 @@ future GitLab releases.** | **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. | | **CI_CONFIG_PATH** | 9.4 | 0.5 | The path to CI config file. Defaults to `.gitlab-ci.yml` | | **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | -| **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Mark that job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. | +| **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. | | **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job | | **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. | | **CI_ENVIRONMENT_URL** | 9.3 | all | The URL of the environment for this job | @@ -74,7 +74,7 @@ future GitLab releases.** | **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs | | **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | | **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | -| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Mark that job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. | +| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. | | **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job | | **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | | **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | diff --git a/doc/user/project/integrations/img/webhook_logs.png b/doc/user/project/integrations/img/webhook_logs.png Binary files differindex 917068d9398..803678db6b6 100644 --- a/doc/user/project/integrations/img/webhook_logs.png +++ b/doc/user/project/integrations/img/webhook_logs.png diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md index 9ef6f9185c9..f9a268fb789 100644 --- a/doc/user/project/pipelines/job_artifacts.md +++ b/doc/user/project/pipelines/job_artifacts.md @@ -52,7 +52,8 @@ directly in the job artifacts browser without the need to download them. >**Note:** With [GitLab 10.1][ce-14399], HTML files in a public project can be previewed -directly in a new tab without the need to download them. +directly in a new tab without the need to download them when +[GitLab Pages](../../../administration/pages/index.md) is enabled After a job finishes, if you visit the job's specific page, there are three buttons. You can download the artifacts archive or browse its contents, whereas @@ -69,7 +70,8 @@ browse inside them. Below you can see how browsing looks like. In this case we have browsed inside the archive and at this point there is one directory, a couple files, and -one HTML file that you can view directly online (opens in a new tab). +one HTML file that you can view directly online when +[GitLab Pages](../../../administration/pages/index.md) is enabled (opens in a new tab). ![Job artifacts browser](img/job_artifacts_browser.png) diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb index 742f989c50b..7dc9cc7c281 100644 --- a/lib/gitlab/diff/parser.rb +++ b/lib/gitlab/diff/parser.rb @@ -17,7 +17,9 @@ module Gitlab # without having to instantiate all the others that come after it. Enumerator.new do |yielder| @lines.each do |line| - next if filename?(line) + # We're expecting a filename parameter only in a meta-part of the diff content + # when type is defined then we're already in a content-part + next if filename?(line) && type.nil? full_line = line.delete("\n") diff --git a/lib/gitlab/git/hooks_service.rb b/lib/gitlab/git/hooks_service.rb index c327e9b1616..f302b852b35 100644 --- a/lib/gitlab/git/hooks_service.rb +++ b/lib/gitlab/git/hooks_service.rb @@ -8,7 +8,7 @@ module Gitlab def execute(pusher, repository, oldrev, newrev, ref) @repository = repository @gl_id = pusher.gl_id - @gl_username = pusher.name + @gl_username = pusher.username @oldrev = oldrev @newrev = newrev @ref = ref diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb new file mode 100644 index 00000000000..33b23db302a --- /dev/null +++ b/spec/controllers/concerns/lfs_request_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe LfsRequest do + include ProjectForksHelper + + controller(Projects::GitHttpClientController) do + # `described_class` is not available in this context + include LfsRequest # rubocop:disable RSpec/DescribedClass + + def show + storage_project + + render nothing: true + end + + def project + @project ||= Project.find(params[:id]) + end + + def download_request? + true + end + + def ci? + false + end + end + + let(:project) { create(:project, :public) } + + before do + stub_lfs_setting(enabled: true) + end + + describe '#storage_project' do + it 'assigns the project as storage project' do + get :show, id: project.id + + expect(assigns(:storage_project)).to eq(project) + end + + it 'assigns the source of a forked project' do + forked_project = fork_project(project) + + get :show, id: forked_project.id + + expect(assigns(:storage_project)).to eq(project) + end + end +end diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 7b0976e3e67..4aed2a25baa 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -59,17 +59,6 @@ describe MetricsController do expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/) end - it 'returns file system check metrics' do - get :index - - expect(response.body).to match(/^filesystem_access_latency_seconds{shard="default"} [0-9\.]+$/) - expect(response.body).to match(/^filesystem_accessible{shard="default"} 1$/) - expect(response.body).to match(/^filesystem_write_latency_seconds{shard="default"} [0-9\.]+$/) - expect(response.body).to match(/^filesystem_writable{shard="default"} 1$/) - expect(response.body).to match(/^filesystem_read_latency_seconds{shard="default"} [0-9\.]+$/) - expect(response.body).to match(/^filesystem_readable{shard="default"} 1$/) - end - context 'prometheus metrics are disabled' do before do allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(false) diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 135fd6449ff..77bf997bec5 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -105,6 +105,19 @@ describe Projects::NotesController do expect(note_json[:discussion_html]).to be_nil expect(note_json[:diff_discussion_html]).to be_nil end + + context 'when user cannot read commit' do + before do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(user, :download_code, project).and_return(false) + end + + it 'renders 404' do + get :index, params + + expect(response).to have_gitlab_http_status(404) + end + end end end diff --git a/spec/features/projects/settings/forked_project_settings_spec.rb b/spec/features/projects/settings/forked_project_settings_spec.rb new file mode 100644 index 00000000000..28954a4fb40 --- /dev/null +++ b/spec/features/projects/settings/forked_project_settings_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +feature 'Settings for a forked project', :js do + include ProjectForksHelper + let(:user) { create(:user) } + let(:original_project) { create(:project) } + let(:forked_project) { fork_project(original_project, user) } + + before do + original_project.add_master(user) + forked_project.add_master(user) + sign_in(user) + end + + shared_examples 'project settings for a forked projects' do + it 'allows deleting the link to the forked project' do + visit edit_project_path(forked_project) + + click_button 'Remove fork relationship' + + wait_for_requests + + fill_in('confirm_name_input', with: forked_project.name) + click_button('Confirm') + + expect(page).to have_content('The fork relationship has been removed.') + expect(forked_project.reload.forked?).to be_falsy + end + end + + it_behaves_like 'project settings for a forked projects' + + context 'when the original project is deleted' do + before do + original_project.destroy! + end + + it_behaves_like 'project settings for a forked projects' + end +end diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb index 8af49ed50ff..80c8c189665 100644 --- a/spec/lib/gitlab/diff/parser_spec.rb +++ b/spec/lib/gitlab/diff/parser_spec.rb @@ -143,4 +143,21 @@ eos it { expect(parser.parse([])).to eq([]) } it { expect(parser.parse(nil)).to eq([]) } end + + describe 'tolerates special diff markers in a content' do + it "counts lines correctly" do + diff = <<~END + --- a/test + +++ b/test + @@ -1,2 +1,2 @@ + +ipsum + +++ b + -ipsum + END + + lines = parser.parse(diff.lines).to_a + + expect(lines.size).to eq(3) + end + end end diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb index 51e4e3fdad1..3ed3feb4c74 100644 --- a/spec/lib/gitlab/git/hooks_service_spec.rb +++ b/spec/lib/gitlab/git/hooks_service_spec.rb @@ -1,24 +1,26 @@ require 'spec_helper' describe Gitlab::Git::HooksService, seed_helper: true do - let(:user) { Gitlab::Git::User.new('janedoe', 'Jane Doe', 'janedoe@example.com', 'user-456') } + let(:gl_id) { 'user-456' } + let(:gl_username) { 'janedoe' } + let(:user) { Gitlab::Git::User.new(gl_username, 'Jane Doe', 'janedoe@example.com', gl_id) } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, 'project-123') } let(:service) { described_class.new } - - before do - @blankrev = Gitlab::Git::BLANK_SHA - @oldrev = SeedRepo::Commit::PARENT_ID - @newrev = SeedRepo::Commit::ID - @ref = 'refs/heads/feature' - end + let(:blankrev) { Gitlab::Git::BLANK_SHA } + let(:oldrev) { SeedRepo::Commit::PARENT_ID } + let(:newrev) { SeedRepo::Commit::ID } + let(:ref) { 'refs/heads/feature' } describe '#execute' do context 'when receive hooks were successful' do - it 'calls post-receive hook' do - hook = double(trigger: [true, nil]) + let(:hook) { double(:hook) } + + it 'calls all three hooks' do expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) + expect(hook).to receive(:trigger).with(gl_id, gl_username, blankrev, newrev, ref) + .exactly(3).times.and_return([true, nil]) - service.execute(user, repository, @blankrev, @newrev, @ref) { } + service.execute(user, repository, blankrev, newrev, ref) { } end end @@ -28,7 +30,7 @@ describe Gitlab::Git::HooksService, seed_helper: true do expect(service).not_to receive(:run_hook).with('post-receive') expect do - service.execute(user, repository, @blankrev, @newrev, @ref) + service.execute(user, repository, blankrev, newrev, ref) end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) end end @@ -40,7 +42,7 @@ describe Gitlab::Git::HooksService, seed_helper: true do expect(service).not_to receive(:run_hook).with('post-receive') expect do - service.execute(user, repository, @blankrev, @newrev, @ref) + service.execute(user, repository, blankrev, newrev, ref) end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index e1be23541e8..f75de0a0d88 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -547,6 +547,15 @@ describe Environment do expect(environment.slug).to eq(original_slug) end + + it "regenerates the slug if nil" do + environment = build(:environment, slug: nil) + + new_slug = environment.slug + + expect(new_slug).not_to be_nil + expect(environment.slug).to eq(new_slug) + end end describe '#generate_slug' do @@ -583,6 +592,12 @@ describe Environment do it 'returns a path that uses the slug and does not have spaces' do expect(environment.ref_path).to start_with('refs/environments/staging-review-1-') end + + it "doesn't change when the slug is nil initially" do + environment.slug = nil + + expect(environment.ref_path).to eq(environment.ref_path) + end end describe '#external_url_for' do diff --git a/spec/models/fork_network_spec.rb b/spec/models/fork_network_spec.rb index 605ccd6db06..a43baf1820a 100644 --- a/spec/models/fork_network_spec.rb +++ b/spec/models/fork_network_spec.rb @@ -24,6 +24,16 @@ describe ForkNetwork do end end + describe '#merge_requests' do + it 'finds merge requests within the fork network' do + project = create(:project) + forked_project = fork_project(project) + merge_request = create(:merge_request, source_project: forked_project, target_project: project) + + expect(project.fork_network.merge_requests).to include(merge_request) + end + end + context 'for a deleted project' do it 'keeps the fork network' do project = create(:project, :public) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index cf26dbfea49..83be9642437 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1907,6 +1907,20 @@ describe Project do expect(forked_project.in_fork_network_of?(other_project)).to be_falsy end end + + describe '#fork_source' do + let!(:second_fork) { fork_project(forked_project) } + + it 'returns the direct source if it exists' do + expect(second_fork.fork_source).to eq(forked_project) + end + + it 'returns the root of the fork network when the directs source was deleted' do + forked_project.destroy + + expect(second_fork.fork_source).to eq(project) + end + end end describe '#pushes_since_gc' do diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb index 50d3a4ec982..2bba71fef4f 100644 --- a/spec/services/projects/unlink_fork_service_spec.rb +++ b/spec/services/projects/unlink_fork_service_spec.rb @@ -12,6 +12,9 @@ describe Projects::UnlinkForkService do context 'with opened merge request on the source project' do let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: fork_link.forked_from_project) } + let(:merge_request2) { create(:merge_request, source_project: forked_project, target_project: fork_project(project)) } + let(:merge_request_in_fork) { create(:merge_request, source_project: forked_project, target_project: forked_project) } + let(:mr_close_service) { MergeRequests::CloseService.new(forked_project, user) } before do @@ -22,9 +25,14 @@ describe Projects::UnlinkForkService do it 'close all pending merge requests' do expect(mr_close_service).to receive(:execute).with(merge_request) + expect(mr_close_service).to receive(:execute).with(merge_request2) subject.execute end + + it 'does not close merge requests for the project being unlinked' do + expect(mr_close_service).not_to receive(:execute).with(merge_request_in_fork) + end end it 'remove fork relation' do @@ -53,4 +61,14 @@ describe Projects::UnlinkForkService do expect(source.forks_count).to be_zero end + + context 'when the original project was deleted' do + it 'does not fail when the original project is deleted' do + source = forked_project.forked_from_project + source.destroy + forked_project.reload + + expect { subject.execute }.not_to raise_error + end + end end diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index 4d448a55978..4ead78529c3 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -38,6 +38,10 @@ module StubConfiguration allow(Gitlab.config.backup).to receive_messages(to_settings(messages)) end + def stub_lfs_setting(messages) + allow(Gitlab.config.lfs).to receive_messages(to_settings(messages)) + end + def stub_storage_settings(messages) # Default storage is always required messages['default'] ||= Gitlab.config.repositories.storages.default |