Commit 9586a57e authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'ce-com/master' into ce-to-ee

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents 9567a1ee 4b3011e1
......@@ -72,16 +72,18 @@ stages:
- docker.elastic.co/elasticsearch/elasticsearch:5.3.2
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
.only-if-want-mysql: &only-if-want-mysql
only:
- /mysql/
- /-stable/
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
- tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee
- tags@gitlab/gitlab-ee
# Skip all jobs except the ones that begin with 'docs/'.
# Used for commits including ONLY documentation changes.
......@@ -120,7 +122,7 @@ stages:
.rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
<<: *only-if-want-mysql
<<: *except-docs
.spinach-knapsack: &spinach-knapsack
......@@ -152,7 +154,7 @@ stages:
.spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
<<: *only-if-want-mysql
<<: *except-docs
.only-canonical-masters: &only-canonical-masters
......
......@@ -406,7 +406,7 @@ gem 'sys-filesystem', '~> 1.1.6'
gem 'net-ntp'
# Gitaly GRPC client
gem 'gitaly', '~> 0.23.0'
gem 'gitaly', '~> 0.24.0'
gem 'toml-rb', '~> 0.3.15', require: false
......
......@@ -293,7 +293,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly (0.23.0)
gitaly (0.24.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
......@@ -1008,7 +1008,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.23.0)
gitaly (~> 0.24.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0)
......
......@@ -383,7 +383,10 @@ import initGroupAnalytics from './init_group_analytics';
setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
<<<<<<< HEAD
new UsersSelect();
=======
>>>>>>> ce-com/master
break;
case 'projects:imports:show':
new ProjectImport();
......
<script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
<<<<<<< HEAD
import linkedPipelinesColumn from './linked_pipelines_column.vue';
=======
import '~/flash';
>>>>>>> ce-com/master
import stageColumnComponent from './stage_column_component.vue';
export default {
......
......@@ -7,6 +7,14 @@ const LOADING_HTML = `
</div>
`;
function getSystemDate(systemUtcOffsetSeconds) {
const date = new Date();
const localUtcOffsetMinutes = 0 - date.getTimezoneOffset();
const systemUtcOffsetMinutes = systemUtcOffsetSeconds / 60;
date.setMinutes((date.getMinutes() - localUtcOffsetMinutes) + systemUtcOffsetMinutes);
return date;
}
function formatTooltipText({ date, count }) {
const dateObject = new Date(date);
const dateDayName = gl.utils.getDayName(dateObject);
......@@ -22,7 +30,7 @@ function formatTooltipText({ date, count }) {
const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
export default class ActivityCalendar {
constructor(container, timestamps, calendarActivitiesPath) {
constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) {
this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = '';
......@@ -37,7 +45,7 @@ export default class ActivityCalendar {
this.timestampsTmp = [];
let group = 0;
const today = new Date();
const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0);
const oneYearAgo = new Date(today);
......
......@@ -150,15 +150,21 @@ export default class UserTabs {
const $calendarWrap = this.$parentEl.find('.user-calendar');
const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
let utcFormatted = 'UTC';
if (utcOffset !== 0) {
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`;
}
$.ajax({
dataType: 'json',
url: calendarPath,
success: (activityData) => {
$calendarWrap.html(CALENDAR_TEMPLATE);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
// eslint-disable-next-line no-new
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath);
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath, utcOffset);
},
});
......
......@@ -45,6 +45,7 @@
margin-top: -23px;
float: right;
font-size: 12px;
direction: ltr;
}
.pika-single.gitlab-theme {
......
......@@ -329,9 +329,9 @@ header {
li {
.badge {
position: inherit;
top: -3px;
top: -8px;
font-weight: normal;
margin-left: -12px;
margin-left: -11px;
font-size: 11px;
color: $white-light;
padding: 1px 5px 2px;
......
......@@ -328,9 +328,17 @@
margin-bottom: 10px;
color: $issuable-sidebar-color;
svg {
fill: $issuable-sidebar-color;
}
&:hover,
&:hover .todo-undone {
color: $gl-text-color;
svg {
fill: $gl-text-color;
}
}
span {
......
......@@ -620,17 +620,26 @@ class Repository
end
def last_commit_for_path(sha, path)
sha = last_commit_id_for_path(sha, path)
commit(sha)
raw_repository.gitaly_migrate(:last_commit_for_path) do |is_enabled|
if is_enabled
last_commit_for_path_by_gitaly(sha, path)
else
last_commit_for_path_by_rugged(sha, path)
end
end
end
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/383
def last_commit_id_for_path(sha, path)
key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}"
cache.fetch(key) do
args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path})
Gitlab::Popen.popen(args, path_to_repo).first.strip
raw_repository.gitaly_migrate(:last_commit_for_path) do |is_enabled|
if is_enabled
last_commit_for_path_by_gitaly(sha, path).id
else
last_commit_id_for_path_by_shelling_out(sha, path)
end
end
end
end
......@@ -1218,6 +1227,21 @@ class Repository
Rugged::Commit.create(rugged, params)
end
def last_commit_for_path_by_gitaly(sha, path)
c = raw_repository.gitaly_commit_client.last_commit_for_path(sha, path)
commit(c)
end
def last_commit_for_path_by_rugged(sha, path)
sha = last_commit_id_for_path_by_shelling_out(sha, path)
commit(sha)
end
def last_commit_id_for_path_by_shelling_out(sha, path)
args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path})
Gitlab::Popen.popen(args, path_to_repo).first.strip
end
def repository_storage_path
@project.repository_storage_path
end
......
......@@ -12,7 +12,6 @@ module MergeRequests
merge_request.source_project = source_project
merge_request.source_branch = params[:source_branch]
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
merge_request.head_pipeline = head_pipeline_for(merge_request)
create(merge_request)
end
......@@ -22,10 +21,16 @@ module MergeRequests
notification_service.new_merge_request(issuable, current_user)
todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user)
update_merge_requests_head_pipeline(issuable)
end
private
def update_merge_requests_head_pipeline(merge_request)
pipeline = head_pipeline_for(merge_request)
merge_request.update(head_pipeline_id: pipeline.id) if pipeline
end
def head_pipeline_for(merge_request)
return unless merge_request.source_project
......
- page_title "CI Lint"
- page_description "Validate your GitLab CI configuration file"
- content_for :page_specific_javascripts do
- content_for :library_javascripts do
= page_specific_javascript_tag('lib/ace.js')
%h2 Check your .gitlab-ci.yml
......
......@@ -38,6 +38,9 @@
= Gon::Base.render_data
- if content_for?(:library_javascripts)
= yield :library_javascripts
= webpack_bundle_tag "webpack_runtime"
= webpack_bundle_tag "common"
= webpack_bundle_tag "locale"
......
......@@ -44,7 +44,7 @@
= icon('tachometer fw')
%li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw')
= custom_icon('issues')
- issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count)
......
......@@ -38,7 +38,7 @@
= icon('tachometer fw')
%li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw')
= custom_icon('issues')
- issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count)
......
......@@ -30,7 +30,11 @@
%span
Contribution Analytics
- if current_user && can?(current_user, :admin_group, @group)
<<<<<<< HEAD
= nav_link(path: %w[groups#projects groups#edit ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do
=======
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
>>>>>>> ce-com/master
= link_to edit_group_path(@group), title: 'Settings' do
%span
Settings
......@@ -121,7 +121,7 @@
= nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do
.nav-icon-container
= custom_icon('mr_bold')
= custom_icon('spam_logs')
%span.nav-item-name
Spam Logs
......
......@@ -94,5 +94,8 @@
= link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
%span
CI / CD
<<<<<<< HEAD
= render "groups/ee/settings_nav"
=======
>>>>>>> ce-com/master
......@@ -76,7 +76,7 @@
= nav_link(controller: %w[projects/registry/repositories]) do
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
.nav-icon-container
= custom_icon('mr_bold')
= custom_icon('container_registry')
%span.nav-item-name
Registry
......
- form = local_assigns.fetch(:form)
<<<<<<< HEAD
= render 'projects/ee/merge_request_settings', form: form, project: @project
=======
>>>>>>> ce-com/master
= render 'projects/merge_request_merge_settings', form: form
- page_title "General"
- @content_class = "limit-container-width" unless fluid_layout
- expanded = Rails.env.test?
<<<<<<< HEAD
- content_for :page_specific_javascripts do
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('service_desk')
=======
>>>>>>> ce-com/master
= render "projects/settings/head"
......@@ -49,6 +52,10 @@
= f.label :tag_list, "Tags", class: 'label-light'
= f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control"
%p.help-block Separate tags with commas.
<<<<<<< HEAD
=======
%fieldset.features
>>>>>>> ce-com/master
%h5.prepend-top-0
Project avatar
.form-group
......@@ -116,6 +123,7 @@
%span.help-block Share code pastes with others out of Git repository
.col-md-4
= project_feature_access_select(:snippets_access_level)
<<<<<<< HEAD
.row
.col-md-8.project-feature
......@@ -183,6 +191,59 @@
enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_address if @project.service_desk_enabled) } }
=======
.row
.col-md-8.project-feature
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
%span.help-block Lightweight issue tracking system for this project
.col-md-4
= project_feature_access_select(:issues_access_level)
.row
.col-md-8.project-feature
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
%span.help-block Pages for project documentation
.col-md-4
= project_feature_access_select(:wiki_access_level)
.form-group
= render 'shared/allow_request_access', form: f
- if Gitlab.config.lfs.enabled && current_user.admin?
.row.js-lfs-enabled.form-group.sharing-and-permissions
.col-md-8
= f.label :lfs_enabled, 'Git Large File Storage', class: 'label-light'
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
%span.help-block Manages large files such as audio, video and graphics files.
.col-md-4
.select-wrapper
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
= icon('chevron-down')
- if Gitlab.config.registry.enabled
.form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this project
= link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
= f.submit 'Save changes', class: "btn btn-save"
%section.settings.merge-requests-feature{ style: ("display: none;" if @project.project_feature.send(:merge_requests_access_level) == 0) }
.settings-header
%h4
Merge request settings
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Customize your merge request restrictions.
.settings-content.no-animate{ class: ('expanded' if expanded) }
= 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"
>>>>>>> ce-com/master
%section.settings
.settings-header
%h4
......
......@@ -15,7 +15,7 @@
- else
= s_("PipelineSchedules|None")
%td.next-run-cell
- if pipeline_schedule.active?
- if pipeline_schedule.active? && pipeline_schedule.next_run_at
= time_ago_with_tooltip(pipeline_schedule.real_next_run)
- else
= s_("PipelineSchedules|Inactive")
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m16 11.764v-8.764c0-1.657-1.343-3-3-3h-10c-1.657 0-3 1.343-3 3v8.764c.531-.475 1.232-.764 2-.764v-8c0-.552.448-1 1-1h10c.552 0 1 .448 1 1v8c.768 0 1.469.289 2 .764m-14 .236h12c1.105 0 2 .895 2 2 0 1.105-.895 2-2 2h-12c-1.105 0-2-.895-2-2 0-1.105.895-2 2-2m10 1c-.552 0-1 .448-1 1 0 .552.448 1 1 1 .552 0 1-.448 1-1 0-.552-.448-1-1-1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
......@@ -67,7 +67,7 @@
.block.issues
.sidebar-collapsed-icon
%strong
= icon('hashtag', 'aria-hidden': 'true')
= custom_icon('issues')
%span= milestone.issues_visible_to_user(current_user).count
.title.hide-collapsed
Issues
......
%h4.prepend-top-20
Contributions for
%strong= @calendar_date.to_s(:short)
%strong= @calendar_date.to_s(:medium)
- if @events.any?
%ul.bordered-list
......@@ -8,7 +8,7 @@
%li
%span.light
%i.fa.fa-clock-o
= event.created_at.to_s(:time)
= event.created_at.strftime('%-I:%M%P')
- if event.push?
#{event.action_name} #{event.ref_type}
%strong
......@@ -30,4 +30,4 @@
= event.project_name
- else
%p
No contributions found for #{@calendar_date.to_s(:short)}
No contributions found for #{@calendar_date.to_s(:medium)}
......@@ -104,7 +104,7 @@
.tab-content
#activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs
.user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path } }
.user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light
%i.fa.fa-spinner.fa-spin
.user-calendar-activities
......
---
title: Fix timezone inconsistencies in user contribution graph
merge_request: 13208
author:
---
title: Fix an order of operations for CI connection error message in merge request
widget
merge_request: 13252
author:
---
title: Fix pipeline_schedules pages when active schedule has an abnormal state
merge_request: 13286
author:
......@@ -80,7 +80,7 @@ Install, upgrade, integrate, migrate to GitLab:
| :------------ | :------: | --------------: |
| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017/01/23 |
| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016/07/13 |
| [Get started with OpenShift Origin 3 and GitLab](https://about.gitlab.com/2016/06/28/get-started-with-openshift-origin-3-and-gitlab/) | Tutorial | 2016/06/28 |
| [Get started with OpenShift Origin 3 and GitLab](openshift_and_gitlab/index.md) | Tutorial | 2016/06/28 |
| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016/04/27 |
## Software development
......
This diff is collapsed.
......@@ -168,8 +168,10 @@ are out of date, so we'll need to install through the following commands:
curl --location https://deb.nodesource.com/setup_7.x | sudo bash -
sudo apt-get install -y nodejs
# install yarn
curl --location https://yarnpkg.com/install.sh | bash -
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
Visit the official websites for [node](https://nodejs.org/en/download/package-manager/) and [yarn](https://yarnpkg.com/en/docs/install/) if you have any trouble with these steps.
......
......@@ -65,7 +65,10 @@ Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
```bash
curl --location https://yarnpkg.com/install.sh | bash -
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
......
......@@ -65,7 +65,10 @@ Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
```bash
curl --location https://yarnpkg.com/install.sh | bash -
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
......
......@@ -65,7 +65,10 @@ Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
```bash
curl --location https://yarnpkg.com/install.sh | bash -
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
......
......@@ -65,7 +65,10 @@ Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
```bash
curl --location https://yarnpkg.com/install.sh | bash -
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
......
......@@ -65,7 +65,10 @@ Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
```bash
curl --location https://yarnpkg.com/install.sh | bash -
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
......
......@@ -48,7 +48,7 @@ module Gitlab
end
def starting_month
Date.today.month
Date.current.month
end
private
......@@ -66,12 +66,18 @@ module Gitlab
.select(:id)
conditions = t[:created_at].gteq(date_from.beginning_of_day)
.and(t[:created_at].lteq(Date.today.end_of_day))
.and(t[:created_at].lteq(Date.current.end_of_day))
.and(t[:author_id].eq(contributor.id))
date_interval = if Gitlab::Database.postgresql?
"INTERVAL '#{Time.zone.now.utc_offset} seconds'"
else
"INTERVAL #{Time.zone.now.utc_offset} SECOND"
end
Event.reorder(nil)
.select(t[:project_id], t[:target_type], t[:action], 'date(created_at) AS date', 'count(id) as total_amount')
.group(t[:project_id], t[:target_type], t[:action], 'date(created_at)')
.select(t[:project_id], t[:target_type], t[:action], "date(created_at + #{date_interval}) AS date", 'count(id) as total_amount')
.group(t[:project_id], t[:target_type], t[:action], "date(created_at + #{date_interval})")
.where(conditions)
.having(t[:project_id].in(Arel::Nodes::SqlLiteral.new(authed_projects.to_sql)))
end
......
# Gitaly note: JV: needs 1 RPC for #load_blame.
module Gitlab
module Git
class Blame
......@@ -26,15 +24,29 @@ module Gitlab
private
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/376
def load_blame
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{@repo.path} blame -p #{@sha} -- #{@path})
# Read in binary mode to ensure ASCII-8BIT
raw_output = IO.popen(cmd, 'rb') {|io| io.read }
raw_output = @repo.gitaly_migrate(:blame) do |is_enabled|
if is_enabled
load_blame_by_gitaly
else
load_blame_by_shelling_out
end
end
output = encode_utf8(raw_output)
process_raw_blame output
end
def load_blame_by_gitaly
@repo.gitaly_commit_client.raw_blame(@sha, @path)
end
def load_blame_by_shelling_out
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{@repo.path} blame -p #{@sha} -- #{@path})
# Read in binary mode to ensure ASCII-8BIT
IO.popen(cmd, 'rb') {|io| io.read }
end
def process_raw_blame(output)
lines, final = [], []
info, commits = {}, {}
......
......@@ -683,6 +683,14 @@ module Gitlab
@gitaly_repository_client ||= Gitlab::GitalyClient::RepositoryService.new(self)
end
def gitaly_migrate(method, &block)
Gitlab::GitalyClient.migrate(method, &block)
rescue GRPC::NotFound => e
raise NoRepository.new(e)
rescue GRPC::BadStatus => e
raise CommandError.new(e)
end
private
# Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'.
......@@ -998,6 +1006,11 @@ module Gitlab
end.sort_by(&:name)
end
def last_commit_for_path_by_rugged(sha, path)
sha = last_commit_id_for_path(sha, path)
commit(sha)
end
def tags_from_gitaly
gitaly_ref_client.tags
end
......@@ -1017,14 +1030,6 @@ module Gitlab
raw_output.to_i
end
def gitaly_migrate(method, &block)
Gitlab::GitalyClient.migrate(method, &block)
rescue GRPC::NotFound => e
raise NoRepository.new(e)
rescue GRPC::BadStatus => e
raise CommandError.new(e)
end
end
end
end
......@@ -97,6 +97,20 @@ module Gitlab
GitalyClient.call(@repository.storage, :commit_service, :count_commits, request).count
end
def last_commit_for_path(revision, path)
request = Gitaly::LastCommitForPathRequest.new(
repository: @gitaly_repo,
revision: revision.force_encoding(Encoding::ASCII_8BIT),
path: path.to_s.force_encoding(Encoding::ASCII_8BIT)
)
gitaly_commit = GitalyClient.call(@repository.storage, :commit_service, :last_commit_for_path, request).commit
return unless gitaly_commit
commit = GitalyClient::Commit.new(@repository, gitaly_commit)
Gitlab::Git::Commit.new(commit)
end
def between(from, to)
request = Gitaly::CommitsBetweenRequest.new(
repository: @gitaly_repo,
......@@ -128,6 +142,17 @@ module Gitlab
response.languages.map { |l| { value: l.share.round(2), label: l.name, color: l.color, highlight: l.color } }
end
def raw_blame(revision, path)
request = Gitaly::RawBlameRequest.new(
repository: @gitaly_repo,
revision: revision,
path: path
)
response = GitalyClient.call(@repository.storage, :commit_service, :raw_blame, request)
response.reduce("") { |memo, msg| memo << msg.data }
end
private
def commit_diff_request_params(commit, options = {})
......
require 'spec_helper'
feature 'Pipelines for Merge Requests', js: true do
given(:user) { create(:user) }
given(:merge_request) { create(:merge_request) }
given(:project) { merge_request.target_project }
describe 'pipeline tab' do
given(:user) { create(:user) }
given(:merge_request) { create(:merge_request) }
given(:project) { merge_request.target_project }
before do
project.team << [user, :master]
sign_in user
end
context 'with pipelines' do
let!(:pipeline) do
create(:ci_empty_pipeline,
project: merge_request.source_project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha)
before do
project.team << [user, :master]
sign_in user
end
before do
visit project_merge_request_path(project, merge_request)
context 'with pipelines' do
let!(:pipeline) do
create(:ci_empty_pipeline,
project: merge_request.source_project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha)
end
before do
visit project_merge_request_path(project, merge_request)
end
scenario 'user visits merge request pipelines tab' do
page.within('.merge-request-tabs') do
click_link('Pipelines')
end
wait_for_requests
expect(page).to have_selector('.stage-cell')
end
end
scenario 'user visits merge request pipelines tab' do
page.within('.merge-request-tabs') do
click_link('Pipelines')
context 'without pipelines' do
before do
visit project_merge_request_path(project, merge_request)
end
wait_for_requests
expect(page).to have_selector('.stage-cell')
scenario 'user visits merge request page' do
page.within('.merge-request-tabs') do
expect(page).to have_no_link('Pipelines')
end
end
end
end
context 'without pipelines' do
before do
visit project_merge_request_path(project, merge_request)
describe 'race condition' do
given(:project) { create(:project, :repository) }
given(:user) { create(:user) }
given(:build_push_data) { { ref: 'feature', checkout_sha: TestEnv::BRANCH_SHA['feature'] } }
given(:merge_request_params) do
{ "source_branch" => "feature", "source_project_id" => project.id,
"target_branch" => "master", "target_project_id" => project.id, "title" => "A" }
end
background do
project.add_master(user)
sign_in user
end
scenario 'user visits merge request page' do
page.within('.merge-request-tabs') do
expect(page).to have_no_link('Pipelines')
context 'when pipeline and merge request were created simultaneously' do
background do
stub_ci_pipeline_to_return_yaml_file
threads = []
threads << Thread.new do
@merge_request = MergeRequests::CreateService.new(project, user, merge_request_params).execute
end
threads << Thread.new do
@pipeline = Ci::CreatePipelineService.new(project, user, build_push_data).execute(:push)
end
threads.each { |thr| thr.join }
end
scenario 'user sees pipeline in merge request widget' do
visit project_merge_request_path(project, @merge_request)
expect(page.find(".ci-widget")).to have_content(TestEnv::BRANCH_SHA['feature'])
expect(page.find(".ci-widget")).to have_content("##{@pipeline.id}")
end
end
end
......
......@@ -219,6 +219,25 @@ feature 'Pipeline Schedules', :js do
end
end
end
context 'when active is true and next_run_at is NULL' do
background do
create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule|
pipeline_schedule.update_attribute(:cron, nil) # Consequently next_run_at will be nil
end
end
scenario 'user edit and recover the problematic pipeline schedule' do
visit_pipelines_schedules
find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click
fill_in 'schedule_cron', with: '* 1 2 3 4'
click_button 'Save pipeline schedule'
page.within('.pipeline-schedule-table-row:nth-child(1)') do
expect(page).to have_css(".next-run-cell time")
end
end
end
end
context 'logged in as non-member' do
......
......@@ -22,12 +22,14 @@ describe Gitlab::ContributionsCalendar do
end
end
let(:today) { Time.now.to_date }
let(:today) { Time.now.utc.to_date }
let(:yesterday) { today - 1.day }
let(:tomorrow) { today + 1.day }
let(:last_week) { today - 7.days }
let(:last_year) { today - 1.year }
before do
travel_to today
travel_to Time.now.utc.end_of_day
end
after do
......@@ -38,7 +40,7 @@ describe Gitlab::ContributionsCalendar do
described_class.new(contributor, current_user)
end
def create_event(project, day)
def create_event(project, day, hour = 0)
@targets ||= {}
@targets[project] ||= create(:issue, project: project, author: contributor)
......@@ -47,7 +49,7 @@ describe Gitlab::ContributionsCalendar do
action: Event::CREATED,
target: @targets[project],
author: contributor,
created_at: day
created_at: DateTime.new(day.year, day.month, day.day, hour)
)
end
......@@ -68,6 +70,34 @@ describe Gitlab::ContributionsCalendar do
expect(calendar(user).activity_dates[today]).to eq(0)
expect(calendar(contributor).activity_dates[today]).to eq(2)
end
context "when events fall under different dates depending on the time zone" do
before do
create_event(public_project, today, 1)
create_event(public_project, today, 4)
create_event(public_project, today, 10)
create_event(public_project, today, 16)
create_event(public_project, today, 23)
end
it "renders correct event counts within the UTC timezone" do
Time.use_zone('UTC') do
expect(calendar.activity_dates).to eq(today => 5)
end
end
it "renders correct event counts within the Sydney timezone" do
Time.use_zone('Sydney') do
expect(calendar.activity_dates).to eq(today => 3, tomorrow => 2)
end
end
it "renders correct event counts within the US Central timezone" do
Time.use_zone('Central Time (US & Canada)') do
expect(calendar.activity_dates).to eq(yesterday => 2, today => 3)
end
end
end
end
describe '#events_by_date' do
......
......@@ -7,63 +7,73 @@ describe Gitlab::Git::Blame, seed_helper: true do
Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md")
end
context "each count" do
it do
data = []
blame.each do |commit, line|
data << {
commit: commit,
line: line
}
end
expect(data.size).to eq(95)
expect(data.first[:commit]).to be_kind_of(Gitlab::Git::Commit)
expect(data.first[:line]).to eq("# Contribute to GitLab")
expect(data.first[:line]).to be_utf8
end
end
shared_examples 'blaming a file' do
context "each count" do
it do
data = []
blame.each do |commit, line|
data << {
commit: commit,
line: line
}
end
context "ISO-8859 encoding" do
let(:blame) do
Gitlab::Git::Blame.new(repository, SeedRepo::EncodingCommit::ID, "encoding/iso8859.txt")
expect(data.size).to eq(95)
expect(data.first[:commit]).to be_kind_of(Gitlab::Git::Commit)
expect(data.first[:line]).to eq("# Contribute to GitLab")
expect(data.first[:line]).to be_utf8
end
end
it 'converts to UTF-8' do
data = []
blame.each do |commit, line|
data << {
commit: commit,
line: line
}
context "ISO-8859 encoding" do
let(:blame) do
Gitlab::Git::Blame.new(repository, SeedRepo::EncodingCommit::ID, "encoding/iso8859.txt")
end
expect(data.size).to eq(1)
expect(data.first[:commit]).to be_kind_of(Gitlab::Git::Commit)
expect(data.first[:line]).to eq("Ä ü")
expect(data.first[:line]).to be_utf8
end
end
it 'converts to UTF-8' do
data = []
blame.each do |commit, line|
data << {
commit: commit,
line: line
}
end
context "unknown encoding" do
let(:blame) do
Gitlab::Git::Blame.new(repository, SeedRepo::EncodingCommit::ID, "encoding/iso8859.txt")
expect(data.size).to eq(1)
expect(data.first[:commit]).to be_kind_of(Gitlab::Git::Commit)
expect(data.first[:line]).to eq("Ä ü")
expect(data.first[:line]).to be_utf8
end
end
it 'converts to UTF-8' do
expect(CharlockHolmes::EncodingDetector).to receive(:detect).and_return(nil)
data = []
blame.each do |commit, line|
data << {
context "unknown encoding" do
let(:blame) do
Gitlab::Git::Blame.new(repository, SeedRepo::EncodingCommit::ID, "encoding/iso8859.txt")
end
it 'converts to UTF-8' do
expect(CharlockHolmes::EncodingDetector).to receive(:detect).and_return(nil)
data = []
blame.each do |commit, line|
data << {
commit: commit,
line: line
}
end
}
end
expect(data.size).to eq(1)
expect(data.first[:commit]).to be_kind_of(Gitlab::Git::Commit)
expect(data.first[:line]).to eq(" ")
expect(data.first[:line]).to be_utf8
expect(data.size).to eq(1)
expect(data.first[:commit]).to be_kind_of(Gitlab::Git::Commit)
expect(data.first[:line]).to eq(" ")
expect(data.first[:line]).to be_utf8
end
end
end
context 'when Gitaly blame feature is enabled' do
it_behaves_like 'blaming a file'
end
context 'when Gitaly blame feature is disabled', skip_gitaly_mock: true do
it_behaves_like 'blaming a file'
end
end
......@@ -139,24 +139,44 @@ describe Repository do
end
describe '#last_commit_for_path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
shared_examples 'getting last commit for path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
end
context 'when Gitaly feature last_commit_for_path is enabled' do
it_behaves_like 'getting last commit for path'
end
context 'when Gitaly feature last_commit_for_path is disabled', skip_gitaly_mock: true do
it_behaves_like 'getting last commit for path'
end
end
describe '#last_commit_id_for_path' do
subject { repository.last_commit_id_for_path(sample_commit.id, '.gitignore') }
shared_examples 'getting last commit ID for path' do
subject { repository.last_commit_id_for_path(sample_commit.id, '.gitignore') }
it "returns last commit id for a given path" do
is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8')
it "returns last commit id for a given path" do
is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8')
end
it "caches last commit id for a given path" do
cache = repository.send(:cache)
key = "last_commit_id_for_path:#{sample_commit.id}:#{Digest::SHA1.hexdigest('.gitignore')}"
expect(cache).to receive(:fetch).with(key).and_return('c1acaa5')
is_expected.to eq('c1acaa5')
end
end
it "caches last commit id for a given path" do
cache = repository.send(:cache)
key = "last_commit_id_for_path:#{sample_commit.id}:#{Digest::SHA1.hexdigest('.gitignore')}"
context 'when Gitaly feature last_commit_for_path is enabled' do
it_behaves_like 'getting last commit ID for path'
end
expect(cache).to receive(:fetch).with(key).and_return('c1acaa5')
is_expected.to eq('c1acaa5')
context 'when Gitaly feature last_commit_for_path is disabled', skip_gitaly_mock: true do
it_behaves_like 'getting last commit ID for path'
end
end
......
......@@ -294,6 +294,26 @@ describe API::MergeRequests do
expect(json_response.length).to eq(0)
end
it 'returns an array of labeled merge requests that are merged for a milestone' do
bug_label = create(:label, title: 'bug', color: '#FFAABB', project: project)
mr1 = create(:merge_request, state: "merged", source_project: project, target_project: project, milestone: milestone)
mr2 = create(:merge_request, state: "merged", source_project: project, target_project: project, milestone: milestone1)
mr3 = create(:merge_request, state: "closed", source_project: project, target_project: project, milestone: milestone1)
_mr = create(:merge_request, state: "merged", source_project: project, target_project: project, milestone: milestone1)
create(:label_link, label: bug_label, target: mr1)
create(:label_link, label: bug_label, target: mr2)
create(:label_link, label: bug_label, target: mr3)
get api("/projects/#{project.id}/merge_requests?labels=#{bug_label.title}&milestone=#{milestone1.title}&state=merged", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(mr2.id)
end
context "with ordering" do
before do
@mr_later = mr_with_later_created_and_updated_at_time
......
require_relative 'devise_helpers'
module LoginHelpers
include DeviseHelpers
......
......@@ -231,7 +231,6 @@ module TestEnv
# Otherwise they'd be created by the first test, often timing out and
# causing a transient test failure
def eager_load_driver_server
return unless ENV['CI']
return unless defined?(Capybara)
puts "Starting the Capybara driver server..."
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment