Commit 77cf83aa authored by Valery Sizov's avatar Valery Sizov

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

parents 497844d0 477f0309
......@@ -13,7 +13,7 @@ variables:
ELASTIC_HOST: "registry.gitlab.com-gitlab-org-test-elastic-image"
RAILS_ENV: "test"
SIMPLECOV: "true"
USE_DB: "true"
SETUP_DB: "true"
USE_BUNDLE_INSTALL: "true"
GIT_DEPTH: "20"
PHANTOMJS_VERSION: "2.1.1"
......@@ -24,7 +24,7 @@ before_script:
- bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
- retry gem install knapsack
- '[ "$USE_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate'
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate'
stages:
- prepare
......@@ -36,7 +36,7 @@ stages:
.knapsack-state: &knapsack-state
services: []
variables:
USE_DB: "false"
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
cache:
key: "knapsack"
......@@ -198,7 +198,7 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
.ruby-static-analysis: &ruby-static-analysis
variables:
SIMPLECOV: "false"
USE_DB: "false"
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true"
.exec: &exec
......@@ -226,6 +226,23 @@ rake db:migrate:reset:
script:
- rake db:migrate:reset
rake db:seed_fu:
stage: test
<<: *use-db
variables:
SIZE: "1"
SETUP_DB: "false"
RAILS_ENV: "development"
script:
- git clone https://gitlab.com/gitlab-org/gitlab-test.git
/home/git/repositories/gitlab-org/gitlab-test.git
- bundle exec rake db:setup db:seed_fu
artifacts:
when: on_failure
expire_in: 1d
paths:
- log/development.log
teaspoon:
stage: test
<<: *use-db
......@@ -274,7 +291,7 @@ coverage:
stage: post-test
services: []
variables:
USE_DB: "false"
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true"
script:
- bundle exec scripts/merge-simplecov
......@@ -290,7 +307,7 @@ coverage:
notify:slack:
stage: post-test
variables:
USE_DB: "false"
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.12.1 (unreleased)
v 8.13.0 (unreleased)
- Speed-up group milestones show page
v 8.12.2 (unreleased)
- Fix Import/Export not recognising correctly the imported services.
v 8.12.1
- Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
- Fix issue with search filter labels not displaying
v 8.12.0
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
......@@ -29,6 +38,7 @@ v 8.12.0
- Move pushes_since_gc from the database to Redis
- Limit number of shown environments on Merge Request: show only environments for target_branch, source_branch and tags
- Add font color contrast to external label in admin area (ClemMakesApps)
- Fix find file navigation links (ClemMakesApps)
- Change logo animation to CSS (ClemMakesApps)
- Instructions for enabling Git packfile bitmaps !6104
- Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint
......@@ -47,6 +57,7 @@ v 8.12.0
- Fix file permissions change when updating a file on the Gitlab UI !5979
- Added horizontal padding on build page sidebar on code coverage block. !6196 (Vitaly Baev)
- Change merge_error column from string to text type
- Fix issue with search filter labels not displaying
- Reduce contributions calendar data payload (ClemMakesApps)
- Show all pipelines for merge requests even from discarded commits !6414
- Replace contributions calendar timezone payload with dates (ClemMakesApps)
......@@ -81,6 +92,7 @@ v 8.12.0
- Fix markdown anchor icon interaction (ClemMakesApps)
- Test migration paths from 8.5 until current release !4874
- Replace animateEmoji timeout with eventListener (ClemMakesApps)
- Show badges in Milestone tabs. !5946 (Dan Rowden)
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
- Require confirmation when not logged in for unsubscribe links !6223 (Maximiliano Perez Coto)
- Add `wiki_page_events` to project hook APIs (Ben Boeckel)
......
......@@ -333,10 +333,6 @@ group :test do
gem 'timecop', '~> 0.8.0'
end
group :production do
gem 'gitlab_meta', '7.0'
end
gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0'
......
......@@ -306,7 +306,6 @@ GEM
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
rugged (~> 0.24.0)
gitlab_meta (7.0)
gitlab_omniauth-ldap (1.2.1)
net-ldap (~> 0.9)
omniauth (~> 1.0)
......@@ -895,7 +894,6 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0)
gitlab_git (~> 10.6.6)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
......@@ -1025,4 +1023,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.13.0
1.13.1
......@@ -7,7 +7,7 @@
## Canonical source
The cannonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/).
The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/).
The source of GitLab Enterprise Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ee).
......
8.12.0-ee
8.13.0-ee-pre
((global) => {
const COOKIE_NAME = 'cycle_analytics_help_dismissed';
const store = gl.cycleAnalyticsStore = {
isLoading: true,
hasError: false,
isHelpDismissed: $.cookie(COOKIE_NAME),
analytics: {}
};
gl.CycleAnalytics = class CycleAnalytics {
constructor() {
const that = this;
this.isHelpDismissed = $.cookie(COOKIE_NAME);
this.vue = new Vue({
el: '#cycle-analytics',
name: 'CycleAnalytics',
created: this.fetchData(),
data: this.decorateData({ isLoading: true }),
data: store,
methods: {
dismissLanding() {
that.dismissLanding();
......@@ -21,6 +26,7 @@
}
fetchData(options) {
store.isLoading = true;
options = options || { startDate: 30 };
$.ajax({
......@@ -30,22 +36,20 @@
contentType: 'application/json',
data: { start_date: options.startDate }
}).done((data) => {
this.vue.$data = this.decorateData(data);
this.decorateData(data);
this.initDropdown();
})
.error((data) => {
this.handleError(data);
})
.always(() => {
this.vue.isLoading = false;
store.isLoading = false;
})
}
decorateData(data) {
data.summary = data.summary || [];
data.stats = data.stats || [];
data.isHelpDismissed = this.isHelpDismissed;
data.isLoading = data.isLoading || false;
data.summary.forEach((item) => {
item.value = item.value || '-';
......@@ -53,23 +57,21 @@
data.stats.forEach((item) => {
item.value = item.value || '- - -';
})
});
return data;
store.analytics = data;
}
handleError(data) {
this.vue.$data = {
hasError: true,
isHelpDismissed: this.isHelpDismissed
};
store.hasError = true;
new Flash('There was an error while fetching cycle analytics data.', 'alert');
}
dismissLanding() {
this.vue.isHelpDismissed = true;
$.cookie(COOKIE_NAME, true);
store.isHelpDismissed = true;
$.cookie(COOKIE_NAME, true, {
path: gon.relative_url_root || '/'
});
}
initDropdown() {
......@@ -82,7 +84,6 @@
const value = $target.data('value');
$label.text($target.text().trim());
this.vue.isLoading = true;
this.fetchData({ startDate: value });
})
}
......
......@@ -608,14 +608,13 @@
}
}
field = [];
fieldName = typeof this.options.fieldName === 'function' ? this.options.fieldName(selectedObject) : this.options.fieldName;
value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
if (isInput) {
field = $(this.el);
} else if(value) {
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']");
}
if (field.length && el.hasClass(ACTIVE_CLASS)) {
if (el.hasClass(ACTIVE_CLASS)) {
el.removeClass(ACTIVE_CLASS);
if (field && field.length) {
if (isInput) {
......
......@@ -7,7 +7,6 @@
function ProjectFindFile(element1, options) {
this.element = element1;
this.options = options;
this.goToBlob = bind(this.goToBlob, this);
this.goToTree = bind(this.goToTree, this);
this.selectRowDown = bind(this.selectRowDown, this);
this.selectRowUp = bind(this.selectRowUp, this);
......@@ -36,16 +35,6 @@
}
};
})(this));
return this.element.find(".tree-content-holder .tree-table").on("click", function(event) {
var path;
if (event.target.nodeName !== "A") {
path = this.element.find(".tree-item-file-name a", this).attr("href");
if (path) {
return location.href = path;
}
}
});
// init event
};
ProjectFindFile.prototype.findFile = function() {
......@@ -121,11 +110,12 @@
// make tbody row html
ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
var $tr;
$tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>");
$tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>");
if (matches) {
$tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl));
} else {
$tr.find("a").attr("href", blobItemUrl).text(filePath);
$tr.find("a").attr("href", blobItemUrl);
$tr.find(".str-truncated").text(filePath);
}
return $tr;
};
......@@ -164,14 +154,6 @@
return location.href = this.options.treeUrl;
};
ProjectFindFile.prototype.goToBlob = function() {
var path;
path = this.element.find(".tree-item.selected .tree-item-file-name a").attr("href");
if (path) {
return location.href = path;
}
};
return ProjectFindFile;
})();
......
......@@ -67,7 +67,6 @@
dataType: 'json',
data: {
_method: 'PATCH',
id: this.$wrap.data('banchId'),
protected_branch: formData
},
success: (response) => {
......
......@@ -3,7 +3,6 @@
margin: 0;
margin-bottom: $gl-padding;
font-size: 14px;
z-index: 100;
.flash-notice {
@extend .alert;
......@@ -41,4 +40,3 @@
}
}
}
......@@ -269,6 +269,12 @@ $calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: $gray-light;
/*
* Cycle Analytics
*/
$cycle-analytics-box-padding: 30px;
$cycle-analytics-box-text-color: #8c8c8c;
/*
* Personal Access Tokens
*/
......
#cycle-analytics {
margin: 24px auto 0;
width: 800px;
max-width: 800px;
position: relative;
.panel {
......@@ -9,11 +9,19 @@
padding: 24px 0;
border-bottom: none;
position: relative;
@media (max-width: $screen-sm-min) {
padding: 6px 0 24px;
}
}
.column {
text-align: center;
@media (max-width: $screen-sm-min) {
padding: 15px 0;
}
.header {
font-size: 30px;
line-height: 38px;
......@@ -28,11 +36,14 @@
&:last-child {
text-align: right;
@media (max-width: $screen-sm-min) {
text-align: center;
}
}
}
.dropdown {
position: relative;
top: 13px;
}
}
......@@ -40,7 +51,7 @@
.bordered-box {
border: 1px solid $border-color;
@include border-radius($border-radius-default);
position: relative;
}
.content-list {
......@@ -60,9 +71,15 @@
line-height: 19px;
font-size: 15px;
font-weight: 600;
color: $gl-title-color;
}
&.text {
color: $layout-link-gray;
&.value-col {
color: $gl-title-color;
}
&:text {
color: #8c8c8c;
}
}
}
......@@ -71,7 +88,9 @@
text-align: right;
span {
line-height: 42px;
position: relative;
vertical-align: middle;
top: 3px;
}
}
}
......@@ -82,21 +101,25 @@
.dismiss-icon {
position: absolute;
right: $gl-padding;
right: $cycle-analytics-box-padding;
cursor: pointer;
color: #b2b2b2;
}
.svg-container {
text-align: center;
svg {
margin: 0 20px;
float: left;
width: 136px;
height: 136px;
}
}
.inner-content {
width: 480px;
float: left;
@media (max-width: $screen-sm-min) {
padding: 0 28px;
text-align: center;
}
h4 {
color: $gl-text-color;
......@@ -104,7 +127,7 @@
}
p {
color: #8c8c8c;
color: $cycle-analytics-box-text-color;
margin-bottom: $gl-padding;
}
}
......
......@@ -2,7 +2,10 @@
max-width: 90%;
}
li.milestone {
.milestones {
.milestone {
padding: 10px 16px;
h4 {
font-weight: bold;
}
......@@ -10,6 +13,7 @@ li.milestone {
.progress {
height: 6px;
}
}
}
.milestone-content {
......@@ -64,3 +68,14 @@ li.milestone {
border-bottom: 1px solid $border-color;
padding: 20px 0;
}
@media (max-width: $screen-sm-min) {
.milestone-actions {
@include clearfix();
padding-top: $gl-vert-padding;
.btn:first-child {
margin-left: 0;
}
}
}
......@@ -177,6 +177,10 @@
border-bottom: 2px solid $border-color;
}
}
a {
display: block;
}
}
}
......
......@@ -55,6 +55,15 @@
}
.tree-item {
.link-container {
padding: 0;
a {
padding: 10px $gl-padding;
display: block;
}
}
.tree-item-file-name {
max-width: 320px;
vertical-align: middle;
......
......@@ -6,8 +6,6 @@ class SearchController < ApplicationController
layout 'search'
def show
return if params[:search].nil? || params[:search].blank?
if params[:project_id].present?
@project = Project.find_by(id: params[:project_id])
@project = nil unless can?(current_user, :download_code, @project)
......@@ -18,6 +16,8 @@ class SearchController < ApplicationController
@group = nil unless can?(current_user, :read_group, @group)
end
return if params[:search].nil? || params[:search].blank?
@search_term = params[:search]
@scope = params[:scope]
......
......@@ -35,6 +35,30 @@ module MilestonesHelper
milestone.issues.with_label(label.title).send(state).size
end
# Returns count of milestones for different states
# Uses explicit hash keys as the 'opened' state URL params differs from the db value
# and we need to add the total
def milestone_counts(milestones)
counts = milestones.reorder(nil).group(:state).count
{
opened: counts['active'] || 0,
closed: counts['closed'] || 0,
all: counts.values.sum || 0
}
end
# Show 'active' class if provided GET param matches check
# `or_blank` allows the function to return 'active' when given an empty param
# Could be refactored to be simpler but that may make it harder to read
def milestone_class_for_state(param, check, match_blank_param = false)
if match_blank_param
'active' if param.blank? || param == check
else
'active' if param == check
end
end
def milestone_progress_bar(milestone)
options = {
class: 'progress-bar progress-bar-success',
......
......@@ -91,7 +91,7 @@ module Ci
sha: build.sha,
ref: build.ref,
tag: build.tag,
options: build.options[:environment],
options: build.options.to_h[:environment],
variables: build.variables)
service.execute(build)
end
......
......@@ -8,7 +8,8 @@ class GlobalMilestone
milestones = milestones.group_by(&:title)
milestones.map do |title, milestones|
new(title, milestones)
milestones_relation = Milestone.where(id: milestones.map(&:id))
new(title, milestones_relation)
end
end
......@@ -31,7 +32,7 @@ class GlobalMilestone
end
def projects
@projects ||= Project.for_milestones(milestones.map(&:id))
@projects ||= Project.for_milestones(milestones.select(:id))
end
def state
......@@ -53,19 +54,19 @@ class GlobalMilestone
end
def issues
@issues ||= Issue.of_milestones(milestones.map(&:id)).includes(:project)
@issues ||= Issue.of_milestones(milestones.select(:id)).includes(:project, :assignee, :labels)
end
def merge_requests
@merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)).includes(:target_project)
@merge_requests ||= MergeRequest.of_milestones(milestones.select(:id)).includes(:target_project, :assignee, :labels)
end
def participants
@participants ||= milestones.map(&:participants).flatten.compact.uniq
@participants ||= milestones.includes(:participants).map(&:participants).flatten.compact.uniq
end
def labels
@labels ||= GlobalLabel.build_collection(milestones.map(&:labels).flatten)
@labels ||= GlobalLabel.build_collection(milestones.includes(:labels).map(&:labels).flatten)
.sort_by!(&:title)
end
......
......@@ -9,6 +9,10 @@ class CustomIssueTrackerService < IssueTrackerService
end
end
def title=(value)
self.properties['title'] = value if self.properties
end
def description
if self.properties && self.properties['description'].present?
self.properties['description']
......
= form_tag(user_omniauth_callback_path(server['provider_name']), id: 'new_ldap_user' ) do
= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user') do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "#{server['label']} Login", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
- if devise_mapping.rememberable?
......
......@@ -2,12 +2,14 @@
- page_title "Cycle Analytics"
= render "projects/pipelines/head"
#cycle-analytics{"v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project)}}
#cycle-analytics{class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project)}}
.bordered-box.landing.content-block{"v-if" => "!isHelpDismissed"}
= icon('times', class: 'dismiss-icon', "@click": "dismissLanding()")
.row
.col-sm-3.col-xs-12.svg-container
= custom_icon('icon_cycle_analytics_splash')
.inner-content
.col-sm-8.col-xs-12.inner-content
%h4
Introducing Cycle Analytics
%p
......@@ -25,11 +27,11 @@
.content-block
.container-fluid
.row
.col-xs-3.column{"v-for" => "item in summary"}
.col-sm-3.col-xs-12.column{"v-for" => "item in analytics.summary"}
%h3.header {{item.value}}
%p.text {{item.title}}
.col-xs-3.column
.col-sm-3.col-xs-12.column
.dropdown.inline.js-ca-dropdown
%button.dropdown-menu-toggle{"data-toggle" => "dropdown", :type => "button"}
%span.dropdown-label Last 30 days
......@@ -44,14 +46,14 @@
.bordered-box
%ul.content-list
%li{"v-for" => "item in stats"}
%li{"v-for" => "item in analytics.stats"}
.container-fluid
.row
.col-xs-10.title-col
.col-xs-8.title-col
%p.title
{{item.title}}
%p.text
{{item.description}}
.col-xs-2.value-col
.col-xs-4.value-col
%span
{{item.value}}
......@@ -14,9 +14,9 @@
.disabled-comment.text-center
.disabled-comment-text.inline
Please
= link_to "register", new_session_path(:user, redirect_to_referer: 'yes')
= link_to "sign up", new_session_path(:user, redirect_to_referer: 'yes')
or
= link_to "login", new_session_path(:user, redirect_to_referer: 'yes')
= link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes')
to post a comment
:javascript
......
%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch), branch_id: protected_branch.id } }
%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } }
%td
= protected_branch.name
- if @project.root_ref?(protected_branch.name)
......
......@@ -24,7 +24,7 @@
= succeed '.' do
More examples are in the
= link_to 'documentation', help_page_path("user/project/markdown", anchor: "wiki-specific-markdown")
= link_to 'documentation', help_page_path("user/markdown", anchor: "wiki-specific-markdown")
.form-group
= f.label :commit_message, class: 'control-label'
......
- if @project
- counts = milestone_counts(@project.milestones)
%ul.nav-links
%li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
%li{class: milestone_class_for_state(params[:state], 'opened', true)}
= link_to milestones_filter_path(state: 'opened') do
Open
%li{class: ("active" if params[:state] == 'closed')}
- if @project
%span.badge #{counts[:opened]}
%li{class: milestone_class_for_state(params[:state], 'closed')}
= link_to milestones_filter_path(state: 'closed') do
Closed
%li{class: ("active" if params[:state] == 'all')}
- if @project
%span.badge #{counts[:closed]}
%li{class: milestone_class_for_state(params[:state], 'all')}
= link_to milestones_filter_path(state: 'all') do
All
- if @project
%span.badge #{counts[:all]}
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="5 0 30 40">
<path fill="#7E7E7E" fill-rule="evenodd" d="M22,29.5351288 L22,22.7193602 C26.1888699,21.5098039 29.3985457,16.802989 29.3985457,16.802989 C29.740988,16.3567547 30,15.5559546 30,15.0081969 L30,10.4648712 C31.1956027,9.77325238 32,8.48056471 32,7 C32,4.790861 30.209139,3 28,3 C25.790861,3 24,4.790861 24,7 C24,8.48056471 24.8043973,9.77325238 26,10.4648712 L26,14.7083871 C26,14.8784435 25.9055559,15.0987329 25.7890533,15.2104147 C25.7890533,15.2104147 24.5373893,16.4126202 23.9488702,16.9515733 C22.5015398,18.2770075 21.1191354,19 20.090554,19 C19.0477772,19 17.6172728,18.2608988 16.1128852,16.9142923 C15.5030182,16.3683886 14.3672121,15.3403307 14.3672121,15.3403307 C14.1659605,15.1583364 14.0000086,14.7846305 14.0000192,14.5088473 C14.0000192,14.5088473 14.0000932,12.7539451 14.0001308,10.4647956 C15.1956614,9.77315812 16,8.48051074 16,7 C16,4.790861 14.209139,3 12,3 C9.790861,3 8,4.790861 8,7 C8,8.48056471 8.80439726,9.77325238 10,10.4648712 L10,15.0081969 C10,15.5446944 10.2736352,16.3534183 10.6111812,16.7893819 C10.6111812,16.7893819 13.8599776,21.3779363 18,22.6668724 L18,29.5351288 C16.8043973,30.2267476 16,31.5194353 16,33 C16,35.209139 17.790861,37 20,37 C22.209139,37 24,35.209139 24,33 C24,31.5194353 23.1956027,30.2267476 22,29.5351288 Z M14,7 C14,5.8954305 13.1045695,5 12,5 C10.8954305,5 10,5.8954305 10,7 C10,8.1045695 10.8954305,9 12,9 C13.1045695,9 14,8.1045695 14,7 Z M30,7 C30,5.8954305 29.1045695,5 28,5 C26.8954305,5 26,5.8954305 26,7 C26,8.1045695 26.8954305,9 28,9 C29.1045695,9 30,8.1045695 30,7 Z M22,33 C22,31.8954305 21.1045695,31 20,31 C18.8954305,31 18,31.8954305 18,33 C18,34.1045695 18.8954305,35 20,35 C21.1045695,35 22,34.1045695 22,33 Z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="5 0 30 40"><path fill="#7E7E7E" fill-rule="evenodd" d="M22 29.535V22.72c4.19-1.21 7.4-5.917 7.4-5.917.34-.446.6-1.247.6-1.795v-4.543C31.196 9.773 32 8.48 32 7c0-2.21-1.79-4-4-4s-4 1.79-4 4c0 1.48.804 2.773 2 3.465v4.243c0 .17-.094.39-.21.502 0 0-1.253 1.203-1.84 1.742C22.5 18.277 21.12 19 20.09 19c-1.042 0-2.473-.74-3.977-2.086-.61-.546-1.746-1.574-1.746-1.574-.2-.182-.367-.555-.367-.83v-4.045C15.196 9.773 16 8.48 16 7c0-2.21-1.79-4-4-4S8 4.79 8 7c0 1.48.804 2.773 2 3.465v4.543c0 .537.274 1.345.61 1.78 0 0 3.25 4.59 7.39 5.88v6.867c-1.196.692-2 1.984-2 3.465 0 2.21 1.79 4 4 4s4-1.79 4-4c0-1.48-.804-2.773-2-3.465zM14 7c0-1.105-.895-2-2-2s-2 .895-2 2 .895 2 2 2 2-.895 2-2zm16 0c0-1.105-.895-2-2-2s-2 .895-2 2 .895 2 2 2 2-.895 2-2zm-8 26c0-1.105-.895-2-2-2s-2 .895-2 2 .895 2 2 2 2-.895 2-2z"/></svg>
......@@ -33,7 +33,7 @@
- if @project
.row
.col-sm-6= render('shared/milestone_expired', milestone: milestone)
.col-sm-6
.col-sm-6.milestone-actions
- if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
= link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs btn-grouped" do
Edit
......
......@@ -3,11 +3,11 @@ require 'sidekiq/testing'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
project_urls = [
'https://github.com/documentcloud/underscore.git',
'https://gitlab.com/gitlab-org/gitlab-test.git',
'https://gitlab.com/gitlab-org/gitlab-ce.git',
'https://gitlab.com/gitlab-org/gitlab-ci.git',
'https://gitlab.com/gitlab-org/gitlab-shell.git',
'https://gitlab.com/gitlab-org/gitlab-test.git',
'https://github.com/documentcloud/underscore.git',
'https://github.com/twitter/flight.git',
'https://github.com/twitter/typeahead.js.git',
'https://github.com/h5bp/html5-boilerplate.git',
......@@ -38,12 +38,7 @@ Sidekiq::Testing.inline! do
]
# You can specify how many projects you need during seed execution
size = if ENV['SIZE'].present?
ENV['SIZE'].to_i
else
8
end
size = ENV['SIZE'].present? ? ENV['SIZE'].to_i : 8
project_urls.first(size).each_with_index do |url, i|
group_path, project_path = url.split('/')[-2..-1]
......
class AddIndexToLabelsTitle < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def change
add_concurrent_index :labels, :title
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160915201649) do
ActiveRecord::Schema.define(version: 20160920160832) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -582,6 +582,7 @@ ActiveRecord::Schema.define(version: 20160915201649) do
add_index "labels", ["priority"], name: "index_labels_on_priority", using: :btree
add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
add_index "labels", ["title"], name: "index_labels_on_title", using: :btree
create_table "ldap_group_links", force: :cascade do |t|
t.string "cn", null: false
......
......@@ -10,6 +10,7 @@ following locations:
- [Award Emoji](award_emoji.md)
- [Branches](branches.md)
- [Broadcast Messages](broadcast_messages.md)
- [Builds](builds.md)
- [Build Triggers](build_triggers.md)
- [Build Variables](build_variables.md)
......
......@@ -13,7 +13,7 @@ Apart from those, here is an collection of tutorials and guides on setting up yo
- [Test a Scala application](test-scala-application.md)
- [Using `dpl` as deployment tool](deployment/README.md)
- [Blog post about using GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
- [Repo's with examples for various languages](https://gitlab.com/groups/gitlab-examples)
- [Repositories with examples for various languages](https://gitlab.com/groups/gitlab-examples)
- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml
......@@ -44,7 +44,7 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project |
| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the build is run |
| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returnes the address of the registry tied to the specific project |
| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags |
......
......@@ -28,6 +28,7 @@ module API
helpers ::SentryHelper
helpers ::API::Helpers
# Keep in alphabetical order
mount ::API::AccessRequests
mount ::API::AwardEmoji
mount ::API::Branches
......@@ -52,6 +53,7 @@ module API
mount ::API::Lint
mount ::API::Members
mount ::API::MergeRequests
mount ::API::MergeRequestDiffs
mount ::API::Milestones
mount ::API::Namespaces
mount ::API::Notes
......@@ -76,6 +78,5 @@ module API
mount ::API::Triggers
mount ::API::Users
mount ::API::Variables
mount ::API::MergeRequestDiffs
end
end
......@@ -7,7 +7,7 @@ module Banzai
UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze
def whitelist
whitelist = super.dup
whitelist = super
customize_whitelist(whitelist)
......@@ -42,20 +42,19 @@ module Banzai
# Allow any protocol in `a` elements...
whitelist[:protocols].delete('a')
whitelist[:transformers] = whitelist[:transformers].dup
# ...but then remove links with unsafe protocols
whitelist[:transformers].push(remove_unsafe_links)
whitelist[:transformers].push(self.class.remove_unsafe_links)
# Remove `rel` attribute from `a` elements
whitelist[:transformers].push(remove_rel)
whitelist[:transformers].push(self.class.remove_rel)
# Remove `class` attribute from non-highlight spans
whitelist[:transformers].push(clean_spans)
whitelist[:transformers].push(self.class.clean_spans)
whitelist
end
class << self
def remove_unsafe_links
lambda do |env|
node = env[:node]
......@@ -89,7 +88,7 @@ module Banzai
return unless node.name == 'span'
return unless node.has_attribute?('class')
unless has_ancestor?(node, 'pre')
unless node.ancestors.any? { |n| n.name.casecmp('pre').zero? }
node.remove_attribute('class')
end
......@@ -98,4 +97,5 @@ module Banzai
end
end
end
end
end
......@@ -63,10 +63,8 @@ module Gitlab
# When the DBMS is not available, an exception (e.g. PG::ConnectionBad) is raised
active_db_connection = ActiveRecord::Base.connection.active? rescue false
ENV['USE_DB'] != 'false' &&
active_db_connection &&
ActiveRecord::Base.connection.table_exists?('application_settings')
rescue ActiveRecord::NoDatabaseError
false
end
......
......@@ -73,5 +73,7 @@ excluded_attributes:
methods:
statuses:
- :type
services:
- :type
merge_request_diff:
- :utf8_st_diffs
......@@ -9,19 +9,22 @@ module Gitlab
SIDEKIQ_NAMESPACE = 'resque:gitlab'
MAILROOM_NAMESPACE = 'mail_room:gitlab'
DEFAULT_REDIS_URL = 'redis://localhost:6379'
CONFIG_FILE = File.expand_path('../../config/resque.yml', __dir__)
# To be thread-safe we must be careful when writing the class instance
# variables @url and @pool. Because @pool depends on @url we need two
# variables @_raw_config and @pool. Because @pool depends on @_raw_config we need two
# mutexes to prevent deadlock.
PARAMS_MUTEX = Mutex.new
RAW_CONFIG_MUTEX = Mutex.new
POOL_MUTEX = Mutex.new
private_constant :PARAMS_MUTEX, :POOL_MUTEX
private_constant :RAW_CONFIG_MUTEX, :POOL_MUTEX
class << self
# Do NOT cache in an instance variable. Result may be mutated by caller.
def params
@params || PARAMS_MUTEX.synchronize { @params = new.params }
new.params
end
# Do NOT cache in an instance variable. Result may be mutated by caller.
# @deprecated Use .params instead to get sentinel support
def url
new.url
......@@ -36,8 +39,17 @@ module Gitlab
@pool.with { |redis| yield redis }
end
def reset_params!
@params = nil
def _raw_config
return @_raw_config if defined?(@_raw_config)
RAW_CONFIG_MUTEX.synchronize do
begin
@_raw_config = File.read(CONFIG_FILE).freeze
rescue Errno::ENOENT
@_raw_config = false
end
end
@_raw_config
end
end
......@@ -83,12 +95,7 @@ module Gitlab
end
def fetch_config
file = config_file
File.exist?(file) ? YAML.load_file(file)[@rails_env] : false
end
def config_file
File.expand_path('../../../config/resque.yml', __FILE__)
self.class._raw_config ? YAML.load(self.class._raw_config)[@rails_env] : false
end
end
end
......@@ -60,7 +60,7 @@ module Gitlab
def send_git_diff(repository, diff_refs)
params = {
'RepoPath' => repository.path_to_repo,
'ShaFrom' => diff_refs.start_sha,
'ShaFrom' => diff_refs.base_sha,
'ShaTo' => diff_refs.head_sha
}
......@@ -73,7 +73,7 @@ module Gitlab
def send_git_patch(repository, diff_refs)
params = {
'RepoPath' => repository.path_to_repo,
'ShaFrom' => diff_refs.start_sha,
'ShaFrom' => diff_refs.base_sha,
'ShaTo' => diff_refs.head_sha
}
......
......@@ -3,10 +3,15 @@ FactoryGirl.define do
title
project
trait :active do
state "active"
end
trait :closed do
state :closed
state "closed"
end
factory :active_milestone, traits: [:active]
factory :closed_milestone, traits: [:closed]
end
end
require 'spec_helper'
describe "Search", feature: true do
include WaitForAjax
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, assignee: user) }
......@@ -16,6 +18,36 @@ describe "Search", feature: true do
expect(page).not_to have_selector('.search')
end
context 'search filters', js: true do
let(:group) { create(:group) }
before do
group.add_owner(user)
end
it 'shows group name after filtering' do
find('.js-search-group-dropdown').click
wait_for_ajax
page.within '.search-holder' do
click_link group.name
end
expect(find('.js-search-group-dropdown')).to have_content(group.name)
end
it 'shows project name after filtering' do
page.within('.project-filter') do
find('.js-search-project-dropdown').click
wait_for_ajax
click_link project.name_with_namespace
end
expect(find('.js-search-project-dropdown')).to have_content(project.name_with_namespace)
end
end
describe 'searching for Projects' do
it 'finds a project' do
page.within '.search-holder' do
......
require 'spec_helper'
describe MilestonesHelper do
describe '#milestone_counts' do
let(:project) { FactoryGirl.create(:project) }
let(:counts) { helper.milestone_counts(project.milestones) }
context 'when there are milestones' do
let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) }
let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) }
let!(:milestone_3) { FactoryGirl.create(:closed_milestone, project: project) }
it 'returns the correct counts' do
expect(counts).to eq(opened: 2, closed: 1, all: 3)
end
end
context 'when there are only milestones of one type' do
let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) }
let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) }
it 'returns the correct counts' do
expect(counts).to eq(opened: 2, closed: 0, all: 2)
end
end
context 'when there are no milestones' do
it 'returns the correct counts' do
expect(counts).to eq(opened: 0, closed: 0, all: 0)
end
end
end
end
......@@ -6918,6 +6918,7 @@
"note_events": true,
"build_events": true,
"category": "issue_tracker",
"type": "CustomIssueTrackerService",
"default": true,
"wiki_page_events": true
},
......
......@@ -107,6 +107,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Label.first.label_links.first.target).not_to be_nil
end
it 'restores the correct service' do
restored_project_json
expect(CustomIssueTrackerService.first).not_to be_nil
end
context 'Merge requests' do
before do
restored_project_json
......
......@@ -111,6 +111,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(saved_project_json['issues'].first['label_links'].first['label']).not_to be_empty
end
it 'saves the correct service type' do
expect(saved_project_json['services'].first['type']).to eq('CustomIssueTrackerService')
end
it 'has project feature' do
project_feature = saved_project_json['project_feature']
expect(project_feature).not_to be_empty
......@@ -161,6 +165,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
commit_id: ci_pipeline.sha)
create(:event, target: milestone, project: project, action: Event::CREATED, author: user)
create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker')
project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED)
......
......@@ -3,19 +3,27 @@ require 'spec_helper'
describe Gitlab::Redis do
let(:redis_config) { Rails.root.join('config', 'resque.yml').to_s }
before(:each) { described_class.reset_params! }
after(:each) { described_class.reset_params! }
before(:each) { clear_raw_config }
after(:each) { clear_raw_config }
describe '.params' do
subject { described_class.params }
it 'withstands mutation' do
params1 = described_class.params
params2 = described_class.params
params1[:foo] = :bar
expect(params2).not_to have_key(:foo)
end
context 'when url contains unix socket reference' do
let(:config_old) { Rails.root.join('spec/fixtures/config/redis_old_format_socket.yml').to_s }
let(:config_new) { Rails.root.join('spec/fixtures/config/redis_new_format_socket.yml').to_s }
context 'with old format' do
it 'returns path key instead' do
expect_any_instance_of(described_class).to receive(:config_file) { config_old }
stub_const("#{described_class}::CONFIG_FILE", config_old)
is_expected.to include(path: '/path/to/old/redis.sock')
is_expected.not_to have_key(:url)
......@@ -24,7 +32,7 @@ describe Gitlab::Redis do
context 'with new format' do
it 'returns path key instead' do
expect_any_instance_of(described_class).to receive(:config_file) { config_new }
stub_const("#{described_class}::CONFIG_FILE", config_new)
is_expected.to include(path: '/path/to/redis.sock')
is_expected.not_to have_key(:url)
......@@ -38,7 +46,7 @@ describe Gitlab::Redis do
context 'with old format' do
it 'returns hash with host, port, db, and password' do
expect_any_instance_of(described_class).to receive(:config_file) { config_old }
stub_const("#{described_class}::CONFIG_FILE", config_old)
is_expected.to include(host: 'localhost', password: 'mypassword', port: 6379, db: 99)
is_expected.not_to have_key(:url)
......@@ -47,7 +55,7 @@ describe Gitlab::Redis do
context 'with new format' do
it 'returns hash with host, port, db, and password' do
expect_any_instance_of(described_class).to receive(:config_file) { config_new }
stub_const("#{described_class}::CONFIG_FILE", config_new)
is_expected.to include(host: 'localhost', password: 'mynewpassword', port: 6379, db: 99)
is_expected.not_to have_key(:url)
......@@ -56,6 +64,30 @@ describe Gitlab::Redis do
end
end
describe '.url' do
it 'withstands mutation' do
url1 = described_class.url
url2 = described_class.url
url1 << 'foobar'
expect(url2).not_to end_with('foobar')
end
end
describe '._raw_config' do
subject { described_class._raw_config }
it 'should be frozen' do
expect(subject).to be_frozen
end
it 'returns false when the file does not exist' do
stub_const("#{described_class}::CONFIG_FILE", '/var/empty/doesnotexist')
expect(subject).to eq(false)
end
end
describe '#raw_config_hash' do
it 'returns default redis url when no config file is present' do
expect(subject).to receive(:fetch_config) { false }
......@@ -71,9 +103,15 @@ describe Gitlab::Redis do
describe '#fetch_config' do
it 'returns false when no config file is present' do
allow(File).to receive(:exist?).with(redis_config) { false }
allow(described_class).to receive(:_raw_config) { false }
expect(subject.send(:fetch_config)).to be_falsey
end
end
def clear_raw_config
described_class.remove_instance_variable(:@_raw_config)
rescue NameError
# raised if @_raw_config was not set; ignore
end
end
......@@ -2,7 +2,15 @@ require 'spec_helper'
describe Gitlab::Workhorse, lib: true do
let(:project) { create(:project) }
let(:subject) { Gitlab::Workhorse }
let(:repository) { project.repository }
def decode_workhorse_header(array)
key, value = array
command, encoded_params = value.split(":")
params = JSON.parse(Base64.urlsafe_decode64(encoded_params))
[key, command, params]
end
describe ".send_git_archive" do
context "when the repository doesn't have an archive file path" do
......@@ -11,9 +19,35 @@ describe Gitlab::Workhorse, lib: true do
end
it "raises an error" do
expect { subject.send_git_archive(project.repository, ref: "master", format: "zip") }.to raise_error(RuntimeError)
expect { described_class.send_git_archive(project.repository, ref: "master", format: "zip") }.to raise_error(RuntimeError)
end
end
end
describe '.send_git_patch' do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_patch(repository, diff_refs) }
it 'sets the header correctly' do
key, command, params = decode_workhorse_header(subject)
expect(key).to eq("Gitlab-Workhorse-Send-Data")
expect(command).to eq("git-format-patch")
expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
end
end
describe '.send_git_diff' do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_patch(repository, diff_refs) }
it 'sets the header correctly' do
key, command, params = decode_workhorse_header(subject)
expect(key).to eq("Gitlab-Workhorse-Send-Data")
expect(command).to eq("git-format-patch")
expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
end
end
describe ".secret" do
......
......@@ -50,8 +50,9 @@ describe GlobalMilestone, models: true do
milestone1_project2,
milestone1_project3,
]
milestones_relation = Milestone.where(id: milestones.map(&:id))
@global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones)
@global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones_relation)
end
it 'has exactly one group milestone' do
......@@ -67,7 +68,7 @@ describe GlobalMilestone, models: true do
let(:milestone) { create(:milestone, title: "git / test", project: project1) }
it 'strips out slashes and spaces' do
global_milestone = GlobalMilestone.new(milestone.title, [milestone])
global_milestone = GlobalMilestone.new(milestone.title, Milestone.where(id: milestone.id))
expect(global_milestone.safe_title).to eq('git-test')
end
......
......@@ -25,5 +25,21 @@ describe CustomIssueTrackerService, models: true do
it { is_expected.not_to validate_presence_of(:issues_url) }
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
context 'title' do
let(:issue_tracker) { described_class.new(properties: {}) }
it 'sets a default title' do
issue_tracker.title = nil
expect(issue_tracker.title).to eq('Custom Issue Tracker')
end
it 'sets the custom title' do
issue_tracker.title = 'test title'
expect(issue_tracker.title).to eq('test title')
end
end
end
end
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