提交 b4e0be60 编写于 作者: M Marcia Ramos

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into...

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 33329-tech-article-deploying-maven-artifacts
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);
});
......
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>
`,
};
<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>
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',
......
......@@ -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>
......
......@@ -403,6 +403,7 @@ header.navbar-gitlab-new {
}
.breadcrumbs-extra {
display: flex;
flex: 0 0 auto;
margin-left: auto;
}
......
......@@ -286,6 +286,10 @@
.gpg-status-box {
&:empty {
display: none;
}
&.valid {
@include green-status-color;
}
......
......@@ -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
......
......@@ -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,
......
......@@ -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)
......
......@@ -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) }
......
......@@ -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
......
......@@ -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)
......
......@@ -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
......@@ -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
......
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
......@@ -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
- 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
= 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) }),
......
- 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
---
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
---
title: Fix timeouts when creating projects in groups with many members
merge_request: 13508
author:
type: fixed
---
title: Fix inconsistent spacing for edit buttons on issues and merge request page
merge_request:
author:
type: fixed
---
title: Fix edit merge request and issues button inconsistent letter casing
merge_request:
author:
type: fixed
---
title: Cache the number of forks of a project
merge_request: 13535
author:
type: other
---
title: Fix API responses when dealing with txt files
merge_request:
author:
......@@ -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([
{
......
......@@ -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
......@@ -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"`.
......@@ -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
......
......@@ -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.** |
......
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)
{
......
......@@ -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
......
......@@ -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
......
......@@ -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']
......
......@@ -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
......
......@@ -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
......
......@@ -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}"
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
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);
});
});
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';
......
......@@ -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);
});
......
......@@ -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
......
......@@ -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
......
......@@ -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)
......
......@@ -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')
......
......@@ -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) }
......
......@@ -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
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -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
......
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
......@@ -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
xOn1 䜯 9&O "noYD6ՒҪ?j;wQ GrN(HPrArR7tpM#M”cNrsI
%p>۫pz?Y3XBB̰GB4 p?kv۞y~W])[a<CP_
\ No newline at end of file
......@@ -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
......
module GpgHelpers
SIGNED_COMMIT_SHA = '8a852d50dda17cc8fd1408d2fd0c5b0f24c76ca4'.freeze
module User1
extend self
......
......@@ -97,6 +97,7 @@ module SeedRepo
gitattributes-updated
master
merge-test
missing-gitmodules
].freeze
TAGS = %w[
v1.0.0
......
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
......@@ -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"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册