diff options
author | Marcia Ramos <virtua.creative@gmail.com> | 2017-08-15 14:13:06 -0300 |
---|---|---|
committer | Marcia Ramos <virtua.creative@gmail.com> | 2017-08-15 14:13:06 -0300 |
commit | b4e0be609593524e66db5e1ea1df066fa8f180f9 (patch) | |
tree | 168f5796cf5bc2860f3ac4f1333bd2d1e5e7c12e | |
parent | 06785f92a13f9f722515cc2363569437c9ccd339 (diff) | |
parent | 05151f7699ee2adf67ce835413dfdbd80c58a3e5 (diff) | |
download | gitlab-ce-b4e0be609593524e66db5e1ea1df066fa8f180f9.tar.gz |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 33329-tech-article-deploying-maven-artifacts
71 files changed, 510 insertions, 101 deletions
diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js index 1c379e9bb67..7ac9dcd1112 100644 --- a/app/assets/javascripts/gpg_badges.js +++ b/app/assets/javascripts/gpg_badges.js @@ -1,12 +1,14 @@ export default class GpgBadges { static fetch() { + const badges = $('.js-loading-gpg-badge'); const form = $('.commits-search-form'); + badges.html('<i class="fa fa-spinner fa-spin"></i>'); + $.get({ url: form.data('signatures-path'), data: form.serialize(), }).done((response) => { - const badges = $('.js-loading-gpg-badge'); response.signatures.forEach((signature) => { badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); }); diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js deleted file mode 100644 index c827b7402dc..00000000000 --- a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js +++ /dev/null @@ -1,50 +0,0 @@ -import Vue from 'vue'; -import Cookies from 'js-cookie'; -import Translate from '../../vue_shared/translate'; -import illustrationSvg from '../icons/intro_illustration.svg'; - -Vue.use(Translate); - -const cookieKey = 'pipeline_schedules_callout_dismissed'; - -export default { - name: 'PipelineSchedulesCallout', - data() { - return { - docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl, - illustrationSvg, - calloutDismissed: Cookies.get(cookieKey) === 'true', - }; - }, - methods: { - dismissCallout() { - this.calloutDismissed = true; - Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 }); - }, - }, - template: ` - <div v-if="!calloutDismissed" class="pipeline-schedules-user-callout user-callout"> - <div class="bordered-box landing content-block"> - <button - id="dismiss-callout-btn" - class="btn btn-default close" - @click="dismissCallout"> - <i class="fa fa-times"></i> - </button> - <div class="svg-container" v-html="illustrationSvg"></div> - <div class="user-callout-copy"> - <h4>{{ __('Scheduling Pipelines') }}</h4> - <p> - {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }} - </p> - <p> {{ __('Learn more in the') }} - <a - :href="docsUrl" - target="_blank" - rel="nofollow">{{ s__('Learn more in the|pipeline schedules documentation') }}</a>. <!-- oneline to prevent extra space before period --> - </p> - </div> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue new file mode 100644 index 00000000000..6e0bc2d697a --- /dev/null +++ b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue @@ -0,0 +1,59 @@ +<script> + import Vue from 'vue'; + import Cookies from 'js-cookie'; + import Translate from '../../vue_shared/translate'; + import illustrationSvg from '../icons/intro_illustration.svg'; + + Vue.use(Translate); + + const cookieKey = 'pipeline_schedules_callout_dismissed'; + + export default { + name: 'PipelineSchedulesCallout', + data() { + return { + docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl, + calloutDismissed: Cookies.get(cookieKey) === 'true', + }; + }, + methods: { + dismissCallout() { + this.calloutDismissed = true; + Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 }); + }, + }, + created() { + this.illustrationSvg = illustrationSvg; + }, + }; +</script> +<template> + <div + v-if="!calloutDismissed" + class="pipeline-schedules-user-callout user-callout"> + <div class="bordered-box landing content-block"> + <button + id="dismiss-callout-btn" + class="btn btn-default close" + @click="dismissCallout"> + <i + aria-hidden="true" + class="fa fa-times"> + </i> + </button> + <div class="svg-container" v-html="illustrationSvg"></div> + <div class="user-callout-copy"> + <h4>{{ __('Scheduling Pipelines') }}</h4> + <p> + {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }} + </p> + <p> {{ __('Learn more in the') }} + <a + :href="docsUrl" + target="_blank" + rel="nofollow">{{ s__('Learn more in the|pipeline schedules documentation') }}</a>. <!-- oneline to prevent extra space before period --> + </p> + </div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js b/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js index 6584549ad06..a6c945e22b0 100644 --- a/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js +++ b/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import PipelineSchedulesCallout from './components/pipeline_schedules_callout'; +import PipelineSchedulesCallout from './components/pipeline_schedules_callout.vue'; document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#pipeline-schedules-callout', diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue index 422c02c7b7e..cfacba09fad 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -71,7 +71,7 @@ export default { /> <div v-if="!isConfidential" class="no-value confidential-value"> <i class="fa fa-eye is-not-confidential"></i> - None + This issue is not confidential </div> <div v-else class="value confidential-value hide-collapsed"> <i aria-hidden="true" data-hidden="true" class="fa fa-eye-slash is-confidential"></i> diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index 795ee91af8b..3e2f23e6b2a 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -403,6 +403,7 @@ header.navbar-gitlab-new { } .breadcrumbs-extra { + display: flex; flex: 0 0 auto; margin-left: auto; } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index cd9f2d787c5..46fbfe5f91e 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -286,6 +286,10 @@ .gpg-status-box { + &:empty { + display: none; + } + &.valid { @include green-status-color; } diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 09cfd06dad3..278d394bc03 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -234,6 +234,8 @@ module ProjectsHelper # If no limit is applied we'll just issue a COUNT since the result set could # be too large to load into memory. def any_projects?(projects) + return projects.any? if projects.is_a?(Array) + if projects.limit_value projects.to_a.any? else diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb index 7b617b359ea..d76c61c369f 100644 --- a/app/mailers/emails/members.rb +++ b/app/mailers/emails/members.rb @@ -11,11 +11,11 @@ module Emails @member_source_type = member_source_type @member_id = member_id - admins = member_source.members.owners_and_masters.includes(:user).pluck(:notification_email) + admins = member_source.members.owners_and_masters.pluck(:notification_email) # A project in a group can have no explicit owners/masters, in that case # we fallbacks to the group's owners/masters. if admins.empty? && member_source.respond_to?(:group) && member_source.group - admins = member_source.group.members.owners_and_masters.includes(:user).pluck(:notification_email) + admins = member_source.group.members.owners_and_masters.pluck(:notification_email) end mail(to: admins, diff --git a/app/models/group.rb b/app/models/group.rb index bd5735ed82e..2816a68257c 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -212,21 +212,39 @@ class Group < Namespace end def user_ids_for_project_authorizations - users_with_parents.pluck(:id) + members_with_parents.pluck(:user_id) end def members_with_parents - GroupMember.active.where(source_id: ancestors.pluck(:id).push(id)).where.not(user_id: nil) + # Avoids an unnecessary SELECT when the group has no parents + source_ids = + if parent_id + self_and_ancestors.reorder(nil).select(:id) + else + id + end + + GroupMember + .active_without_invites + .where(source_id: source_ids) + end + + def members_with_descendants + GroupMember + .active_without_invites + .where(source_id: self_and_descendants.reorder(nil).select(:id)) end def users_with_parents - User.where(id: members_with_parents.select(:user_id)) + User + .where(id: members_with_parents.select(:user_id)) + .reorder(nil) end def users_with_descendants - members_with_descendants = GroupMember.non_request.where(source_id: descendants.pluck(:id).push(id)) - - User.where(id: members_with_descendants.select(:user_id)) + User + .where(id: members_with_descendants.select(:user_id)) + .reorder(nil) end def max_member_access_for_user(user) diff --git a/app/models/member.rb b/app/models/member.rb index b26b5017183..ee2cb13697b 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -41,9 +41,20 @@ class Member < ActiveRecord::Base is_external_invite = arel_table[:user_id].eq(nil).and(arel_table[:invite_token].not_eq(nil)) user_is_active = User.arel_table[:state].eq(:active) - includes(:user).references(:users) - .where(is_external_invite.or(user_is_active)) + user_ok = Arel::Nodes::Grouping.new(is_external_invite).or(user_is_active) + + left_join_users + .where(user_ok) + .where(requested_at: nil) + .reorder(nil) + end + + # Like active, but without invites. For when a User is required. + scope :active_without_invites, -> do + left_join_users + .where(users: { state: 'active' }) .where(requested_at: nil) + .reorder(nil) end scope :invite, -> { where.not(invite_token: nil) } diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 6073fb94a3f..e7bc1d1b080 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -156,6 +156,14 @@ class Namespace < ActiveRecord::Base .base_and_ancestors end + def self_and_ancestors + return self.class.where(id: id) unless parent_id + + Gitlab::GroupHierarchy + .new(self.class.where(id: id)) + .base_and_ancestors + end + # Returns all the descendants of the current namespace. def descendants Gitlab::GroupHierarchy @@ -163,6 +171,12 @@ class Namespace < ActiveRecord::Base .base_and_descendants end + def self_and_descendants + Gitlab::GroupHierarchy + .new(self.class.where(id: id)) + .base_and_descendants + end + def user_ids_for_project_authorizations [owner_id] end diff --git a/app/models/project.rb b/app/models/project.rb index 7cdd00bc17b..0de7da0ddaa 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -196,7 +196,6 @@ class Project < ActiveRecord::Base accepts_nested_attributes_for :import_data delegate :name, to: :owner, allow_nil: true, prefix: true - delegate :count, to: :forks, prefix: true delegate :members, to: :team, prefix: true delegate :add_user, :add_users, to: :team delegate :add_guest, :add_reporter, :add_developer, :add_master, to: :team @@ -1396,6 +1395,10 @@ class Project < ActiveRecord::Base # @deprecated cannot remove yet because it has an index with its name in elasticsearch alias_method :path_with_namespace, :full_path + def forks_count + Projects::ForksCountService.new(self).count + end + private def cross_namespace_reference?(from) diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 11ad4838471..54eb75ab9bf 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -128,6 +128,8 @@ module Projects project.repository.before_delete Repository.new(wiki_path, project, disk_path: repo_path).before_delete + + Projects::ForksCountService.new(project).delete_cache end end end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index a2b23ea6171..ad67e68a86a 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -21,11 +21,17 @@ module Projects builds_access_level = @project.project_feature.builds_access_level new_project.project_feature.update_attributes(builds_access_level: builds_access_level) + refresh_forks_count + new_project end private + def refresh_forks_count + Projects::ForksCountService.new(@project).refresh_cache + end + def allowed_visibility_level project_level = @project.visibility_level diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb new file mode 100644 index 00000000000..e2e2b1da91d --- /dev/null +++ b/app/services/projects/forks_count_service.rb @@ -0,0 +1,30 @@ +module Projects + # Service class for getting and caching the number of forks of a project. + class ForksCountService + def initialize(project) + @project = project + end + + def count + Rails.cache.fetch(cache_key) { uncached_count } + end + + def refresh_cache + Rails.cache.write(cache_key, uncached_count) + end + + def delete_cache + Rails.cache.delete(cache_key) + end + + private + + def uncached_count + @project.forks.count + end + + def cache_key + ['projects', @project.id, 'forks_count'] + end + end +end diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb index f385e426827..f30b40423c8 100644 --- a/app/services/projects/unlink_fork_service.rb +++ b/app/services/projects/unlink_fork_service.rb @@ -13,7 +13,13 @@ module Projects ::MergeRequests::CloseService.new(@project, @current_user).execute(mr) end + refresh_forks_count(@project.forked_from_project) + @project.forked_project_link.destroy end + + def refresh_forks_count(project) + Projects::ForksCountService.new(project).refresh_cache + end end end diff --git a/app/views/projects/commit/_ajax_signature.html.haml b/app/views/projects/commit/_ajax_signature.html.haml index 22674b671c9..83821326aec 100644 --- a/app/views/projects/commit/_ajax_signature.html.haml +++ b/app/views/projects/commit/_ajax_signature.html.haml @@ -1,3 +1,2 @@ - if commit.has_signature? %button{ class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'auto top', title: 'GPG signature (loading...)', 'commit-sha' => commit.sha } } - %i.fa.fa-spinner.fa-spin diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml index 756faf4625e..13809da6523 100644 --- a/app/views/projects/issues/_nav_btns.html.haml +++ b/app/views/projects/issues/_nav_btns.html.haml @@ -1,7 +1,7 @@ = link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10 has-tooltip', title: 'Subscribe' do = icon('rss') - if @can_bulk_update - = button_tag "Edit Issues", class: "btn btn-default append-right-10 js-bulk-update-toggle" + = button_tag "Edit issues", class: "btn btn-default append-right-10 js-bulk-update-toggle" = link_to "New issue", new_project_issue_path(@project, issue: { assignee_id: issues_finder.assignee.try(:id), milestone_id: issues_finder.milestones.first.try(:id) }), diff --git a/app/views/projects/merge_requests/_nav_btns.html.haml b/app/views/projects/merge_requests/_nav_btns.html.haml index e92f2712347..e73dab8ad4a 100644 --- a/app/views/projects/merge_requests/_nav_btns.html.haml +++ b/app/views/projects/merge_requests/_nav_btns.html.haml @@ -1,5 +1,5 @@ - if @can_bulk_update - = button_tag "Edit Merge Requests", class: "btn js-bulk-update-toggle" + = button_tag "Edit merge requests", class: "btn append-right-10 js-bulk-update-toggle" - if merge_project = link_to new_merge_request_path, class: "btn btn-new", title: "New merge request" do New merge request diff --git a/app/views/shared/icons/_node_express.svg b/app/views/shared/icons/_express.svg index f2c94319f19..f2c94319f19 100644 --- a/app/views/shared/icons/_node_express.svg +++ b/app/views/shared/icons/_express.svg diff --git a/app/views/shared/icons/_java_spring.svg b/app/views/shared/icons/_spring.svg index 508349aa456..508349aa456 100644 --- a/app/views/shared/icons/_java_spring.svg +++ b/app/views/shared/icons/_spring.svg diff --git a/changelogs/unreleased/34371-pipeline-schedule-vue-files.yml b/changelogs/unreleased/34371-pipeline-schedule-vue-files.yml new file mode 100644 index 00000000000..7de30d82601 --- /dev/null +++ b/changelogs/unreleased/34371-pipeline-schedule-vue-files.yml @@ -0,0 +1,6 @@ +--- +title: Improves performance of vue code by using vue files and moving svg out of data + function in pipeline schedule callout +merge_request: +author: +type: other diff --git a/changelogs/unreleased/34533-speed-up-group-project-authorizations.yml b/changelogs/unreleased/34533-speed-up-group-project-authorizations.yml new file mode 100644 index 00000000000..ddaaf4a2507 --- /dev/null +++ b/changelogs/unreleased/34533-speed-up-group-project-authorizations.yml @@ -0,0 +1,5 @@ +--- +title: Fix timeouts when creating projects in groups with many members +merge_request: 13508 +author: +type: fixed diff --git a/changelogs/unreleased/fix-btn-alignment.yml b/changelogs/unreleased/fix-btn-alignment.yml new file mode 100644 index 00000000000..e5dce3d3a0e --- /dev/null +++ b/changelogs/unreleased/fix-btn-alignment.yml @@ -0,0 +1,5 @@ +--- +title: Fix inconsistent spacing for edit buttons on issues and merge request page +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix-edit-merge-request-button-case.yml b/changelogs/unreleased/fix-edit-merge-request-button-case.yml new file mode 100644 index 00000000000..8550f3e3c1b --- /dev/null +++ b/changelogs/unreleased/fix-edit-merge-request-button-case.yml @@ -0,0 +1,5 @@ +--- +title: Fix edit merge request and issues button inconsistent letter casing +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/forks-count-cache.yml b/changelogs/unreleased/forks-count-cache.yml new file mode 100644 index 00000000000..da8c53c2abd --- /dev/null +++ b/changelogs/unreleased/forks-count-cache.yml @@ -0,0 +1,5 @@ +--- +title: Cache the number of forks of a project +merge_request: 13535 +author: +type: other diff --git a/changelogs/unreleased/issue_31790.yml b/changelogs/unreleased/issue_31790.yml new file mode 100644 index 00000000000..df02cad423a --- /dev/null +++ b/changelogs/unreleased/issue_31790.yml @@ -0,0 +1,4 @@ +--- +title: Fix API responses when dealing with txt files +merge_request: +author: diff --git a/config/webpack.config.js b/config/webpack.config.js index 8e1b80cd39f..6a347c2e660 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -223,6 +223,9 @@ var config = { names: ['main', 'locale', 'common', 'webpack_runtime'], }), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // copy pre-compiled vendor libraries verbatim new CopyWebpackPlugin([ { diff --git a/db/migrate/20170809134534_add_broadcast_message_not_null_constraints.rb b/db/migrate/20170809134534_add_broadcast_message_not_null_constraints.rb index 13e8ef52f22..5551fb51a6e 100644 --- a/db/migrate/20170809134534_add_broadcast_message_not_null_constraints.rb +++ b/db/migrate/20170809134534_add_broadcast_message_not_null_constraints.rb @@ -9,9 +9,21 @@ class AddBroadcastMessageNotNullConstraints < ActiveRecord::Migration COLUMNS = %i[starts_at ends_at created_at updated_at message_html] - def change + class BroadcastMessage < ActiveRecord::Base + self.table_name = 'broadcast_messages' + end + + def up COLUMNS.each do |column| + BroadcastMessage.where(column => nil).delete_all + change_column_null :broadcast_messages, column, false end end + + def down + COLUMNS.each do |column| + change_column_null :broadcast_messages, column, true + end + end end diff --git a/doc/ci/autodeploy/img/auto_monitoring.png b/doc/ci/autodeploy/img/auto_monitoring.png Binary files differnew file mode 100644 index 00000000000..5661b50841b --- /dev/null +++ b/doc/ci/autodeploy/img/auto_monitoring.png diff --git a/doc/ci/autodeploy/index.md b/doc/ci/autodeploy/index.md index 9fa2b2c4969..a714689ebd5 100644 --- a/doc/ci/autodeploy/index.md +++ b/doc/ci/autodeploy/index.md @@ -69,3 +69,28 @@ PostgreSQL provisioning can be disabled by setting the variable `DISABLE_POSTGRE [review-app]: ../review_apps/index.md [container-registry]: https://docs.gitlab.com/ce/user/project/container_registry.html [postgresql]: https://www.postgresql.org/ + +## Auto Monitoring + +> Introduced in [GitLab 9.5](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13438). + +Apps auto-deployed using one the [Kubernetes templates](#supported-templates) can also be automatically monitored for: + +* Response Metrics: latency, throughput, error rate +* System Metrics: CPU utilization, memory utilization + +Metrics are gathered from [nginx-ingress](../../user/project/integrations/prometheus_library/nginx_ingress.md) and [Kubernetes](../../user/project/integrations/prometheus_library/kubernetes.md). + +To view the metrics, open the [Monitoring dashboard for a deployed environment](../environments.md#monitoring-environments). + +![Auto Metrics](img/auto_monitoring.png) + +### Configuring Auto Monitoring + +If GitLab has been deployed using the [omnibus-gitlab](../../install/kubernetes/gitlab_omnibus.md) Helm chart, no configuration is required. + +If you have installed GitLab using a different method: + +1. [Deploy Prometheus](../../user/project/integrations/prometheus.md#configuring-your-own-prometheus-server-within-kubernetes) into your Kubernetes cluster +1. If you would like response metrics, ensure you are running at least version 0.9.0 of NGINX Ingress and [enable Prometheus metrics](https://github.com/kubernetes/ingress/blob/master/examples/customization/custom-vts-metrics/nginx/nginx-vts-metrics-conf.yaml). +1. Finally, [annotate](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) the NGINX Ingress deployment to be scraped by Prometheus using `prometheus.io/scrape: "true"` and `prometheus.io/port: "10254"`. diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md index b7e86ea7c81..bd3a85272d0 100644 --- a/doc/install/kubernetes/gitlab_omnibus.md +++ b/doc/install/kubernetes/gitlab_omnibus.md @@ -126,7 +126,7 @@ Let's Encrypt limits a single TLD to five certificate requests within a single w ## Installing GitLab using the Helm Chart > You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage. -Once you have reviewed the [configuration settings](#configuring-and-installing-gitlab), you can install the chart. We recommending saving your configuration options in a `values.yaml` file for easier upgrades in the future. +Once you have reviewed the [configuration settings](#configuring-and-installing-gitlab) and [added the Helm repository](index.md#add-the-gitlab-helm-repository), you can install the chart. We recommending saving your configuration options in a `values.yaml` file for easier upgrades in the future. For example: ```bash diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png Binary files differindex e69376f74c4..63aa0e99a50 100644 --- a/doc/user/project/integrations/img/jira_service_page.png +++ b/doc/user/project/integrations/img/jira_service_page.png diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md index 4f583879a4e..93aec56f8dc 100644 --- a/doc/user/project/integrations/jira.md +++ b/doc/user/project/integrations/jira.md @@ -10,7 +10,12 @@ JIRA](https://www.programmableweb.com/news/how-and-why-to-integrate-gitlab-jira/ ## Configuration -Each GitLab project can be configured to connect to a different JIRA instance. +Each GitLab project can be configured to connect to a different JIRA instance. That +means one GitLab project maps to _all_ JIRA projects in that JIRA instance once +the configuration is set up. Therefore, you don't have to explicitly associate +one GitLab project to any JIRA project. Once the configuration is set up, any JIRA +projects in the JIRA instance are already mapped to the GitLab project. + If you have one JIRA instance you can pre-fill the settings page with a default template, see the [Services Templates][services-templates] docs. @@ -103,7 +108,6 @@ in the table below. | ----- | ----------- | | `Web URL` | The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., `https://jira.example.com`. | | `JIRA API URL` | The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. | -| `Project key` | Put a JIRA project key (in uppercase), e.g. `MARS` in this field. This is only for testing the configuration settings. JIRA integration in GitLab works with _all_ JIRA projects in your JIRA instance. This field will be removed in a future release. | | `Username` | The user name created in [configuring JIRA step](#configuring-jira). | | `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). | | `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** | diff --git a/lib/api/files.rb b/lib/api/files.rb index 450334fee84..e2ac7142bc4 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -1,5 +1,8 @@ module API class Files < Grape::API + # Prevents returning plain/text responses for files with .txt extension + after_validation { content_type "application/json" } + helpers do def commit_params(attrs) { diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 89dda88d3f5..15c3832b032 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -351,6 +351,8 @@ module API if user_project.forked_from_project.nil? user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id) + + ::Projects::ForksCountService.new(forked_from_project).refresh_cache else render_api_error!("Project already forked", 409) end diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb index eb090453b48..449876c10d9 100644 --- a/lib/api/v3/projects.rb +++ b/lib/api/v3/projects.rb @@ -388,6 +388,8 @@ module API if user_project.forked_from_project.nil? user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id) + + ::Projects::ForksCountService.new(forked_from_project).refresh_cache else render_api_error!("Project already forked", 409) end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index afe4fb58ad0..38772d06dbd 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -811,6 +811,8 @@ module Gitlab return unless commit_object && commit_object.type == :COMMIT gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) + return unless gitmodules + found_module = GitmodulesParser.new(gitmodules.data).parse[path] found_module && found_module['url'] diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb index cf461adf697..732fbf68dad 100644 --- a/lib/gitlab/project_template.rb +++ b/lib/gitlab/project_template.rb @@ -25,7 +25,9 @@ module Gitlab end TEMPLATES_TABLE = [ - ProjectTemplate.new('rails', 'Ruby on Rails') + ProjectTemplate.new('rails', 'Ruby on Rails'), + ProjectTemplate.new('spring', 'Spring'), + ProjectTemplate.new('express', 'NodeJS Express') ].freeze class << self diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 3f25e463412..a362a3a0bc6 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -45,7 +45,6 @@ module Gitlab raise "Unsupported action: #{action}" end if feature_enabled - params[:GitalyAddress] = server[:address] # This field will be deprecated params[:GitalyServer] = server end diff --git a/lib/tasks/gitlab/update_templates.rake b/lib/tasks/gitlab/update_templates.rake index a7e30423c7a..f44abc2b81b 100644 --- a/lib/tasks/gitlab/update_templates.rake +++ b/lib/tasks/gitlab/update_templates.rake @@ -21,13 +21,18 @@ namespace :gitlab do params = { import_url: template.clone_url, namespace_id: admin.namespace.id, - path: template.title, + path: template.name, skip_wiki: true } - puts "Creating project for #{template.name}" + puts "Creating project for #{template.title}" project = Projects::CreateService.new(admin, params).execute + unless project.persisted? + puts project.errors.messages + exit(1) + end + loop do if project.finished? puts "Import finished for #{template.name}" diff --git a/package.json b/package.json index c5247a63e67..cbb9be3a27f 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "vue-loader": "^11.3.4", "vue-resource": "^1.3.4", "vue-template-compiler": "^2.2.6", - "webpack": "^3.4.0", + "webpack": "^3.5.4", "webpack-bundle-analyzer": "^2.8.2", "webpack-stats-plugin": "^0.1.5" }, diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 847e3856ba5..b2229b44f99 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -353,7 +353,7 @@ feature 'Issues > Labels bulk assignment' do context 'cannot bulk assign labels' do it do - expect(page).not_to have_button 'Edit Issues' + expect(page).not_to have_button 'Edit issues' expect(page).not_to have_css '.check-all-issues' expect(page).not_to have_css '.issue-check' end @@ -411,7 +411,7 @@ feature 'Issues > Labels bulk assignment' do def enable_bulk_update visit project_issues_path(project) - click_button 'Edit Issues' + click_button 'Edit issues' end def disable_bulk_update diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index 5a7c4f54cb6..bcc6e9bab0f 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -14,7 +14,7 @@ feature 'Multiple issue updating from issues#index', :js do it 'sets to closed' do visit project_issues_path(project) - click_button 'Edit Issues' + click_button 'Edit issues' find('#check-all-issues').click find('.js-issue-status').click @@ -27,7 +27,7 @@ feature 'Multiple issue updating from issues#index', :js do create_closed visit project_issues_path(project, state: 'closed') - click_button 'Edit Issues' + click_button 'Edit issues' find('#check-all-issues').click find('.js-issue-status').click @@ -41,7 +41,7 @@ feature 'Multiple issue updating from issues#index', :js do it 'updates to current user' do visit project_issues_path(project) - click_button 'Edit Issues' + click_button 'Edit issues' find('#check-all-issues').click click_update_assignee_button @@ -57,7 +57,7 @@ feature 'Multiple issue updating from issues#index', :js do create_assigned visit project_issues_path(project) - click_button 'Edit Issues' + click_button 'Edit issues' find('#check-all-issues').click click_update_assignee_button @@ -73,7 +73,7 @@ feature 'Multiple issue updating from issues#index', :js do it 'updates milestone' do visit project_issues_path(project) - click_button 'Edit Issues' + click_button 'Edit issues' find('#check-all-issues').click find('.issues-bulk-update .js-milestone-select').click @@ -89,7 +89,7 @@ feature 'Multiple issue updating from issues#index', :js do expect(first('.issue')).to have_content milestone.title - click_button 'Edit Issues' + click_button 'Edit issues' find('#check-all-issues').click find('.issues-bulk-update .js-milestone-select').click diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb index cf30a687df8..e6dc284cba7 100644 --- a/spec/features/merge_requests/update_merge_requests_spec.rb +++ b/spec/features/merge_requests/update_merge_requests_spec.rb @@ -98,7 +98,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do end def change_status(text) - click_button 'Edit Merge Requests' + click_button 'Edit merge requests' find('#check-all-issues').click find('.js-issue-status').click find('.dropdown-menu-status a', text: text).click @@ -106,7 +106,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do end def change_assignee(text) - click_button 'Edit Merge Requests' + click_button 'Edit merge requests' find('#check-all-issues').click find('.js-update-assignee').click wait_for_requests @@ -119,7 +119,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do end def change_milestone(text) - click_button 'Edit Merge Requests' + click_button 'Edit merge requests' find('#check-all-issues').click find('.issues-bulk-update .js-milestone-select').click find('.dropdown-menu-milestone a', text: text).click diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 37a5e6b474e..d1efa318d14 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -432,9 +432,7 @@ describe ProjectsHelper do end describe '#any_projects?' do - before do - create(:project) - end + let!(:project) { create(:project) } it 'returns true when projects will be returned' do expect(helper.any_projects?(Project.all)).to eq(true) @@ -444,6 +442,14 @@ describe ProjectsHelper do expect(helper.any_projects?(Project.none)).to eq(false) end + it 'returns true when using a non-empty Array' do + expect(helper.any_projects?([project])).to eq(true) + end + + it 'returns false when using an empty Array' do + expect(helper.any_projects?([])).to eq(false) + end + it 'only executes a single query when a LIMIT is applied' do relation = Project.limit(1) recorder = ActiveRecord::QueryRecorder.new do diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js new file mode 100644 index 00000000000..7a826487bf9 --- /dev/null +++ b/spec/javascripts/gpg_badges_spec.js @@ -0,0 +1,48 @@ +import GpgBadges from '~/gpg_badges'; + +describe('GpgBadges', () => { + const dummyCommitSha = 'n0m0rec0ffee'; + const dummyBadgeHtml = 'dummy html'; + const dummyResponse = { + signatures: [{ + commit_sha: dummyCommitSha, + html: dummyBadgeHtml, + }], + }; + + beforeEach(() => { + setFixtures(` + <div class="parent-container"> + <div class="js-loading-gpg-badge" data-commit-sha="${dummyCommitSha}"></div> + </div> + `); + }); + + it('displays a loading spinner', () => { + spyOn($, 'get').and.returnValue({ + done() { + // intentionally left blank + }, + }); + + GpgBadges.fetch(); + + expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); + const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); + expect(spinners.length).toBe(1); + }); + + it('replaces the loading spinner', () => { + spyOn($, 'get').and.returnValue({ + done(callback) { + callback(dummyResponse); + }, + }); + + GpgBadges.fetch(); + + expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); + const parentContainer = document.querySelector('.parent-container'); + expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); + }); +}); diff --git a/spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js b/spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js index 6120d224ac0..ed481cb60a1 100644 --- a/spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js +++ b/spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import Cookies from 'js-cookie'; -import PipelineSchedulesCallout from '~/pipeline_schedules/components/pipeline_schedules_callout'; +import PipelineSchedulesCallout from '~/pipeline_schedules/components/pipeline_schedules_callout.vue'; const PipelineSchedulesCalloutComponent = Vue.extend(PipelineSchedulesCallout); const cookieKey = 'pipeline_schedules_callout_dismissed'; diff --git a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js index 90eac1ed1ab..2e16adffb5b 100644 --- a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js +++ b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js @@ -41,7 +41,7 @@ describe('Confidential Issue Sidebar Block', () => { ).toBe(true); expect( - vm2.$el.innerHTML.includes('None'), + vm2.$el.innerHTML.includes('This issue is not confidential'), ).toBe(true); }); diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index c531d4b055f..ac33cd8a2c9 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -310,8 +310,8 @@ describe Gitlab::Git::Commit, seed_helper: true do commits.map(&:id) end - it 'has 33 elements' do - expect(subject.size).to eq(33) + it 'has 34 elements' do + expect(subject.size).to eq(34) end it 'includes the expected commits' do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 8d24d6f55b8..4ef5d9070a2 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -289,7 +289,13 @@ describe Gitlab::Git::Repository, seed_helper: true do it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') } end - context 'no submodules at commit' do + context 'no .gitmodules at commit' do + let(:ref) { '9596bc54a6f0c0c98248fe97077eb5ccf48a98d0' } + + it { expect(submodule_url('six')).to eq(nil) } + end + + context 'no gitlink entry' do let(:ref) { '6d39438' } it { expect(submodule_url('six')).to eq(nil) } @@ -986,7 +992,7 @@ describe Gitlab::Git::Repository, seed_helper: true do describe '#branch_count' do it 'returns the number of branches' do - expect(repository.branch_count).to eq(9) + expect(repository.branch_count).to eq(10) end end diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb index 12e75cdd5d0..d19bd611919 100644 --- a/spec/lib/gitlab/project_template_spec.rb +++ b/spec/lib/gitlab/project_template_spec.rb @@ -4,7 +4,9 @@ describe Gitlab::ProjectTemplate do describe '.all' do it 'returns a all templates' do expected = [ - described_class.new('rails', 'Ruby on Rails') + described_class.new('rails', 'Ruby on Rails'), + described_class.new('spring', 'Spring'), + described_class.new('express', 'NodeJS Express') ] expect(described_class.all).to be_an(Array) diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index e78892d4232..b66afafa174 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -202,7 +202,6 @@ describe Gitlab::Workhorse do context 'when Gitaly is enabled' do let(:gitaly_params) do { - GitalyAddress: Gitlab::GitalyClient.address('default'), GitalyServer: { address: Gitlab::GitalyClient.address('default'), token: Gitlab::GitalyClient.token('default') diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 1a00c50690c..69286eff984 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -315,6 +315,20 @@ describe Namespace do end end + describe '#self_and_ancestors', :nested_groups do + let(:group) { create(:group) } + let(:nested_group) { create(:group, parent: group) } + let(:deep_nested_group) { create(:group, parent: nested_group) } + let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) } + + it 'returns the correct ancestors' do + expect(very_deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group) + expect(deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group) + expect(nested_group.self_and_ancestors).to contain_exactly(group, nested_group) + expect(group.self_and_ancestors).to contain_exactly(group) + end + end + describe '#descendants', :nested_groups do let!(:group) { create(:group, path: 'git_lab') } let!(:nested_group) { create(:group, parent: group) } @@ -331,6 +345,22 @@ describe Namespace do end end + describe '#self_and_descendants', :nested_groups do + let!(:group) { create(:group, path: 'git_lab') } + let!(:nested_group) { create(:group, parent: group) } + let!(:deep_nested_group) { create(:group, parent: nested_group) } + let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) } + let!(:another_group) { create(:group, path: 'gitllab') } + let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) } + + it 'returns the correct descendants' do + expect(very_deep_nested_group.self_and_descendants).to contain_exactly(very_deep_nested_group) + expect(deep_nested_group.self_and_descendants).to contain_exactly(deep_nested_group, very_deep_nested_group) + expect(nested_group.self_and_descendants).to contain_exactly(nested_group, deep_nested_group, very_deep_nested_group) + expect(group.self_and_descendants).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group) + end + end + describe '#users_with_descendants', :nested_groups do let(:user_a) { create(:user) } let(:user_b) { create(:user) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d9ab44dc49f..eba71ba2f72 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2310,4 +2310,14 @@ describe Project do end end end + + describe '#forks_count' do + it 'returns the number of forks' do + project = build(:project) + + allow(project.forks).to receive(:count).and_return(1) + + expect(project.forks_count).to eq(1) + end + end end diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 55c998b13b8..ea97c556430 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -33,6 +33,15 @@ describe API::Files do expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end + it 'returns json when file has txt extension' do + file_path = "bar%2Fbranch-test.txt" + + get api(route(file_path), current_user), params + + expect(response).to have_http_status(200) + expect(response.content_type).to eq('application/json') + end + it 'returns file by commit sha' do # This file is deleted on HEAD file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee" @@ -220,6 +229,7 @@ describe API::Files do post api(route("new_file_with_author%2Etxt"), user), valid_params expect(response).to have_http_status(201) + expect(response.content_type).to eq('application/json') last_commit = project.repository.commit.raw expect(last_commit.author_email).to eq(author_email) expect(last_commit.author_name).to eq(author_name) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 6cb27d16fe5..a89a58ff713 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1065,6 +1065,14 @@ describe API::Projects do expect(project_fork_target.forked?).to be_truthy end + it 'refreshes the forks count cachce' do + expect(project_fork_source.forks_count).to be_zero + + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) + + expect(project_fork_source.forks_count).to eq(1) + end + it 'fails if forked_from project which does not exist' do post api("/projects/#{project_fork_target.id}/fork/9999", admin) expect(response).to have_http_status(404) diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index fca5b5b5d82..a514166274a 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -1004,6 +1004,14 @@ describe API::V3::Projects do expect(project_fork_target.forked?).to be_truthy end + it 'refreshes the forks count cachce' do + expect(project_fork_source.forks_count).to be_zero + + post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) + + expect(project_fork_source.forks_count).to eq(1) + end + it 'fails if forked_from project which does not exist' do post v3_api("/projects/#{project_fork_target.id}/fork/9999", admin) expect(response).to have_http_status(404) diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index c90536ba346..21c4b30734c 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -50,6 +50,14 @@ describe Projects::ForkService do expect(@from_project.avatar.file).to be_exists end + + it 'flushes the forks count cache of the source project' do + expect(@from_project.forks_count).to be_zero + + fork_project(@from_project, @to_user) + + expect(@from_project.forks_count).to eq(1) + end end end diff --git a/spec/services/projects/forks_count_service_spec.rb b/spec/services/projects/forks_count_service_spec.rb new file mode 100644 index 00000000000..cf299c5d09b --- /dev/null +++ b/spec/services/projects/forks_count_service_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Projects::ForksCountService do + let(:project) { build(:project, id: 42) } + let(:service) { described_class.new(project) } + + describe '#count' do + it 'returns the number of forks' do + allow(service).to receive(:uncached_count).and_return(1) + + expect(service.count).to eq(1) + end + + it 'caches the forks count', :use_clean_rails_memory_store_caching do + expect(service).to receive(:uncached_count).once.and_return(1) + + 2.times { service.count } + end + end + + describe '#refresh_cache', :use_clean_rails_memory_store_caching do + it 'refreshes the cache' do + expect(service).to receive(:uncached_count).once.and_return(1) + + service.refresh_cache + + expect(service.count).to eq(1) + end + end + + describe '#delete_cache', :use_clean_rails_memory_store_caching do + it 'removes the cache' do + expect(service).to receive(:uncached_count).twice.and_return(1) + + service.count + service.delete_cache + service.count + end + end +end diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb index 2ae8d5f7c54..4f1ab697460 100644 --- a/spec/services/projects/unlink_fork_service_spec.rb +++ b/spec/services/projects/unlink_fork_service_spec.rb @@ -29,4 +29,14 @@ describe Projects::UnlinkForkService do subject.execute end + + it 'refreshes the forks count cache of the source project' do + source = fork_project.forked_from_project + + expect(source.forks_count).to eq(1) + + subject.execute + + expect(source.forks_count).to be_zero + end end diff --git a/spec/support/gitlab-git-test.git/objects/3e/20715310a699808282e772720b9c04a0696bcc b/spec/support/gitlab-git-test.git/objects/3e/20715310a699808282e772720b9c04a0696bcc Binary files differnew file mode 100644 index 00000000000..86bf37ac887 --- /dev/null +++ b/spec/support/gitlab-git-test.git/objects/3e/20715310a699808282e772720b9c04a0696bcc diff --git a/spec/support/gitlab-git-test.git/objects/95/96bc54a6f0c0c98248fe97077eb5ccf48a98d0 b/spec/support/gitlab-git-test.git/objects/95/96bc54a6f0c0c98248fe97077eb5ccf48a98d0 new file mode 100644 index 00000000000..d90cb028e9b --- /dev/null +++ b/spec/support/gitlab-git-test.git/objects/95/96bc54a6f0c0c98248fe97077eb5ccf48a98d0 @@ -0,0 +1,2 @@ +xOn1䜯 9&O "noYD6ՒҪ?j;wQ GrN(HPrArR7tpM#McNrsI +%p>۫pz?Y3XBB̰GB4
p?kv۞y~W])[a<CP_
\ No newline at end of file diff --git a/spec/support/gitlab-git-test.git/packed-refs b/spec/support/gitlab-git-test.git/packed-refs index ce5ab1f705b..507e4ce785a 100644 --- a/spec/support/gitlab-git-test.git/packed-refs +++ b/spec/support/gitlab-git-test.git/packed-refs @@ -8,6 +8,7 @@ 46e1395e609395de004cacd4b142865ab0e52a29 refs/heads/gitattributes-updated 4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6 refs/heads/master 5937ac0a7beb003549fc5fd26fc247adbce4a52e refs/heads/merge-test +9596bc54a6f0c0c98248fe97077eb5ccf48a98d0 refs/heads/missing-gitmodules f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8 refs/tags/v1.0.0 ^6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9 8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b refs/tags/v1.1.0 diff --git a/spec/support/gpg_helpers.rb b/spec/support/gpg_helpers.rb index 96ea6f28b30..65b38626a51 100644 --- a/spec/support/gpg_helpers.rb +++ b/spec/support/gpg_helpers.rb @@ -1,4 +1,6 @@ module GpgHelpers + SIGNED_COMMIT_SHA = '8a852d50dda17cc8fd1408d2fd0c5b0f24c76ca4'.freeze + module User1 extend self diff --git a/spec/support/seed_repo.rb b/spec/support/seed_repo.rb index cfe7fc980a8..b4868e82cd7 100644 --- a/spec/support/seed_repo.rb +++ b/spec/support/seed_repo.rb @@ -97,6 +97,7 @@ module SeedRepo gitattributes-updated master merge-test + missing-gitmodules ].freeze TAGS = %w[ v1.0.0 diff --git a/spec/views/projects/commits/_commit.html.haml_spec.rb b/spec/views/projects/commits/_commit.html.haml_spec.rb new file mode 100644 index 00000000000..4c247361bd7 --- /dev/null +++ b/spec/views/projects/commits/_commit.html.haml_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe 'projects/commits/_commit.html.haml' do + context 'with a singed commit' do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:ref) { GpgHelpers::SIGNED_COMMIT_SHA } + let(:commit) { repository.commit(ref) } + + it 'does not display a loading spinner for GPG status' do + render partial: 'projects/commits/commit', locals: { + project: project, + ref: ref, + commit: commit + } + + within '.gpg-status-box' do + expect(page).not_to have_css('i.fa.fa-spinner.fa-spin') + end + end + end +end diff --git a/vendor/project_templates/express.tar.gz b/vendor/project_templates/express.tar.gz Binary files differnew file mode 100644 index 00000000000..6353f6605d5 --- /dev/null +++ b/vendor/project_templates/express.tar.gz diff --git a/vendor/project_templates/spring.tar.gz b/vendor/project_templates/spring.tar.gz Binary files differnew file mode 100644 index 00000000000..d7c0ab74d01 --- /dev/null +++ b/vendor/project_templates/spring.tar.gz diff --git a/yarn.lock b/yarn.lock index c9e1b630a9e..5fc28f8b5ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6275,9 +6275,9 @@ webpack-stats-plugin@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.1.5.tgz#29e5f12ebfd53158d31d656a113ac1f7b86179d9" -webpack@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.4.0.tgz#e9465b660ad79dd2d33874d968b31746ea9a8e63" +webpack@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.5.4.tgz#5583eb263ed27b78b5bd17bfdfb0eb1b1cd1bf81" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" |