Commit f9ace37a authored by Regis's avatar Regis

Merge branch 'master' into auto-pipelines-vue

parents 384ea58f b216d9bf
......@@ -249,7 +249,7 @@ teaspoon:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs
- npm install --global istanbul
- teaspoon
- rake teaspoon
artifacts:
name: coverage-javascript
expire_in: 31d
......
......@@ -143,7 +143,7 @@ linters:
# with two colons. Pseudo-classes, like :hover and :first-child, should
# be declared with one colon.
PseudoElement:
enabled: false
enabled: true
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
QualifyingElement:
......
Please view this file on the master branch, on stable branches it's out of date.
## 8.14.0 (2016-11-22)
- Backups do not fail anymore when using tar on annex and custom_hooks only. !5814
- Adds user project membership expired event to clarify why user was removed (Callum Dryden)
- Trim leading and trailing whitespace on project_path (Linus Thiel)
- Prevent award emoji via notes for issues/MRs authored by user (barthc)
- Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
- Fix extra space on Build sidebar on Firefox !7060
- Fix mobile layout issues in admin user overview page !7087
- Fix HipChat notifications rendering (airatshigapov, eisnerd)
- Refactor Jira service to use jira-ruby gem
- Add hover to trash icon in notes !7008 (blackst0ne)
- Only show one error message for an invalid email !5905 (lycoperdon)
- Fix sidekiq stats in admin area (blackst0ne)
- Created cycle analytics bundle JavaScript file
- API: Fix booleans not recognized as such when using the `to_boolean` helper
- Removed delete branch tooltip !6954
- Stop unauthorized users dragging on milestone page (blackst0ne)
- Restore issue boards welcome message when a project is created !6899
- Escape ref and path for relative links !6050 (winniehell)
- Fixed link typo on /help/ui to Alerts section. !6915 (Sam Rose)
- Fix filtering of milestones with quotes in title (airatshigapov)
- Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
- Update mail_room and enable sentinel support to Reply By Email (!7101)
- Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
- Simpler arguments passed to named_route on toggle_award_url helper method
- Fix typo in framework css class. !7086 (Daniel Voogsgerd)
- New issue board list dropdown stays open after adding a new list
- Fix: Backup restore doesn't clear cache
- API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh)
- Replace jquery.cookie plugin with js.cookie !7085
- Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
- Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens
- Show full status link on MR & commit pipelines
- Fix documents and comments on Build API `scope`
- Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
- Shortened merge request modal to let clipboard button not overlap
- In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo)
## 8.13.2
- Fix builds dropdown overlapping bug !7124
- Fix applying labels for GitHub-imported MRs !7139
- Fix importing MR comments from GitHub !7139
- Modify GitHub importer to be retryable !7003
- Fix and improve `Sortable.highest_label_priority`
- Fixed sticky merge request tabs when sidebar is pinned
## 8.13.1 (2016-10-25)
- Fix branch protection API. !6215
......@@ -72,6 +95,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Updating verbiage on git basics to be more intuitive
- Fix project_feature record not generated on project creation
- Clarify documentation for Runners API (Gennady Trafimenkov)
- Use optimistic locking for pipelines and builds
- The instrumentation for Banzai::Renderer has been restored
- Change user & group landing page routing from /u/:username to /:username
- Added documentation for .gitattributes files
......@@ -97,6 +121,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Add more tests for calendar contribution (ClemMakesApps)
- Update Gitlab Shell to fix some problems with moving projects between storages
- Cache rendered markdown in the database, rather than Redis
- Add todo toggle event (ClemMakesApps)
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
- Simplify Mentionable concern instance methods
- API: Ability to retrieve version information (Robert Schilling)
......@@ -138,6 +163,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Only update issuable labels if they have been changed
- Take filters in account in issuable counters. !6496
- Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*)
- Replace static issue fixtures by script !6059 (winniehell)
- Append issue template to existing description !6149 (Joseph Frazier)
- Trending projects now only show public projects and the list of projects is cached for a day
- Memoize Gitlab Shell's secret token (!6599, Justin DiPierro)
......
......@@ -161,6 +161,9 @@ gem 'connection_pool', '~> 2.0'
# HipChat integration
gem 'hipchat', '~> 1.5.0'
# JIRA integration
gem 'jira-ruby', '~> 1.1.2'
# Flowdock integration
gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
......@@ -326,7 +329,7 @@ gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0'
gem 'mail_room', '~> 0.8.1'
gem 'mail_room', '~> 0.9.0'
gem 'email_reply_parser', '~> 0.5.8'
......
......@@ -356,6 +356,9 @@ GEM
cause
json
ipaddress (0.8.3)
jira-ruby (1.1.2)
activesupport
oauth (~> 0.5, >= 0.5.0)
jquery-atwho-rails (1.3.2)
jquery-rails (4.1.1)
rails-dom-testing (>= 1, < 3)
......@@ -402,7 +405,7 @@ GEM
systemu (~> 2.6.2)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail_room (0.8.1)
mail_room (0.9.0)
method_source (0.8.2)
mime-types (2.99.3)
mimemagic (0.3.0)
......@@ -421,7 +424,7 @@ GEM
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
numerizer (0.1.1)
oauth (0.4.7)
oauth (0.5.1)
oauth2 (1.2.0)
faraday (>= 0.8, < 0.10)
jwt (~> 1.0)
......@@ -881,6 +884,7 @@ DEPENDENCIES
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
influxdb (~> 0.2)
jira-ruby (~> 1.1.2)
jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.1.0)
jquery-turbolinks (~> 2.1.0)
......@@ -893,7 +897,7 @@ DEPENDENCIES
license_finder (~> 2.1.0)
licensee (~> 8.0.0)
loofah (~> 2.0.3)
mail_room (~> 0.8.1)
mail_room (~> 0.9.0)
method_source (~> 0.8)
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
......@@ -994,4 +998,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.13.2
1.13.3
......@@ -56,6 +56,10 @@ There are various other options to install GitLab, please refer to the [installa
You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password.
## Contributing
GitLab is an open source project and we are very happy to accept community contributions. Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
## Install a development environment
To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
......
/* eslint-disable */
(() => {
const Store = gl.issueBoards.BoardsStore;
......
......@@ -33,6 +33,7 @@ $(() => {
},
filterable: true,
selectable: true,
multiSelect: true,
clicked (label, $el, e) {
e.preventDefault();
......
/* eslint-disable */
Vue.filter('due-date', (value) => {
const date = new Date(value);
return $.datepicker.formatDate('M d, yy', date);
......
/* eslint-disable */
class ListMilestone {
constructor (obj) {
this.id = obj.id;
......
......@@ -63,7 +63,8 @@
this.removeList('blank');
Cookies.set('issue_board_welcome_hidden', 'true', {
expires: 365 * 10
expires: 365 * 10,
path: ''
});
},
welcomeIsHidden () {
......
......@@ -239,6 +239,7 @@
this.fullData = this.options.data;
currentIndex = -1;
this.parseData(this.options.data);
this.focusTextInput();
} else {
this.remote = new GitLabDropdownRemote(this.options.data, {
dataType: this.options.dataType,
......@@ -247,6 +248,7 @@
return function(data) {
_this.fullData = data;
_this.parseData(_this.fullData);
_this.focusTextInput();
if (_this.options.filterable && _this.filter && _this.filter.input) {
return _this.filter.input.trigger('input');
}
......@@ -452,9 +454,8 @@
contentHtml = $('.dropdown-content', this.dropdown).html();
if (this.remote && contentHtml === "") {
this.remote.execute();
}
if (this.options.filterable) {
this.filterInput.focus();
} else {
this.focusTextInput();
}
if (this.options.showMenuAbove) {
......@@ -691,6 +692,10 @@
return selectedObject;
};
GitLabDropdown.prototype.focusTextInput = function() {
if (this.options.filterable) { this.filterInput.focus() }
}
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
var $input;
// Create hidden input for form
......
/* eslint-disable */
(function() {
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(gl.text.addDelimiter(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
})();
......@@ -95,7 +95,11 @@
return $.ajax({
type: 'PATCH',
url: $('form.js-issuable-update').attr('action'),
data: patchData
data: patchData,
success: function(issue) {
document.querySelector('#task_status').innerText = issue.task_status;
document.querySelector('#task_status_short').innerText = issue.task_status_short;
}
});
// TODO (rspeicher): Make the issue description inline-editable like a note so
// that we can re-use its form here
......
......@@ -8,6 +8,9 @@
if ((base = w.gl).text == null) {
base.text = {};
}
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
}
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
......
......@@ -97,7 +97,11 @@
return $.ajax({
type: 'PATCH',
url: $('form.js-issuable-update').attr('action'),
data: patchData
data: patchData,
success: function(mergeRequest) {
document.querySelector('#task_status').innerText = mergeRequest.task_status;
document.querySelector('#task_status_short').innerText = mergeRequest.task_status_short;
}
});
// TODO (rspeicher): Make the merge request description inline-editable like a
// note so that we can re-use its form here
......
......@@ -82,16 +82,11 @@
};
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
var $todoPendingCount;
$todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(data.count);
$(document).trigger('todo:toggle', data.count);
$btn.enable();
$todoLoading.addClass('hidden');
if (data.count === 0) {
$todoPendingCount.addClass('hidden');
} else {
$todoPendingCount.removeClass('hidden');
}
if (data.delete_path != null) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
return $btnText.text($btn.data('mark-text'));
......
......@@ -38,7 +38,8 @@
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body', (e) => this.handleClickEvent(e))
.on('page:change', () => this.renderState());
.on('page:change', () => this.renderState())
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
this.renderState();
}
......@@ -53,6 +54,10 @@
}
}
updateTodoCount(count) {
$('.js-todos-count').text(gl.text.addDelimiter(count));
}
toggleSidebar() {
this.isExpanded = !this.isExpanded;
this.renderState();
......
......@@ -98,7 +98,8 @@
}
updateBadges(data) {
$('.todos-pending .badge, .todos-pending-count').text(data.count);
$(document).trigger('todo:toggle', data.count);
$('.todos-pending .badge').text(data.count);
return $('.todos-done .badge').text(data.done_count);
}
......
......@@ -15,6 +15,7 @@
@media (max-width: $screen-xs-max) {
width: 100%;
min-width: 240px;
}
}
......@@ -485,7 +486,7 @@
font-size: 20px;
text-indent: 0;
&:before {
&::before {
display: block;
position: relative;
top: -2px;
......@@ -517,7 +518,7 @@
background-color: transparent;
border: 0;
.ui-icon:before {
.ui-icon::before {
color: $md-link-color;
}
}
......@@ -526,7 +527,7 @@
.ui-datepicker-prev {
left: 0;
.ui-icon:before {
.ui-icon::before {
content: '\f104';
text-align: left;
}
......@@ -535,7 +536,7 @@
.ui-datepicker-next {
right: 0;
.ui-icon:before {
.ui-icon::before {
content: '\f105';
text-align: right;
}
......
......@@ -14,7 +14,7 @@
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:after {
&::after {
content: " ";
display: table;
clear: both;
......@@ -38,7 +38,7 @@
&.smoke { background-color: $background-color; }
&:hover {
&:not(.ui-sort-disabled):hover {
background: $row-hover;
}
......
......@@ -164,6 +164,18 @@
padding-left: $sidebar_width;
}
}
.merge-request-tabs-holder.affix {
@media (min-width: $sidebar-breakpoint) {
left: $sidebar_width;
}
}
&.right-sidebar-expanded {
.line-resolve-all-container {
display: none;
}
}
}
header.header-sidebar-pinned {
......
......@@ -45,7 +45,7 @@
@media (max-width: $screen-xs-max) {
.timeline {
&:before {
&::before {
background: none;
}
......
......@@ -148,7 +148,7 @@
a[href*="/uploads/"],
a[href*="storage.googleapis.com/google-code-attachments/"] {
&:before {
&::before {
margin-right: 4px;
font: normal normal normal 14px/1 FontAwesome;
......@@ -158,13 +158,13 @@
content: "\f0c6";
}
&:hover:before {
&:hover::before {
text-decoration: none;
}
}
a.no-attachment-icon {
&:before {
&::before {
display: none;
}
}
......@@ -183,13 +183,13 @@
position: absolute;
text-decoration: none;
&:after {
&::after {
content: image-url('icon_anchor.svg');
visibility: hidden;
}
}
&:hover > a.anchor:after {
&:hover > a.anchor::after {
visibility: visible;
}
}
......
......@@ -80,10 +80,13 @@
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
white-space: nowrap;
}
.user-details {
flex: 1 1 auto;
overflow: hidden;
padding-right: 8px;
}
.user-name {
......@@ -91,6 +94,12 @@
font-weight: 600;
}
.user-name,
.user-email {
overflow: hidden;
text-overflow: ellipsis;
}
.dropdown {
.btn-block {
margin-bottom: 0;
......
......@@ -94,14 +94,14 @@
position: relative;
&.old {
&:before {
&::before {
content: '-';
position: absolute;
}
}
&.new {
&:before {
&::before {
content: '+';
position: absolute;
}
......@@ -471,7 +471,7 @@
.file-holder {
.diff-line-num:not(.js-unfold-bottom) {
a {
&:before {
&::before {
content: attr(data-linenumber);
}
}
......
......@@ -279,6 +279,10 @@
#modal_merge_info .modal-dialog {
width: 600px;
.dark {
margin-right: 40px;
}
.btn-clipboard {
@extend .pull-right;
......@@ -439,12 +443,12 @@
}
.merge-request-tabs-holder {
background-color: #fff;
background-color: $white-light;
&.affix {
top: 100px;
left: 0;
z-index: 9;
z-index: 10;
transition: right .15s;
}
......
......@@ -474,8 +474,8 @@
}
.arrow {
&:before,
&:after {
&::before,
&::after {
content: '';
display: inline-block;
position: absolute;
......@@ -486,14 +486,14 @@
top: 18px;
}
&:before {
&::before {
left: -5px;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: $border-color;
}
&:after {
&::after {
left: -4px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
......@@ -573,8 +573,7 @@
.build {
// Remove right connecting horizontal line from first build in last stage
&:first-child {
&::after,
&::before {
&::after {
border: none;
}
}
......
......@@ -77,14 +77,14 @@
// Middle dot divider between each element in a list of items.
.middle-dot-divider {
&:after {
&::after {
content: "\00B7"; // Middle Dot
padding: 0 6px;
font-weight: bold;
}
&:last-child {
&:after {
&::after {
content: "";
padding: 0;
}
......
......@@ -193,7 +193,7 @@
margin-left: 4px;
.arrow {
&:before {
&::before {
content: '';
display: inline-block;
position: absolute;
......@@ -209,7 +209,7 @@
pointer-events: none;
}
&:after {
&::after {
content: '';
position: absolute;
width: 0;
......@@ -351,7 +351,7 @@ a.deploy-project-label {
line-height: 36px;
margin: 0;
> li + li:before {
> li + li::before {
padding: 0 3px;
color: #999;
}
......@@ -790,7 +790,7 @@ pre.light-well {
top: 7px;
color: $location-icon-color;
&:before {
&::before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
......
......@@ -72,7 +72,7 @@
top: 0;
color: $location-icon-color;
&:before {
&::before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
......
......@@ -5,7 +5,7 @@
}
.example {
&:before {
&::before {
content: "Example";
color: #bbb;
}
......
......@@ -35,7 +35,7 @@ nav.navbar-collapse.collapse,
.nav,
.btn,
ul.notes-form,
.merge-request-ci-status .ci-status-link:after,
.merge-request-ci-status .ci-status-link::after,
.issuable-gutter-toggle,
.gutter-toggle,
.issuable-details .content-block-small,
......
......@@ -18,7 +18,7 @@ module ServiceParams
:add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
:jira_issue_transition_id, :url, :project_key]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
......
......@@ -112,7 +112,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
format.json do
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end
end
......
......@@ -278,7 +278,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.target_project, @merge_request])
end
format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end
end
else
......
......@@ -30,6 +30,8 @@ class ProjectsController < Projects::ApplicationController
@project = ::Projects::CreateService.new(current_user, project_params).execute
if @project.saved?
cookies[:issue_board_welcome_hidden] = { path: project_path(@project), value: nil, expires: Time.at(0) }
redirect_to(
project_path(@project),
notice: "Project '#{@project.name}' was successfully created."
......
......@@ -4,9 +4,8 @@ class LabelsFinder < UnionFinder
@params = params
end
def execute(authorized_only: true)
@authorized_only = authorized_only
def execute(skip_authorization: false)
@skip_authorization = skip_authorization
items = find_union(label_ids, Label)
items = with_title(items)
sort(items)
......@@ -14,7 +13,7 @@ class LabelsFinder < UnionFinder
private
attr_reader :current_user, :params, :authorized_only
attr_reader :current_user, :params, :skip_authorization
def label_ids
label_ids = []
......@@ -70,17 +69,17 @@ class LabelsFinder < UnionFinder
end
def find_project
if authorized_only
available_projects.find_by(id: project_id)
else
if skip_authorization
Project.find_by(id: project_id)
else
available_projects.find_by(id: project_id)
end
end
def projects
return @projects if defined?(@projects)
@projects = authorized_only ? available_projects : Project.all
@projects = skip_authorization ? Project.all : available_projects
@projects = @projects.in_namespace(group_id) if group_id
@projects = @projects.where(id: projects_ids) if projects_ids
@projects = @projects.reorder(nil)
......
......@@ -71,6 +71,14 @@ module IssuablesHelper
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end
if issuable.tasks?
output << "&ensp;".html_safe
output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs")
output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-sm hidden-md hidden-lg")
end
output
end
def issuable_todo(issuable)
......
......@@ -30,23 +30,23 @@ module Ci
end
event :run do
transition any => :running
transition any - [:running] => :running
end
event :skip do
transition any => :skipped
transition any - [:skipped] => :skipped
end
event :drop do
transition any => :failed
transition any - [:failed] => :failed
end
event :succeed do
transition any => :success
transition any - [:success] => :success
end
event :cancel do
transition any => :canceled
transition any - [:canceled] => :canceled
end
# IMPORTANT
......@@ -260,7 +260,7 @@ module Ci
end
def update_status
with_lock do
Gitlab::OptimisticLocking.retry_lock(self) do
case latest_builds_status
when 'pending' then enqueue
when 'running' then run
......
......@@ -73,16 +73,16 @@ class CommitStatus < ActiveRecord::Base
transition [:created, :pending, :running] => :canceled
end
after_transition created: [:pending, :running] do |commit_status|
commit_status.update_attributes queued_at: Time.now
before_transition created: [:pending, :running] do |commit_status|
commit_status.queued_at = Time.now
end
after_transition [:created, :pending] => :running do |commit_status|
commit_status.update_attributes started_at: Time.now
before_transition [:created, :pending] => :running do |commit_status|
commit_status.started_at = Time.now
end
after_transition any => [:success, :failed, :canceled] do |commit_status|
commit_status.update_attributes finished_at: Time.now
before_transition any => [:success, :failed, :canceled] do |commit_status|
commit_status.finished_at = Time.now
end
after_transition do |commit_status, transition|
......
......@@ -12,6 +12,7 @@ module Issuable
include Subscribable
include StripAttribute
include Awardable
include Taskable
included do
cache_markdown_field :title, pipeline: :single_line
......
......@@ -38,16 +38,21 @@ module Sortable
private
def highest_label_priority(target_type:, target_column:, project_column:, excluded_labels: [])
def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
query = Label.select(LabelPriority.arel_table[:priority].minimum).
left_join_priorities.
joins(:label_links).
where("label_priorities.project_id = #{project_column}").
where(label_links: { target_type: target_type }).
where("label_links.target_id = #{target_column}").
reorder(nil)
query.where.not(title: excluded_labels) if excluded_labels.present?
if target_type_column
query = query.where("label_links.target_type = #{target_type_column}")
else
query = query.where(label_links: { target_type: target_type })
end
query = query.where.not(title: excluded_labels) if excluded_labels.present?
query
end
......
......@@ -53,10 +53,22 @@ module Taskable
# Return a string that describes the current state of this Taskable's task
# list items, e.g. "12 of 20 tasks completed"
def task_status
def task_status(short: false)
return '' if description.blank?
prep, completed = if short
['/', '']
else
[' of ', ' completed']
end
sum = tasks.summary
"#{sum.complete_count} of #{sum.item_count} #{'task'.pluralize(sum.item_count)} completed"
"#{sum.complete_count}#{prep}#{sum.item_count} #{'task'.pluralize(sum.item_count)}#{completed}"
end
# Return a short string that describes the current state of this Taskable's
# task list items -- for small screens
def task_status_short
task_status(short: true)
end
end
......@@ -68,7 +68,7 @@ class Group < Namespace
end
def web_url
Gitlab::Routing.url_helpers.group_url(self)
Gitlab::Routing.url_helpers.group_canonical_url(self)
end
def human_name
......
......@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base
include Issuable
include Referable
include Sortable
include Taskable
include Spammable
include FasterCacheKeys
......
......@@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base
include Issuable
include Referable
include Sortable
include Taskable
include Importable
belongs_to :target_project, class_name: "Project"
......
......@@ -738,7 +738,7 @@ class Project < ActiveRecord::Base
def create_labels
Label.templates.each do |label|
params = label.attributes.except('id', 'template', 'created_at', 'updated_at')
Labels::FindOrCreateService.new(owner, self, params).execute
Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true)
end
end
......
class BugzillaService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
......
class CustomIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
......
class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing.url_helpers
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
default_value_for :default, true
......
class IssueTrackerService < Service
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
default_value_for :category, 'issue_tracker'
# Pattern used to extract links from comments
......@@ -38,18 +36,24 @@ class IssueTrackerService < Service
]
end
def initialize_properties
if properties.nil?
if enabled_in_gitlab_config
# Initialize with default properties values
# or receive a block with custom properties
def initialize_properties(&block)
return unless properties.nil?
if enabled_in_gitlab_config
if block_given?
yield
else
self.properties = {
title: issues_tracker['title'],
project_url: issues_tracker['project_url'],
issues_url: issues_tracker['issues_url'],
new_issue_url: issues_tracker['new_issue_url']
}
else
self.properties = {}
end
else
self.properties = {}
end
end
......
This diff is collapsed.
class RedmineService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
......
......@@ -125,14 +125,8 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER
end
def member?(user, min_member_access = nil)
member = !!find_member(user.id)
if min_member_access
member && max_member_access(user.id) >= min_member_access
else
member
end
def member?(user, min_member_access = Gitlab::Access::GUEST)
max_member_access(user.id) >= min_member_access
end
def human_max_access(user_id)
......
......@@ -11,6 +11,20 @@ class Repository
attr_accessor :path_with_namespace, :project
def self.storages
Gitlab.config.repositories.storages
end
def self.remove_storage_from_path(repo_path)
storages.find do |_, storage_path|
if repo_path.start_with?(storage_path)
return repo_path.sub(storage_path, '')
end
end
repo_path
end
def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace
@project = project
......
......@@ -53,7 +53,7 @@ class Todo < ActiveRecord::Base
# Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue"
def order_by_labels_priority
params = {
target_type: ['Issue', 'MergeRequest'],
target_type_column: "todos.target_type",
target_column: "todos.target_id",
project_column: "todos.project_id"
}
......
......@@ -93,8 +93,10 @@ class User < ActiveRecord::Base
#
# Validations
#
# Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true
validates :notification_email, presence: true, email: true
validates :notification_email, presence: true
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
......
......@@ -10,17 +10,14 @@ module Ci
create_builds!
end
@pipeline.with_lock do
new_builds =
stage_indexes_of_created_builds.map do |index|
process_stage(index)
end
new_builds =
stage_indexes_of_created_builds.map do |index|
process_stage(index)
end
@pipeline.update_status
@pipeline.update_status
# Return a flag if a when builds got enqueued
new_builds.flatten.any?
end
new_builds.flatten.any?
end
private
......@@ -32,9 +29,11 @@ module Ci
def process_stage(index)
current_status = status_for_prior_stages(index)
created_builds_in_stage(index).select do |build|
if HasStatus::COMPLETED_STATUSES.include?(current_status)
process_build(build, current_status)
if HasStatus::COMPLETED_STATUSES.include?(current_status)
created_builds_in_stage(index).select do |build|
Gitlab::OptimisticLocking.retry_lock(build) do |subject|
process_build(subject, current_status)
end
end
end
end
......
......@@ -28,17 +28,14 @@ module Ci
if build
# In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition in run! method.
build.with_lock do
build.runner_id = current_runner.id
build.save!
build.run!
end
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
build.runner_id = current_runner.id
build.run!
end
build
rescue StateMachines::InvalidTransition
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
nil
end
......
......@@ -2,21 +2,24 @@ module Labels
class FindOrCreateService
def initialize(current_user, project, params = {})
@current_user = current_user
@group = project.group
@project = project
@params = params.dup
end
def execute
def execute(skip_authorization: false)
@skip_authorization = skip_authorization
find_or_create_label
end
private
attr_reader :current_user, :group, :project, :params
attr_reader :current_user, :project, :params, :skip_authorization
def available_labels
@available_labels ||= LabelsFinder.new(current_user, project_id: project.id).execute
@available_labels ||= LabelsFinder.new(
current_user,
project_id: project.id
).execute(skip_authorization: skip_authorization)
end
def find_or_create_label
......
......@@ -4,17 +4,25 @@ module Members
attr_accessor :source
# source - The source object that respond to `#requesters` (i.g. project or group)
# current_user - The user that performs the access request approval
# params - A hash of parameters
# :user_id - User ID used to retrieve the access requester
# :id - Member ID used to retrieve the access requester
# :access_level - Optional access level set when the request is accepted
def initialize(source, current_user, params = {})
@source = source
@current_user = current_user
@params = params
@params = params.slice(:user_id, :id, :access_level)
end
def execute
# opts - A hash of options
# :force - Bypass permission check: current_user can be nil in that case
def execute(opts = {})
condition = params[:user_id] ? { user_id: params[:user_id] } : { id: params[:id] }
access_requester = source.requesters.find_by!(condition)
raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester)
raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester, opts)
access_requester.access_level = params[:access_level] if params[:access_level]
access_requester.accept_request
......@@ -24,8 +32,11 @@ module Members
private
def can_update_access_requester?(access_requester)
access_requester && can?(current_user, action_member_permission(:update, access_requester), access_requester)
def can_update_access_requester?(access_requester, opts = {})
access_requester && (
opts[:force] ||
can?(current_user, action_member_permission(:update, access_requester), access_requester)
)
end
end
end
......@@ -29,7 +29,7 @@ module Projects
if unknown_url?
# In this case, we only want to import issues, not a repository.
create_repository
else
elsif !project.repository_exists?
import_repository
end
end
......
......@@ -7,7 +7,7 @@
= link_to dashboard_todos_path, title: 'Todos' do
%span
Todos
%span.count= number_with_delimiter(todos_pending_count)
%span.count.js-todos-count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
%span
......
......@@ -31,7 +31,12 @@
= render 'projects/buttons/download', project: @project, ref: branch.name
- if can?(current_user, :push_code, @project)
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: "btn btn-remove remove-row has-tooltip #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}", title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name),
class: "btn btn-remove remove-row #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}",
method: :delete,
data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" },
remote: true,
"aria-label" => "Delete branch" do
= icon("trash-o")
- if branch.name != @repository.root_ref
......
- is_playable = subject.playable? && can?(current_user, :update_build, @project)
- if is_playable
= link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do
= render_status_with_link('build', 'play')
= ci_icon_for_status('play')
.ci-status-text= subject.name
- elsif can?(current_user, :read_build, @project)
= link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do
%span.ci-status-icon
= render_status_with_link('build', subject.status)
= ci_icon_for_status(subject.status)
.ci-status-text= subject.name
- else
%span.ci-status-icon
= render_status_with_link('build', subject.status)
= ci_icon_for_status(subject.status)
= ci_icon_for_status(subject.status)
......@@ -5,10 +5,7 @@
%tr.commit
%td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
- if defined?(status_icon_only) && status_icon_only
= ci_icon_for_status(status)
- else
= ci_status_with_icon(status)
= ci_status_with_icon(status)
%td
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
......@@ -40,7 +37,7 @@
%p.commit-title
- if commit = pipeline.commit
= author_avatar(commit, size: 20)
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message"
= link_to_gfm truncate(commit.title, length: 60, escape: false), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message"
- else
Cant find HEAD commit for this branch
......
- group_status = CommitStatus.where(id: subject).status
%button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
%span.ci-status-icon
= render_status_with_link('build', group_status)
= ci_icon_for_status(group_status)
%span.ci-status-text
= name
%span.badge= subject.size
......
......@@ -12,4 +12,4 @@
%th Stages
%th
%th
= render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, status_icon_only: true, show_commit: false
= render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, show_commit: false
- @no_container = true
- page_title "Cycle Analytics"
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('cycle_analytics/cycle_analytics_bundle.js')
= render "projects/pipelines/head"
#cycle-analytics{class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) }}
......
......@@ -2,9 +2,9 @@
- if subject.target_url
= link_to subject.target_url do
%span.ci-status-icon
= render_status_with_link('commit status', subject.status)
= ci_icon_for_status(subject.status)
%span.ci-status-text= subject.name
- else
%span.ci-status-icon
= render_status_with_link('commit status', subject.status)
= ci_icon_for_status(subject.status)
%span.ci-status-text= subject.name
......@@ -5,33 +5,59 @@
%h4.prepend-top-0
= page_title
.col-lg-9
%h5.prepend-top-0
Pipelines
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f|
%fieldset.builds-feature
- unless @repository.gitlab_ci_yml
.form-group
%p Pipelines need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
%hr
.form-group.append-bottom-default
= f.label :runners_token, "Runner token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used by the Runner to checkout the project
%hr
.form-group
%p Get recent application code using the following command:
%h5.prepend-top-0
Git strategy for pipelines
%p
Choose between <code>clone</code> or <code>fetch</code> to get the recent application code
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy')
.radio
= f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false'
%strong git clone
%br
%span.descr Slower but makes sure you have a clean dir before every build
%span.descr
Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job
.radio
= f.label :build_allow_git_fetch_true do
= f.radio_button :build_allow_git_fetch, 'true'
%strong git fetch
%br
%span.descr Faster
%span.descr
Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)
%hr
.form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
%p.help-block per build in minutes
%p.help-block
Per job in minutes. If a job passes this threshold, it will be marked as failed.
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout')
%hr
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public pipelines
.help-block
Allow everyone to access pipelines for public and internal projects
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'visibility-of-pipelines')
%hr
.form-group
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group
......@@ -39,8 +65,9 @@
= f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
%span.input-group-addon /
%p.help-block
We will use this regular expression to find test coverage output in build trace.
Leave blank if you want to disable this feature
A regular expression that will be used to find the test coverage
output in the build trace. Leave blank to disable
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'test-coverage-parsing')
.bs-callout.bs-callout-info
%p Below are examples of regex for existing tools:
%ul
......@@ -57,21 +84,9 @@
gcovr (C/C++) -
%code ^TOTAL.*\s+(\d+\%)$
%li
tap --coverage-report=text-summary (Node.js) -
tap --coverage-report=text-summary (NodeJS) -
%code ^Statements\s*:\s*([^%]+)
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public builds
.help-block Allow everyone to access builds traces for Public and Internal projects
.form-group.append-bottom-default
= f.label :runners_token, "Runners token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used to checkout project.
= f.submit 'Save changes', class: "btn btn-save"
%hr
......
......@@ -3,8 +3,9 @@
- assignee = issuable.assignee
- issuable_type = issuable.class.table_name
- base_url_args = [project.namespace.becomes(Namespace), project, issuable_type]
- can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable)
%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) }
%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'ui-sort-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) }
%span
- if show_project_name
%strong #{project.name} &middot;
......
......@@ -91,6 +91,7 @@ module Gitlab
config.assets.precompile << "protected_branches/protected_branches_bundle.js"
config.assets.precompile << "diff_notes/diff_notes_bundle.js"
config.assets.precompile << "boards/boards_bundle.js"
config.assets.precompile << "cycle_analytics/cycle_analytics_bundle.js"
config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js"
config.assets.precompile << "boards/test_utils/simulate_drag.js"
config.assets.precompile << "blob_edit/blob_edit_bundle.js"
......
......@@ -547,6 +547,10 @@ test:
project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
jira:
title: "JIRA"
url: https://sample_company.atlasian.net
project_key: PROJECT
ldap:
enabled: false
servers:
......
......@@ -27,10 +27,25 @@
:namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %>
:queue: email_receiver
:worker: EmailReceiverWorker
<% if config[:sentinels] %>
:sentinels:
<% config[:sentinels].each do |sentinel| %>
-
:host: <%= sentinel[:host] %>
:port: <%= sentinel[:port] %>
<% end %>
<% end %>
:arbitration_method: redis
:arbitration_options:
:redis_url: <%= config[:redis_url].to_json %>
:namespace: <%= Gitlab::Redis::MAILROOM_NAMESPACE %>
<% if config[:sentinels] %>
:sentinels:
<% config[:sentinels].each do |sentinel| %>
-
:host: <%= sentinel[:host] %>
:port: <%= sentinel[:port] %>
<% end %>
<% end %>
<% end %>
......@@ -12,23 +12,26 @@ constraints(GroupUrlConstrainer.new) do
end
end
resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } do
member do
get :issues
get :merge_requests
get :projects
get :activity
end
scope module: :groups do
resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
scope constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } do
resources :groups, except: [:show] do
member do
get :issues
get :merge_requests
get :projects
get :activity
end
resource :avatar, only: [:destroy]
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create]
scope module: :groups do
resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
end
resources :labels, except: [:show], constraints: { id: /\d+/ }
resource :avatar, only: [:destroy]
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create]
resources :labels, except: [:show], constraints: { id: /\d+/ }
end
end
get 'groups/:id' => 'groups#show', as: :group_canonical
end
class RemoveInactiveJiraServiceProperties < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = "Removes all inactive jira_service properties"
def up
execute("UPDATE services SET properties = '{}' WHERE services.type = 'JiraService' and services.active = false")
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLockVersionToBuildAndPipelines < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def change
add_column :ci_builds, :lock_version, :integer
add_column :ci_commits, :lock_version, :integer
end
end
class MigrateJiraToGem < ActiveRecord::Migration
DOWNTIME = true
DOWNTIME_REASON = <<-HEREDOC
Refactor all Jira services properties(serialized field) to use new jira-ruby gem.
There were properties on old Jira service that are not needed anymore after the
service refactoring: api_url, project_url, new_issue_url, issues_url.
We extract the new necessary some properties from old keys and delete them:
taking project_key from project_url and url from api_url
HEREDOC
def up
active_services_query = "SELECT id, properties FROM services WHERE services.type IN ('JiraService') AND services.active = true"
select_all(active_services_query).each do |service|
id = service['id']
properties = JSON.parse(service['properties'])
properties_was = properties.clone
# Migrate `project_url` to `project_key`
# Ignore if `project_url` doesn't have jql project query with project key
if properties['project_url'].present?
jql = properties['project_url'].match('project=([A-Za-z]*)')
properties['project_key'] = jql.captures.first if jql
end
# Migrate `api_url` to `url`
if properties['api_url'].present?
url = properties['api_url'].match('(.*)\/rest\/api')
properties['url'] = url.captures.first if url
end
# Delete now unnecessary properties
properties.delete('api_url')
properties.delete('project_url')
properties.delete('new_issue_url')
properties.delete('issues_url')
# Update changes properties
if properties != properties_was
execute("UPDATE services SET properties = '#{quote_string(properties.to_json)}' WHERE id = #{id}")
end
end
end
def down
active_services_query = "SELECT id, properties FROM services WHERE services.type IN ('JiraService') AND services.active = true"
select_all(active_services_query).each do |service|
id = service['id']
properties = JSON.parse(service['properties'])
properties_was = properties.clone
# Rebuild old properties based on sane defaults
if properties['url'].present?
properties['api_url'] = "#{properties['url']}/rest/api/2"
properties['project_url'] =
"#{properties['url']}/issues/?jql=project=#{properties['project_key']}"
properties['issues_url'] = "#{properties['url']}/browse/:id"
properties['new_issue_url'] = "#{properties['url']}/secure/CreateIssue.jspa"
end
# Delete the new properties
properties.delete('url')
properties.delete('project_key')
# Update changes properties
if properties != properties_was
execute("UPDATE services SET properties = '#{quote_string(properties.to_json)}' WHERE id = #{id}")
end
end
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161024042317) do
ActiveRecord::Schema.define(version: 20161025231710) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -189,6 +189,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do
t.text "yaml_variables"
t.datetime "queued_at"
t.string "token"
t.integer "lock_version"
end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
......@@ -219,6 +220,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do
t.datetime "finished_at"
t.integer "duration"
t.integer "user_id"
t.integer "lock_version"
end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
......
......@@ -2,7 +2,7 @@
## List all deploy keys
Get a list of all deploy keys across all projects.
Get a list of all deploy keys across all projects of the GitLab instance. This endpoint requires admin access.
```
GET /deploy_keys
......
......@@ -19,4 +19,5 @@
- [Build permissions](../user/permissions.md#build-permissions)
- [API](../api/ci/README.md)
- [CI services (linked docker containers)](services/README.md)
- [CI/CD pipelines settings](../user/project/pipelines/settings.md)
- [**New CI build permissions model**](../user/project/new_ci_build_permissions_model.md) Read about what changed in GitLab 8.12 and how that affects your builds. There's a new way to access your Git submodules and LFS objects in builds.
......@@ -5,9 +5,9 @@ Introduced in GitLab 8.8.
## Pipelines
A pipeline is a group of [builds] that get executed in [stages] \(batches). All
of the builds in a stage are executed in parallel (if there are enough
concurrent [runners]), and if they all succeed, the pipeline moves on to the
A pipeline is a group of [builds][] that get executed in [stages][](batches).
All of the builds in a stage are executed in parallel (if there are enough
concurrent [Runners]), and if they all succeed, the pipeline moves on to the
next stage. If one of the builds fails, the next stage is not (usually)
executed.
......@@ -25,8 +25,8 @@ See full [documentation](yaml/README.md#jobs).
## Seeing pipeline status
You can find the current and historical pipeline runs under **Pipelines** for your
project.
You can find the current and historical pipeline runs under **Pipelines** for
your project.
## Seeing build status
......@@ -36,42 +36,11 @@ cancel the build, retry it, or erase the build trace.
## Badges
There are build status and test coverage report badges available.
Go to pipeline settings to see available badges and code you can use to embed
badges in the `README.md` or your website.
### Build status badge
You can access a build status badge image using following link:
```
http://example.gitlab.com/namespace/project/badges/branch/build.svg
```
### Test coverage report badge
GitLab makes it possible to define the regular expression for coverage report,
that each build log will be matched against. This means that each build in the
pipeline can have the test coverage percentage value defined.
You can access test coverage badge using following link:
```
http://example.gitlab.com/namespace/project/badges/branch/coverage.svg
```
If you would like to get the coverage report from the specific job, you can add
a `job=coverage_job_name` parameter to the URL. For example, it is possible to
use following Markdown code to embed the est coverage report into `README.md`:
```markdown
![coverage](http://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
```
The latest successful pipeline will be used to read the test coverage value.
Build status and test coverage report badges are available. You can find their
respective link in the [Pipelines settings] page.
[builds]: #builds
[jobs]: yaml/README.md#jobs
[stages]: yaml/README.md#stages
[runners]: runners/README.md
[runners]: runners/READM
[pipelines settings]: ../user/project/pipelines/settings.md
......@@ -93,6 +93,8 @@ merge request.
links shift too, which eventually leads to dead links. If you think it is
compelling to add numbers in headings, make sure to at least discuss it with
someone in the Merge Request
- Avoid adding things that show ephemeral statuses. For example, if a feature is
considered beta or experimental, put this info in a note, not in the heading.
- When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`,
......@@ -466,4 +468,4 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain
[doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation"
[ce-3349]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3349 "Documentation restructure"
[graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/d8d39f4a87b90fb9ae89ca12dc565347b4900d5e/production/resources/gitlab-map.graffle
[gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
\ No newline at end of file
[gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
......@@ -185,6 +185,20 @@ again in the future.
See [the Testing Standards and Style Guidelines](testing.md) for more
information.
### Running frontend tests
`rake teaspoon` runs the frontend-only (JavaScript) tests.
It consists of two subtasks:
- `rake teaspoon:fixtures` (re-)generates fixtures
- `rake teaspoon:tests` actually executes the tests
As long as the fixtures don't change, `rake teaspoon:tests` is sufficient
(and saves you some time).
Please note: Not all of the frontend fixtures are generated. Some are still static
files. These will not be touched by `rake teaspoon:fixtures`.
## Supported browsers
For our currently-supported browsers, see our [requirements][requirements].
......
......@@ -5,7 +5,7 @@ trackers and external authentication.
See the documentation below for details on how to configure these services.
- [Jira](../project_services/jira.md) Integrate with the JIRA issue tracker
- [JIRA](jira.md) Integrate with the JIRA issue tracker
- [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
- [LDAP](ldap.md) Set up sign in via LDAP
- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd and Azure
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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