Commit f6ededfa authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into sh-bring-back-option-to-be-notified-of-own-activity

parents c4c37311 60853843
{
"presets": [
["latest", { "es2015": { "modules": false } }],
"stage-2"
],
"env": {
"coverage": {
"plugins": [
["istanbul", {
"exclude": [
"app/assets/javascripts/droplab/**/*",
"spec/javascripts/**/*"
]
}],
["transform-define", {
"process.env.BABEL_ENV": "coverage"
}]
]
}
}
}
...@@ -277,6 +277,8 @@ rake karma: ...@@ -277,6 +277,8 @@ rake karma:
stage: test stage: test
<<: *use-db <<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
variables:
BABEL_ENV: "coverage"
script: script:
- bundle exec rake karma - bundle exec rake karma
artifacts: artifacts:
...@@ -389,9 +391,11 @@ trigger_docs: ...@@ -389,9 +391,11 @@ trigger_docs:
cache: {} cache: {}
artifacts: {} artifacts: {}
script: script:
- "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=ce https://gitlab.com/api/v3/projects/1794617/trigger/builds" - "HTTP_STATUS=$(curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=${CI_PROJECT_NAME} --silent --output curl.log --write-out '%{http_code}' https://gitlab.com/api/v3/projects/1794617/trigger/builds)"
- if [ "${HTTP_STATUS}" -ne "201" ]; then echo "Error ${HTTP_STATUS}"; cat curl.log; echo; exit 1; fi
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
# Notify slack in the end # Notify slack in the end
notify:slack: notify:slack:
......
...@@ -304,7 +304,7 @@ GEM ...@@ -304,7 +304,7 @@ GEM
multi_json (~> 1.10) multi_json (~> 1.10)
retriable (~> 1.4) retriable (~> 1.4)
signet (~> 0.6) signet (~> 0.6)
google-protobuf (3.2.0) google-protobuf (3.2.0.2)
googleauth (0.5.1) googleauth (0.5.1)
faraday (~> 0.9) faraday (~> 0.9)
jwt (~> 1.4) jwt (~> 1.4)
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
if (this.activeTab === 'selected') { if (this.activeTab === 'selected') {
obj.title = 'You haven\'t selected any issues yet'; obj.title = 'You haven\'t selected any issues yet';
obj.content = ` obj.content = `
Go back to <strong>All issues</strong> and select some issues Go back to <strong>Open issues</strong> and select some issues
to add to your board. to add to your board.
`; `;
} }
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
class="btn btn-default" class="btn btn-default"
@click="changeTab('all')" @click="changeTab('all')"
v-if="activeTab === 'selected'"> v-if="activeTab === 'selected'">
All issues Open issues
</button> </button>
</div> </div>
</div> </div>
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
href="#" href="#"
role="button" role="button"
@click.prevent="changeTab('all')"> @click.prevent="changeTab('all')">
All issues Open issues
<span class="badge"> <span class="badge">
{{ issuesCount }} {{ issuesCount }}
</span> </span>
......
import PrometheusGraph from './monitoring/prometheus_graph'; // TODO: Maybe Make this a bundle
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
/* global UsernameValidator */ /* global UsernameValidator */
/* global ActiveTabMemoizer */ /* global ActiveTabMemoizer */
...@@ -329,8 +328,6 @@ const UserCallout = require('./user_callout'); ...@@ -329,8 +328,6 @@ const UserCallout = require('./user_callout');
case 'ci:lints:show': case 'ci:lints:show':
new gl.CILintEditor(); new gl.CILintEditor();
break; break;
case 'projects:environments:metrics':
new PrometheusGraph();
case 'users:show': case 'users:show':
new UserCallout(); new UserCallout();
break; break;
......
...@@ -132,7 +132,7 @@ class DueDateSelect { ...@@ -132,7 +132,7 @@ class DueDateSelect {
const selectedDateValue = this.datePayload[this.abilityName].due_date; const selectedDateValue = this.datePayload[this.abilityName].due_date;
const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value'; const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
this.$loading.fadeIn(); this.$loading.removeClass('hidden').fadeIn();
if (isDropdown) { if (isDropdown) {
this.$dropdown.trigger('loading.gl.dropdown'); this.$dropdown.trigger('loading.gl.dropdown');
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true); gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
} }
this.resetFilters();
this.dismissDropdown(); this.dismissDropdown();
this.dispatchInputEvent(); this.dispatchInputEvent();
} }
...@@ -107,7 +108,7 @@ ...@@ -107,7 +108,7 @@
const hook = this.getCurrentHook(); const hook = this.getCurrentHook();
if (hook) { if (hook) {
const data = hook.list.data; const data = hook.list.data || [];
const results = data.map((o) => { const results = data.map((o) => {
const updated = o; const updated = o;
updated.droplab_hidden = false; updated.droplab_hidden = false;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global Flash */ /* global Flash */
require('./flash'); require('./flash');
require('~/lib/utils/text_utility');
require('vendor/jquery.waitforimages'); require('vendor/jquery.waitforimages');
require('./task_list'); require('./task_list');
...@@ -50,20 +51,21 @@ class Issue { ...@@ -50,20 +51,21 @@ class Issue {
success: function(data, textStatus, jqXHR) { success: function(data, textStatus, jqXHR) {
if ('id' in data) { if ('id' in data) {
$(document).trigger('issuable:change'); $(document).trigger('issuable:change');
const currentTotal = Number($('.issue_counter').text()); let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) { if (isClose) {
$('a.btn-close').addClass('hidden'); $('a.btn-close').addClass('hidden');
$('a.btn-reopen').removeClass('hidden'); $('a.btn-reopen').removeClass('hidden');
$('div.status-box-closed').removeClass('hidden'); $('div.status-box-closed').removeClass('hidden');
$('div.status-box-open').addClass('hidden'); $('div.status-box-open').addClass('hidden');
$('.issue_counter').text(currentTotal - 1); total -= 1;
} else { } else {
$('a.btn-reopen').addClass('hidden'); $('a.btn-reopen').addClass('hidden');
$('a.btn-close').removeClass('hidden'); $('a.btn-close').removeClass('hidden');
$('div.status-box-closed').addClass('hidden'); $('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden'); $('div.status-box-open').removeClass('hidden');
$('.issue_counter').text(currentTotal + 1); total += 1;
} }
$('.issue_counter').text(gl.text.addDelimiter(total));
} else { } else {
new Flash(issueFailMessage, 'alert'); new Flash(issueFailMessage, 'alert');
} }
......
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
if (!selected.length) { if (!selected.length) {
data[abilityName].label_ids = ['']; data[abilityName].label_ids = [''];
} }
$loading.fadeIn(); $loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown'); $dropdown.trigger('loading.gl.dropdown');
return $.ajax({ return $.ajax({
type: 'PUT', type: 'PUT',
......
...@@ -159,7 +159,7 @@ ...@@ -159,7 +159,7 @@
} }
$dropdown.trigger('loading.gl.dropdown'); $dropdown.trigger('loading.gl.dropdown');
$loading.fadeIn(); $loading.removeClass('hidden').fadeIn();
gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
.then(function () { .then(function () {
...@@ -171,7 +171,7 @@ ...@@ -171,7 +171,7 @@
data = {}; data = {};
data[abilityName] = {}; data[abilityName] = {};
data[abilityName].milestone_id = selected != null ? selected : null; data[abilityName].milestone_id = selected != null ? selected : null;
$loading.fadeIn(); $loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown'); $dropdown.trigger('loading.gl.dropdown');
return $.ajax({ return $.ajax({
type: 'PUT', type: 'PUT',
......
import PrometheusGraph from './prometheus_graph';
document.addEventListener('DOMContentLoaded', function onLoad() {
document.removeEventListener('DOMContentLoaded', onLoad, false);
return new PrometheusGraph();
}, false);
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
/* global Flash */ /* global Flash */
import d3 from 'd3'; import d3 from 'd3';
import _ from 'underscore';
import statusCodes from '~/lib/utils/http_status'; import statusCodes from '~/lib/utils/http_status';
import '~/lib/utils/common_utils'; import '../lib/utils/common_utils';
import '~/flash'; import '../flash';
const prometheusGraphsContainer = '.prometheus-graph'; const prometheusGraphsContainer = '.prometheus-graph';
const metricsEndpoint = 'metrics.json'; const metricsEndpoint = 'metrics.json';
...@@ -31,22 +30,21 @@ class PrometheusGraph { ...@@ -31,22 +30,21 @@ class PrometheusGraph {
} }
createGraph() { createGraph() {
const self = this; Object.keys(this.data).forEach((key) => {
_.each(this.data, (value, key) => { const value = this.data[key];
if (value.length > 0 && (key === 'cpu_values' || key === 'memory_values')) { if (value.length > 0) {
self.plotValues(value, key); this.plotValues(value, key);
} }
}); });
} }
init() { init() {
const self = this;
this.getData().then((metricsResponse) => { this.getData().then((metricsResponse) => {
if (metricsResponse === {}) { if (Object.keys(metricsResponse).length === 0) {
new Flash('Empty metrics', 'alert'); new Flash('Empty metrics', 'alert');
} else { } else {
self.transformData(metricsResponse); this.transformData(metricsResponse);
self.createGraph(); this.createGraph();
} }
}); });
} }
...@@ -321,12 +319,14 @@ class PrometheusGraph { ...@@ -321,12 +319,14 @@ class PrometheusGraph {
transformData(metricsResponse) { transformData(metricsResponse) {
const metricTypes = {}; const metricTypes = {};
_.each(metricsResponse.metrics, (value, key) => { Object.keys(metricsResponse.metrics).forEach((key) => {
const metricValues = value[0].values; if (key === 'cpu_values' || key === 'memory_values') {
metricTypes[key] = _.map(metricValues, metric => ({ const metricValues = (metricsResponse.metrics[key])[0];
time: new Date(metric[0] * 1000), metricTypes[key] = metricValues.values.map(metric => ({
value: metric[1], time: new Date(metric[0] * 1000),
})); value: metric[1],
}));
}
}); });
this.data = metricTypes; this.data = metricTypes;
} }
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
$loading = $block.find('.block-loading').fadeOut(); $loading = $block.find('.block-loading').fadeOut();
var updateIssueBoardsIssue = function () { var updateIssueBoardsIssue = function () {
$loading.fadeIn(); $loading.removeClass('hidden').fadeIn();
gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
.then(function () { .then(function () {
$loading.fadeOut(); $loading.fadeOut();
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
data = {}; data = {};
data[abilityName] = {}; data[abilityName] = {};
data[abilityName].assignee_id = selected != null ? selected : null; data[abilityName].assignee_id = selected != null ? selected : null;
$loading.fadeIn(); $loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown'); $dropdown.trigger('loading.gl.dropdown');
return $.ajax({ return $.ajax({
type: 'PUT', type: 'PUT',
......
...@@ -429,3 +429,9 @@ table { ...@@ -429,3 +429,9 @@ table {
@include str-truncated(100%); @include str-truncated(100%);
} }
} }
.tooltip {
.tooltip-inner {
word-wrap: break-word;
}
}
...@@ -76,12 +76,14 @@ ...@@ -76,12 +76,14 @@
} }
.input-token { .input-token {
flex: 1; max-width: 200px;
-webkit-flex: 1;
} }
.filtered-search-token + .input-token:not(:last-child) { .input-token:only-child,
max-width: 200px; .input-token:last-child {
flex: 1;
-webkit-flex: 1;
max-width: initial;
} }
} }
...@@ -158,8 +160,8 @@ ...@@ -158,8 +160,8 @@
background-color: $white-light; background-color: $white-light;
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
-webkit-flex: 1 1 100%; -webkit-flex: 1 1 auto;
flex: 1 1 100%; flex: 1 1 auto;
margin-bottom: 10px; margin-bottom: 10px;
.dropdown-menu { .dropdown-menu {
...@@ -221,6 +223,10 @@ ...@@ -221,6 +223,10 @@
.filter-dropdown-container { .filter-dropdown-container {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
.dropdown-toggle {
line-height: 22px;
}
} }
.dropdown-menu .filter-dropdown-item { .dropdown-menu .filter-dropdown-item {
...@@ -246,7 +252,9 @@ ...@@ -246,7 +252,9 @@
background-color: $white-light; background-color: $white-light;
border-top: 0; border-top: 0;
} }
}
@media (max-width: $screen-xs) {
.filter-dropdown-container { .filter-dropdown-container {
.dropdown-toggle, .dropdown-toggle,
.dropdown { .dropdown {
......
...@@ -306,6 +306,11 @@ a > code { ...@@ -306,6 +306,11 @@ a > code {
* Textareas intended for GFM * Textareas intended for GFM
* *
*/ */
textarea.js-gfm-input {
font-family: $monospace_font;
font-size: 13px;
}
.strikethrough { .strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }
......
...@@ -148,6 +148,18 @@ ...@@ -148,6 +148,18 @@
.error-alert > .alert { .error-alert > .alert {
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
&.alert-dismissable {
.close {
color: $white-light;
opacity: 0.85;
font-weight: normal;
&:hover {
opacity: 1;
}
}
}
} }
.discussion-body, .discussion-body,
......
...@@ -37,7 +37,6 @@ module ServiceParams ...@@ -37,7 +37,6 @@ module ServiceParams
:namespace, :namespace,
:new_issue_url, :new_issue_url,
:notify, :notify,
:notify_only_broken_builds,
:notify_only_broken_pipelines, :notify_only_broken_pipelines,
:password, :password,
:priority, :priority,
......
...@@ -51,7 +51,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -51,7 +51,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private private
def find_todos def find_todos
@todos ||= TodosFinder.new(current_user, params).execute @todos ||= TodosFinder.new(current_user, params.merge(include_associations: true)).execute
end end
def todos_counts def todos_counts
......
...@@ -6,6 +6,8 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -6,6 +6,8 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableCollections include IssuableCollections
include SpammableActions include SpammableActions
prepend_before_action :authenticate_user!, only: [:new]
before_action :redirect_to_external_issue_tracker, only: [:index, :new] before_action :redirect_to_external_issue_tracker, only: [:index, :new]
before_action :module_enabled before_action :module_enabled
before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests, before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
......
...@@ -45,8 +45,9 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -45,8 +45,9 @@ class Projects::WikisController < Projects::ApplicationController
return render('empty') unless can?(current_user, :create_wiki, @project) return render('empty') unless can?(current_user, :create_wiki, @project)
@page = @project_wiki.find_page(params[:id]) @page = @project_wiki.find_page(params[:id])
@page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page) if @page.valid?
redirect_to( redirect_to(
namespace_project_wiki_path(@project.namespace, @project, @page), namespace_project_wiki_path(@project.namespace, @project, @page),
notice: 'Wiki was successfully updated.' notice: 'Wiki was successfully updated.'
......
...@@ -24,6 +24,7 @@ class TodosFinder ...@@ -24,6 +24,7 @@ class TodosFinder
def execute def execute
items = current_user.todos items = current_user.todos
items = include_associations(items)
items = by_action_id(items) items = by_action_id(items)
items = by_action(items) items = by_action(items)
items = by_author(items) items = by_author(items)
...@@ -38,6 +39,17 @@ class TodosFinder ...@@ -38,6 +39,17 @@ class TodosFinder
private private
def include_associations(items)
return items unless params[:include_associations]
items.includes(
[
target: { project: [:route, namespace: :route] },
author: { namespace: :route },
]
)
end
def action_id? def action_id?
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i) action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end end
......
...@@ -16,6 +16,7 @@ module NavHelper ...@@ -16,6 +16,7 @@ module NavHelper
"page-gutter build-sidebar right-sidebar-expanded" "page-gutter build-sidebar right-sidebar-expanded"
elsif current_path?('wikis#show') || elsif current_path?('wikis#show') ||
current_path?('wikis#edit') || current_path?('wikis#edit') ||
current_path?('wikis#update') ||
current_path?('wikis#history') || current_path?('wikis#history') ||
current_path?('wikis#git_access') current_path?('wikis#git_access')
"page-gutter wiki-sidebar right-sidebar-expanded" "page-gutter wiki-sidebar right-sidebar-expanded"
......
...@@ -39,9 +39,13 @@ module TodosHelper ...@@ -39,9 +39,13 @@ module TodosHelper
namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project, namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project,
todo.target, anchor: anchor) todo.target, anchor: anchor)
else else
path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target] if todo.build_failed?
# associated namespace and route would be loaded from the db again if todo.project was used
path.unshift(:pipelines) if todo.build_failed? project = todo.target.project
path = [:pipelines, project.namespace.becomes(Namespace), project, todo.target]
else
path = [todo.target]
end
polymorphic_path(path, anchor: anchor) polymorphic_path(path, anchor: anchor)
end end
......
module Emails
module Builds
def build_fail_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
add_project_headers
add_build_headers('failed')
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
end
def build_success_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
add_project_headers
add_build_headers('success')
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
end
private
def add_build_headers(status)
headers['X-GitLab-Build-Id'] = @build.id
headers['X-GitLab-Build-Ref'] = @build.ref
headers['X-GitLab-Build-Status'] = status.to_s
end
end
end
...@@ -6,7 +6,6 @@ class Notify < BaseMailer ...@@ -6,7 +6,6 @@ class Notify < BaseMailer
include Emails::Notes include Emails::Notes
include Emails::Projects include Emails::Projects
include Emails::Profile include Emails::Profile
include Emails::Builds
include Emails::Pipelines include Emails::Pipelines
include Emails::Members include Emails::Members
......
...@@ -15,7 +15,7 @@ module Ci ...@@ -15,7 +15,7 @@ module Ci
def persisted_environment def persisted_environment
@persisted_environment ||= Environment.find_by( @persisted_environment ||= Environment.find_by(
name: expanded_environment_name, name: expanded_environment_name,
project_id: gl_project_id project: project
) )
end end
...@@ -223,7 +223,8 @@ module Ci ...@@ -223,7 +223,8 @@ module Ci
def merge_request def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff) merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref, source_project_id: pipeline.gl_project_id) .where(source_branch: ref,
source_project: pipeline.project)
.reorder(iid: :asc) .reorder(iid: :asc)
merge_requests.find do |merge_request| merge_requests.find do |merge_request|
...@@ -231,10 +232,6 @@ module Ci ...@@ -231,10 +232,6 @@ module Ci
end end
end end
def project_id
gl_project_id
end
def repo_url def repo_url
auth = "gitlab-ci-token:#{ensure_token!}@" auth = "gitlab-ci-token:#{ensure_token!}@"
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix| project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
...@@ -542,6 +539,16 @@ module Ci ...@@ -542,6 +539,16 @@ module Ci
Gitlab::Ci::Build::Credentials::Factory.new(self).create! Gitlab::Ci::Build::Credentials::Factory.new(self).create!
end end
def dependencies
depended_jobs = depends_on_builds
return depended_jobs unless options[:dependencies].present?
depended_jobs.select do |job|
options[:dependencies].include?(job.name)
end
end
private private
def update_artifacts_size def update_artifacts_size
...@@ -561,7 +568,7 @@ module Ci ...@@ -561,7 +568,7 @@ module Ci
end end
def unscoped_project def unscoped_project
@unscoped_project ||= Project.unscoped.find_by(id: gl_project_id) @unscoped_project ||= Project.unscoped.find_by(id: project_id)
end end
CI_REGISTRY_USER = 'gitlab-ci-token'.freeze CI_REGISTRY_USER = 'gitlab-ci-token'.freeze
......
...@@ -5,9 +5,7 @@ module Ci ...@@ -5,9 +5,7 @@ module Ci
include Importable include Importable
include AfterCommitQueue include AfterCommitQueue
self.table_name = 'ci_commits' belongs_to :project
belongs_to :project, foreign_key: :gl_project_id
belongs_to :user belongs_to :user
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
......
...@@ -9,7 +9,7 @@ module Ci ...@@ -9,7 +9,7 @@ module Ci
has_many :builds has_many :builds
has_many :runner_projects, dependent: :destroy has_many :runner_projects, dependent: :destroy
has_many :projects, through: :runner_projects, foreign_key: :gl_project_id has_many :projects, through: :runner_projects
has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build'
...@@ -24,7 +24,7 @@ module Ci ...@@ -24,7 +24,7 @@ module Ci
scope :owned_or_shared, ->(project_id) do scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id') joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) .where("ci_runner_projects.project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
end end
scope :assignable_for, ->(project) do scope :assignable_for, ->(project) do
......
module Ci module Ci
class RunnerProject < ActiveRecord::Base class RunnerProject < ActiveRecord::Base
extend Ci::Model extend Ci::Model
belongs_to :runner belongs_to :runner
belongs_to :project, foreign_key: :gl_project_id belongs_to :project
validates :runner_id, uniqueness: { scope: :gl_project_id } validates :runner_id, uniqueness: { scope: :project_id }
end end
end end
...@@ -4,7 +4,7 @@ module Ci ...@@ -4,7 +4,7 @@ module Ci
acts_as_paranoid acts_as_paranoid
belongs_to :project, foreign_key: :gl_project_id belongs_to :project
belongs_to :owner, class_name: "User" belongs_to :owner, class_name: "User"
has_many :trigger_requests, dependent: :destroy has_many :trigger_requests, dependent: :destroy
......
...@@ -2,11 +2,11 @@ module Ci ...@@ -2,11 +2,11 @@ module Ci
class Variable < ActiveRecord::Base class Variable < ActiveRecord::Base
extend Ci::Model extend Ci::Model
belongs_to :project, foreign_key: :gl_project_id belongs_to :project
validates :key, validates :key,
presence: true, presence: true,
uniqueness: { scope: :gl_project_id }, uniqueness: { scope: :project_id },
length: { maximum: 255 }, length: { maximum: 255 },
format: { with: /\A[a-zA-Z0-9_]+\z/, format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." } message: "can contain only letters, digits and '_'." }
......
...@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds' self.table_name = 'ci_builds'
belongs_to :project, foreign_key: :gl_project_id belongs_to :project
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :user belongs_to :user
...@@ -133,6 +133,12 @@ class CommitStatus < ActiveRecord::Base ...@@ -133,6 +133,12 @@ class CommitStatus < ActiveRecord::Base
false false
end end
# Added in 9.0 to keep backward compatibility for projects exported in 8.17
# and prior.
def gl_project_id
'dummy'
end
def detailed_status(current_user) def detailed_status(current_user)
Gitlab::Ci::Status::Factory Gitlab::Ci::Status::Factory
.new(self, current_user) .new(self, current_user)
......
...@@ -51,11 +51,13 @@ module Routable ...@@ -51,11 +51,13 @@ module Routable
paths.each do |path| paths.each do |path|
path = connection.quote(path) path = connection.quote(path)
where = "(routes.path = #{path})"
if cast_lower where =
where = "(#{where} OR (LOWER(routes.path) = LOWER(#{path})))" if cast_lower
end "(LOWER(routes.path) = LOWER(#{path}))"
else
"(routes.path = #{path})"
end
wheres << where wheres << where
end end
......
...@@ -55,6 +55,14 @@ class Issue < ActiveRecord::Base ...@@ -55,6 +55,14 @@ class Issue < ActiveRecord::Base
state :opened state :opened
state :reopened state :reopened
state :closed state :closed
before_transition any => :closed do |issue|
issue.closed_at = Time.zone.now
end
before_transition closed: any do |issue|
issue.closed_at = nil
end
end end
def hook_attrs def hook_attrs
......
...@@ -7,6 +7,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -7,6 +7,7 @@ class MergeRequest < ActiveRecord::Base
belongs_to :target_project, class_name: "Project" belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project" belongs_to :source_project, class_name: "Project"
belongs_to :project, foreign_key: :target_project_id
belongs_to :merge_user, class_name: "User" belongs_to :merge_user, class_name: "User"
has_many :merge_request_diffs, dependent: :destroy has_many :merge_request_diffs, dependent: :destroy
...@@ -540,10 +541,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -540,10 +541,6 @@ class MergeRequest < ActiveRecord::Base
target_project != source_project target_project != source_project
end end
def project
target_project
end
# If the merge request closes any issues, save this information in the # If the merge request closes any issues, save this information in the
# `MergeRequestsClosingIssues` model. This is a performance optimization. # `MergeRequestsClosingIssues` model. This is a performance optimization.
# Calculating this information for a number of merge requests requires # Calculating this information for a number of merge requests requires
......
...@@ -89,7 +89,6 @@ class Project < ActiveRecord::Base ...@@ -89,7 +89,6 @@ class Project < ActiveRecord::Base
has_one :campfire_service, dependent: :destroy has_one :campfire_service, dependent: :destroy
has_one :drone_ci_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy
has_one :emails_on_push_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy
has_one :builds_email_service, dependent: :destroy
has_one :pipelines_email_service, dependent: :destroy has_one :pipelines_email_service, dependent: :destroy
has_one :irker_service, dependent: :destroy has_one :irker_service, dependent: :destroy
has_one :pivotaltracker_service, dependent: :destroy has_one :pivotaltracker_service, dependent: :destroy
...@@ -159,13 +158,13 @@ class Project < ActiveRecord::Base ...@@ -159,13 +158,13 @@ class Project < ActiveRecord::Base
has_one :project_feature, dependent: :destroy has_one :project_feature, dependent: :destroy
has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id has_many :commit_statuses, dependent: :destroy
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline'
has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses has_many :builds, class_name: 'Ci::Build' # the builds are created from the commit_statuses
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
has_many :variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id has_many :variables, dependent: :destroy, class_name: 'Ci::Variable'
has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger'
has_many :environments, dependent: :destroy has_many :environments, dependent: :destroy
has_many :deployments, dependent: :destroy has_many :deployments, dependent: :destroy
......
# This class is to be removed with 9.1
# We should also by then remove BuildsEmailService from database
class BuildsEmailService < Service class BuildsEmailService < Service
prop_accessor :recipients
boolean_accessor :add_pusher
boolean_accessor :notify_only_broken_builds
validates :recipients, presence: true, if: ->(s) { s.activated? && !s.add_pusher? }
def initialize_properties
if properties.nil?
self.properties = {}
self.notify_only_broken_builds = true
end
end
def title
'Builds emails'
end
def description
'Email the builds status to a list of recipients.'
end
def self.to_param def self.to_param
'builds_email' 'builds_email'
end end
def self.supported_events def self.supported_events
%w(build) %w[]
end
def execute(push_data)
return unless supported_events.include?(push_data[:object_kind])
return unless should_build_be_notified?(push_data)
recipients = all_recipients(push_data)
if recipients.any?
BuildEmailWorker.perform_async(
push_data[:build_id],
recipients,
push_data
)
end
end
def can_test?
project.builds.any?
end
def disabled_title
"Please setup a build on your repository."
end
def test_data(project = nil, user = nil)
Gitlab::DataBuilder::Build.build(project.builds.last)
end
def fields
[
{ type: 'textarea', name: 'recipients', placeholder: 'Emails separated by comma' },
{ type: 'checkbox', name: 'add_pusher', label: 'Add pusher to recipients list' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
]
end
def test(data)
begin
# bypass build status verification when testing
data[:build_status] = "failed"
data[:build_allow_failure] = false
result = execute(data)
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result }
end
def should_build_be_notified?(data)
case data[:build_status]
when 'success'
!notify_only_broken_builds?
when 'failed'
!allow_failure?(data)
else
false
end
end
def allow_failure?(data)
data[:build_allow_failure] == true
end
def all_recipients(data)
all_recipients = []
unless recipients.blank?
all_recipients += recipients.split(',').compact.reject(&:blank?)
end
if add_pusher? && data[:user][:email]
all_recipients << data[:user][:email]
end
all_recipients
end end
end end
module ChatMessage
class BuildMessage < BaseMessage
attr_reader :sha
attr_reader :ref_type
attr_reader :ref
attr_reader :status
attr_reader :project_name
attr_reader :project_url
attr_reader :user_name
attr_reader :user_url
attr_reader :duration
attr_reader :stage
attr_reader :build_id
attr_reader :build_name
def initialize(params)
@sha = params[:sha]
@ref_type = params[:tag] ? 'tag' : 'branch'
@ref = params[:ref]
@project_name = params[:project_name]
@project_url = params[:project_url]
@status = params[:commit][:status]
@user_name = params[:commit][:author_name]
@user_url = params[:commit][:author_url]
@duration = params[:commit][:duration]
@stage = params[:build_stage]
@build_name = params[:build_name]
@build_id = params[:build_id]
end
def pretext
''
end
def fallback
format(message)
end
def attachments
[{ text: format(message), color: attachment_color }]
end
private
def message
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_link} #{humanized_status} on build #{build_link} of stage #{stage} in #{duration} #{'second'.pluralize(duration)}"
end
def build_url
"#{project_url}/builds/#{build_id}"
end
def build_link
link(build_name, build_url)
end
def user_link
link(user_name, user_url)
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def humanized_status
case status
when 'success'
'passed'
else
status
end
end
def attachment_color
if status == 'success'
'good'
else
'danger'
end
end
def branch_url
"#{project_url}/commits/#{ref}"
end
def branch_link
link(ref, branch_url)
end
def project_link
link(project_name, project_url)
end
def commit_url
"#{project_url}/commit/#{sha}/builds"
end
def commit_link
link(Commit.truncate_sha(sha), commit_url)
end
end
end
...@@ -6,7 +6,7 @@ class ChatNotificationService < Service ...@@ -6,7 +6,7 @@ class ChatNotificationService < Service
default_value_for :category, 'chat' default_value_for :category, 'chat'
prop_accessor :webhook, :username, :channel prop_accessor :webhook, :username, :channel
boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines boolean_accessor :notify_only_broken_pipelines
validates :webhook, presence: true, url: true, if: :activated? validates :webhook, presence: true, url: true, if: :activated?
...@@ -16,7 +16,6 @@ class ChatNotificationService < Service ...@@ -16,7 +16,6 @@ class ChatNotificationService < Service
if properties.nil? if properties.nil?
self.properties = {} self.properties = {}
self.notify_only_broken_builds = true
self.notify_only_broken_pipelines = true self.notify_only_broken_pipelines = true
end end
end end
...@@ -27,7 +26,7 @@ class ChatNotificationService < Service ...@@ -27,7 +26,7 @@ class ChatNotificationService < Service
def self.supported_events def self.supported_events
%w[push issue confidential_issue merge_request note tag_push %w[push issue confidential_issue merge_request note tag_push
build pipeline wiki_page] pipeline wiki_page]
end end
def execute(data) def execute(data)
...@@ -89,8 +88,6 @@ class ChatNotificationService < Service ...@@ -89,8 +88,6 @@ class ChatNotificationService < Service
ChatMessage::MergeMessage.new(data) unless is_update?(data) ChatMessage::MergeMessage.new(data) unless is_update?(data)
when "note" when "note"
ChatMessage::NoteMessage.new(data) ChatMessage::NoteMessage.new(data)
when "build"
ChatMessage::BuildMessage.new(data) if should_build_be_notified?(data)
when "pipeline" when "pipeline"
ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data) ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
when "wiki_page" when "wiki_page"
...@@ -125,17 +122,6 @@ class ChatNotificationService < Service ...@@ -125,17 +122,6 @@ class ChatNotificationService < Service
data[:object_attributes][:action] == 'update' data[:object_attributes][:action] == 'update'
end end
def should_build_be_notified?(data)
case data[:commit][:status]
when 'success'
!notify_only_broken_builds?
when 'failed'
true
else
false
end
end
def should_pipeline_be_notified?(data) def should_pipeline_be_notified?(data)
case data[:object_attributes][:status] case data[:object_attributes][:status]
when 'success' when 'success'
......
...@@ -9,13 +9,13 @@ class HipchatService < Service ...@@ -9,13 +9,13 @@ class HipchatService < Service
].freeze ].freeze
prop_accessor :token, :room, :server, :color, :api_version prop_accessor :token, :room, :server, :color, :api_version
boolean_accessor :notify_only_broken_builds, :notify boolean_accessor :notify_only_broken_pipelines, :notify
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def initialize_properties def initialize_properties
if properties.nil? if properties.nil?
self.properties = {} self.properties = {}
self.notify_only_broken_builds = true self.notify_only_broken_pipelines = true
end end
end end
...@@ -41,12 +41,12 @@ class HipchatService < Service ...@@ -41,12 +41,12 @@ class HipchatService < Service
placeholder: 'Leave blank for default (v2)' }, placeholder: 'Leave blank for default (v2)' },
{ type: 'text', name: 'server', { type: 'text', name: 'server',
placeholder: 'Leave blank for default. https://hipchat.example.com' }, placeholder: 'Leave blank for default. https://hipchat.example.com' },
{ type: 'checkbox', name: 'notify_only_broken_builds' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' },
] ]
end end
def self.supported_events def self.supported_events
%w(push issue confidential_issue merge_request note tag_push build) %w(push issue confidential_issue merge_request note tag_push pipeline)
end end
def execute(data) def execute(data)
...@@ -90,8 +90,8 @@ class HipchatService < Service ...@@ -90,8 +90,8 @@ class HipchatService < Service
create_merge_request_message(data) unless is_update?(data) create_merge_request_message(data) unless is_update?(data)
when "note" when "note"
create_note_message(data) create_note_message(data)
when "build" when "pipeline"
create_build_message(data) if should_build_be_notified?(data) create_pipeline_message(data) if should_pipeline_be_notified?(data)
end end
end end
...@@ -240,28 +240,29 @@ class HipchatService < Service ...@@ -240,28 +240,29 @@ class HipchatService < Service
message message
end end
def create_build_message(data) def create_pipeline_message(data)
ref_type = data[:tag] ? 'tag' : 'branch' pipeline_attributes = data[:object_attributes]
ref = data[:ref] pipeline_id = pipeline_attributes[:id]
sha = data[:sha] ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
user_name = data[:commit][:author_name] ref = pipeline_attributes[:ref]
status = data[:commit][:status] user_name = (data[:user] && data[:user][:name]) || 'API'
duration = data[:commit][:duration] status = pipeline_attributes[:status]
duration = pipeline_attributes[:duration]
branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>" branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
commit_link = "<a href=\"#{project_url}/commit/#{CGI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>" pipeline_url = "<a href=\"#{project_url}/pipelines/#{pipeline_id}\">##{pipeline_id}</a>"
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)" "#{project_link}: Pipeline #{pipeline_url} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
end end
def message_color(data) def message_color(data)
build_status_color(data) || color || 'yellow' pipeline_status_color(data) || color || 'yellow'
end end
def build_status_color(data) def pipeline_status_color(data)
return unless data && data[:object_kind] == 'build' return unless data && data[:object_kind] == 'pipeline'
case data[:commit][:status] case data[:object_attributes][:status]
when 'success' when 'success'
'green' 'green'
else else
...@@ -294,10 +295,10 @@ class HipchatService < Service ...@@ -294,10 +295,10 @@ class HipchatService < Service
end end
end end
def should_build_be_notified?(data) def should_pipeline_be_notified?(data)
case data[:commit][:status] case data[:object_attributes][:status]
when 'success' when 'success'
!notify_only_broken_builds? !notify_only_broken_pipelines?
when 'failed' when 'failed'
true true
else else
......
...@@ -30,7 +30,6 @@ class MattermostService < ChatNotificationService ...@@ -30,7 +30,6 @@ class MattermostService < ChatNotificationService
[ [
{ type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' }, { type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' },
{ type: 'text', name: 'username', placeholder: 'e.g. GitLab' }, { type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' },
] ]
end end
......
...@@ -29,7 +29,6 @@ class SlackService < ChatNotificationService ...@@ -29,7 +29,6 @@ class SlackService < ChatNotificationService
[ [
{ type: 'text', name: 'webhook', placeholder: 'e.g. https://hooks.slack.com/services/…' }, { type: 'text', name: 'webhook', placeholder: 'e.g. https://hooks.slack.com/services/…' },
{ type: 'text', name: 'username', placeholder: 'e.g. GitLab' }, { type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' },
] ]
end end
......
...@@ -215,7 +215,6 @@ class Service < ActiveRecord::Base ...@@ -215,7 +215,6 @@ class Service < ActiveRecord::Base
assembla assembla
bamboo bamboo
buildkite buildkite
builds_email
bugzilla bugzilla
campfire campfire
custom_issue_tracker custom_issue_tracker
......
...@@ -877,7 +877,7 @@ class User < ActiveRecord::Base ...@@ -877,7 +877,7 @@ class User < ActiveRecord::Base
def ci_authorized_runners def ci_authorized_runners
@ci_authorized_runners ||= begin @ci_authorized_runners ||= begin
runner_ids = Ci::RunnerProject. runner_ids = Ci::RunnerProject.
where("ci_runner_projects.gl_project_id IN (#{ci_projects_union.to_sql})"). where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})").
select(:runner_id) select(:runner_id)
Ci::Runner.specific.where(id: runner_ids) Ci::Runner.specific.where(id: runner_ids)
end end
......
...@@ -155,7 +155,7 @@ class WikiPage ...@@ -155,7 +155,7 @@ class WikiPage
end end
# Returns boolean True or False if this instance # Returns boolean True or False if this instance
# has been fully saved to disk or not. # has been fully created on disk or not.
def persisted? def persisted?
@persisted == true @persisted == true
end end
...@@ -226,6 +226,8 @@ class WikiPage ...@@ -226,6 +226,8 @@ class WikiPage
end end
def save(method, *args) def save(method, *args)
saved = false
project_wiki = wiki project_wiki = wiki
if valid? && project_wiki.send(method, *args) if valid? && project_wiki.send(method, *args)
...@@ -243,10 +245,10 @@ class WikiPage ...@@ -243,10 +245,10 @@ class WikiPage
set_attributes set_attributes
@persisted = true @persisted = true
saved = true
else else
errors.add(:base, project_wiki.error_message) if project_wiki.error_message errors.add(:base, project_wiki.error_message) if project_wiki.error_message
@persisted = false
end end
@persisted saved
end end
end end
...@@ -55,13 +55,13 @@ module Ci ...@@ -55,13 +55,13 @@ module Ci
new_builds. new_builds.
# don't run projects which have not enabled shared runners and builds # don't run projects which have not enabled shared runners and builds
joins(:project).where(projects: { shared_runners_enabled: true }). joins(:project).where(projects: { shared_runners_enabled: true }).
joins('LEFT JOIN project_features ON ci_builds.gl_project_id = project_features.project_id'). joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id').
where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0'). where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
# Implement fair scheduling # Implement fair scheduling
# this returns builds that are ordered by number of running builds # this returns builds that are ordered by number of running builds
# we prefer projects that don't use shared runners at all # we prefer projects that don't use shared runners at all
joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.gl_project_id=project_builds.gl_project_id"). joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id").
order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC') order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
end end
...@@ -71,7 +71,7 @@ module Ci ...@@ -71,7 +71,7 @@ module Ci
def running_builds_for_shared_runners def running_builds_for_shared_runners
Ci::Build.running.where(runner: Ci::Runner.shared). Ci::Build.running.where(runner: Ci::Runner.shared).
group(:gl_project_id).select(:gl_project_id, 'count(*) AS running_builds') group(:project_id).select(:project_id, 'count(*) AS running_builds')
end end
def new_builds def new_builds
......
...@@ -6,7 +6,7 @@ module MergeRequests ...@@ -6,7 +6,7 @@ module MergeRequests
merge_request.source_project = find_source_project merge_request.source_project = find_source_project
merge_request.target_project = find_target_project merge_request.target_project = find_target_project
merge_request.target_branch = find_target_branch merge_request.target_branch = find_target_branch
merge_request.can_be_created = branches_valid? && source_branch_specified? && target_branch_specified? merge_request.can_be_created = branches_valid?
compare_branches if branches_present? compare_branches if branches_present?
assign_title_and_description if merge_request.can_be_created assign_title_and_description if merge_request.can_be_created
......
- content_for :header do
%h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
GitLab (job failed)
%h3
Project:
= link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
%p
Author: #{@build.pipeline.git_author_name}
%p
Branch: #{@build.ref}
%p
Stage: #{@build.stage}
%p
Job: #{@build.name}
%p
Message: #{@build.pipeline.git_commit_message}
%p
Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
Job failed for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.pipeline.short_sha %>
Author: <%= @build.pipeline.git_author_name %>
Branch: <%= @build.ref %>
Stage: <%= @build.stage %>
Job: <%= @build.name %>
Message: <%= @build.pipeline.git_commit_message %>
Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>
- content_for :header do
%h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
GitLab (job successful)
%h3
Project:
= link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
%p
Author: #{@build.pipeline.git_author_name}
%p
Branch: #{@build.ref}
%p
Stage: #{@build.stage}
%p
Job: #{@build.name}
%p
Message: #{@build.pipeline.git_commit_message}
%p
Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
Job successful for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.pipeline.short_sha %>
Author: <%= @build.pipeline.git_author_name %>
Branch: <%= @build.ref %>
Stage: <%= @build.stage %>
Job: <%= @build.name %>
Message: <%= @build.pipeline.git_commit_message %>
Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>
- @no_container = true - @no_container = true
- page_title "Metrics for environment", @environment.name - page_title "Metrics for environment", @environment.name
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('monitoring')
= render "projects/pipelines/head" = render "projects/pipelines/head"
%div{ class: container_class } %div{ class: container_class }
......
...@@ -19,15 +19,14 @@ ...@@ -19,15 +19,14 @@
.nav-controls .nav-controls
= link_to params.merge(rss_url_options), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do = link_to params.merge(rss_url_options), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
= icon('rss') = icon('rss')
- if can? current_user, :create_issue, @project = link_to new_namespace_project_issue_path(@project.namespace,
= link_to new_namespace_project_issue_path(@project.namespace, @project,
@project, issue: { assignee_id: issues_finder.assignee.try(:id),
issue: { assignee_id: issues_finder.assignee.try(:id), milestone_id: issues_finder.milestones.first.try(:id) }),
milestone_id: issues_finder.milestones.first.try(:id) }), class: "btn btn-new",
class: "btn btn-new", title: "New Issue",
title: "New Issue", id: "new_issue_link" do
id: "new_issue_link" do New Issue
New Issue
= render 'shared/issuable/search_bar', type: :issues = render 'shared/issuable/search_bar', type: :issues
.issues-holder .issues-holder
......
...@@ -20,37 +20,34 @@ ...@@ -20,37 +20,34 @@
= confidential_icon(@issue) = confidential_icon(@issue)
= issuable_meta(@issue, @project, "Issue") = issuable_meta(@issue, @project, "Issue")
- if can?(current_user, :create_issue, @project) || can?(current_user, :update_issue, @issue) .issuable-actions
.issuable-actions .clearfix.issue-btn-group.dropdown
.clearfix.issue-btn-group.dropdown %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } }
%button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } } Options
Options = icon('caret-down')
= icon('caret-down') .dropdown-menu.dropdown-menu-align-right.hidden-lg
.dropdown-menu.dropdown-menu-align-right.hidden-lg %ul
%ul %li
- if can?(current_user, :create_issue, @project) = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link'
%li - if can?(current_user, :update_issue, @issue)
= link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' %li
- if can?(current_user, :update_issue, @issue) = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
%li %li
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%li %li
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
%li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
- if @issue.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
- if can?(current_user, :create_issue, @project)
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do
New issue
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- if @issue.submittable_as_spam_by?(current_user) - if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam' %li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do
New issue
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
.issue-details.issuable-details .issue-details.issuable-details
......
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } } %button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } }
%span.light
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
- else - else
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
Also, issues are searchable and filterable. Also, issues are searchable and filterable.
- if project_select_button - if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue' = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
- else
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
- else - else
%h4.text-center There are no issues to show. %h4 There are no issues to show.
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
= icon('user', 'aria-hidden': 'true') = icon('user', 'aria-hidden': 'true')
.title.hide-collapsed .title.hide-collapsed
Assignee Assignee
= icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right' = link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed .value.hide-collapsed
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
None None
.title.hide-collapsed .title.hide-collapsed
Milestone Milestone
= icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right' = link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed .value.hide-collapsed
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
= issuable.due_date.try(:to_s, :medium) || 'None' = issuable.due_date.try(:to_s, :medium) || 'None'
.title.hide-collapsed .title.hide-collapsed
Due date Due date
= icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= link_to 'Edit', '#', class: 'edit-link pull-right' = link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed .value.hide-collapsed
...@@ -121,12 +121,12 @@ ...@@ -121,12 +121,12 @@
- selected_labels = issuable.labels - selected_labels = issuable.labels
.block.labels .block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } } .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } }
= icon('tags', 'aria-hidden': 'true') = icon('tags', class: 'hidden', 'aria-hidden': 'true')
%span %span
= selected_labels.size = selected_labels.size
.title.hide-collapsed .title.hide-collapsed
Labels Labels
= icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right' = link_to 'Edit', '#', class: 'edit-link pull-right'
.value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) } .value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) }
......
class BuildEmailWorker
include Sidekiq::Worker
include BuildQueue
def perform(build_id, recipients, push_data)
recipients.each do |recipient|
begin
case push_data['build_status']
when 'success'
Notify.build_success_email(build_id, recipient).deliver_now
when 'failed'
Notify.build_fail_email(build_id, recipient).deliver_now
end
# These are input errors and won't be corrected even if Sidekiq retries
rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
logger.info("Failed to send e-mail for project '#{push_data['project_name']}' to #{recipient}: #{e}")
end
end
end
end
---
title: Drop unused ci_projects table and some unused project_id columns,
then rename gl_project_id to project_id. Stop exporting job trace when
exporting projects.
merge_request: 9378
author: David Wagner
---
title: hide loading spinners for server-rendered sidebar fields
merge_request:
author:
---
title: Change gfm textarea to use monospace font
merge_request:
author:
---
title: Fixes large file name tooltip cutoff in diff header
merge_request: 9529
author:
---
title: Fixes dismissable error close is not visible enough
merge_request: 9516
author:
---
title: Allow creating merge request even if target branch is not specified in query
params
merge_request: 9968
author:
---
title: Fix Project Wiki update
merge_request: 9990
author: Dongqing Hu
---
title: Fix trigger webhook for ref with a dot
merge_request: 10001
author: George Andrinopoulos
---
title: Allow unauthenticated access to some Branch API GET endpoints
merge_request:
author:
---
title: Removed d3 from the main application.js bundle
merge_request: 10062
author:
---
title: Add closed_at field to issues
merge_request:
author:
---
title: Only add code coverage instrumentation when generating coverage report
merge_request: 9987
author:
---
title: Migrate SlackService and MattermostService from build_events to pipeline_events, and migrate BuildsEmailService to PipelinesEmailService. Update Hipchat to use pipeline events rather than build events.
merge_request: 8196
author:
---
title: Remove various unused CI tables and columns
merge_request: 9639
author:
---
title: Rename table ci_commits to ci_pipelines
merge_request: 9638
author:
---
title: Rename 'All issues' to 'Open issues' in Add issues modal
merge_request: 10042
author: blackst0ne
---
title: Remove repeated routes.path check for postgresql database
merge_request:
author: mhasbini
---
title: Simplify trigger_docs build job for CE and EE
merge_request: 9820
author: winniehell
...@@ -3,17 +3,6 @@ var webpack = require('webpack'); ...@@ -3,17 +3,6 @@ var webpack = require('webpack');
var webpackConfig = require('./webpack.config.js'); var webpackConfig = require('./webpack.config.js');
var ROOT_PATH = path.resolve(__dirname, '..'); var ROOT_PATH = path.resolve(__dirname, '..');
// add coverage instrumentation to babel config
if (webpackConfig.module && webpackConfig.module.rules) {
var babelConfig = webpackConfig.module.rules.find(function (rule) {
return rule.loader === 'babel-loader';
});
babelConfig.options = babelConfig.options || {};
babelConfig.options.plugins = babelConfig.options.plugins || [];
babelConfig.options.plugins.push('istanbul');
}
// remove problematic plugins // remove problematic plugins
if (webpackConfig.plugins) { if (webpackConfig.plugins) {
webpackConfig.plugins = webpackConfig.plugins.filter(function (plugin) { webpackConfig.plugins = webpackConfig.plugins.filter(function (plugin) {
...@@ -27,7 +16,8 @@ if (webpackConfig.plugins) { ...@@ -27,7 +16,8 @@ if (webpackConfig.plugins) {
// Karma configuration // Karma configuration
module.exports = function(config) { module.exports = function(config) {
var progressReporter = process.env.CI ? 'mocha' : 'progress'; var progressReporter = process.env.CI ? 'mocha' : 'progress';
config.set({
var karmaConfig = {
basePath: ROOT_PATH, basePath: ROOT_PATH,
browsers: ['PhantomJS'], browsers: ['PhantomJS'],
frameworks: ['jasmine'], frameworks: ['jasmine'],
...@@ -38,14 +28,20 @@ module.exports = function(config) { ...@@ -38,14 +28,20 @@ module.exports = function(config) {
preprocessors: { preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'], 'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
}, },
reporters: [progressReporter, 'coverage-istanbul'], reporters: [progressReporter],
coverageIstanbulReporter: { webpack: webpackConfig,
webpackMiddleware: { stats: 'errors-only' },
};
if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') {
karmaConfig.reporters.push('coverage-istanbul');
karmaConfig.coverageIstanbulReporter = {
reports: ['html', 'text-summary'], reports: ['html', 'text-summary'],
dir: 'coverage-javascript/', dir: 'coverage-javascript/',
subdir: '.', subdir: '.',
fixWebpackSourcePaths: true fixWebpackSourcePaths: true
}, };
webpack: webpackConfig, }
webpackMiddleware: { stats: 'errors-only' },
}); config.set(karmaConfig);
}; };
...@@ -35,6 +35,7 @@ var config = { ...@@ -35,6 +35,7 @@ var config = {
issuable: './issuable/issuable_bundle.js', issuable: './issuable/issuable_bundle.js',
merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js', merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js',
merge_request_widget: './merge_request_widget/ci_bundle.js', merge_request_widget: './merge_request_widget/ci_bundle.js',
monitoring: './monitoring/monitoring_bundle.js',
network: './network/network_bundle.js', network: './network/network_bundle.js',
profile: './profile/profile_bundle.js', profile: './profile/profile_bundle.js',
protected_branches: './protected_branches/protected_branches_bundle.js', protected_branches: './protected_branches/protected_branches_bundle.js',
...@@ -58,13 +59,7 @@ var config = { ...@@ -58,13 +59,7 @@ var config = {
{ {
test: /\.js$/, test: /\.js$/,
exclude: /(node_modules|vendor\/assets)/, exclude: /(node_modules|vendor\/assets)/,
loader: 'babel-loader', loader: 'babel-loader'
options: {
presets: [
["es2015", {"modules": false}],
'stage-2'
]
}
}, },
{ {
test: /\.svg$/, test: /\.svg$/,
...@@ -120,7 +115,7 @@ var config = { ...@@ -120,7 +115,7 @@ var config = {
// create cacheable common library bundle for all d3 chunks // create cacheable common library bundle for all d3 chunks
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: 'common_d3', name: 'common_d3',
chunks: ['graphs', 'users'], chunks: ['graphs', 'users', 'monitoring'],
}), }),
// create cacheable common library bundles // create cacheable common library bundles
......
...@@ -5,7 +5,7 @@ class AddIndexForLatestSuccessfulPipeline < ActiveRecord::Migration ...@@ -5,7 +5,7 @@ class AddIndexForLatestSuccessfulPipeline < ActiveRecord::Migration
disable_ddl_transaction! disable_ddl_transaction!
def up def up
add_concurrent_index :ci_commits, [:gl_project_id, :ref, :status] add_concurrent_index(:ci_commits, [:gl_project_id, :ref, :status])
end end
def down def down
......
class DropCiProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
drop_table :ci_projects
end
def down
create_table "ci_projects", force: :cascade do |t|
t.string "name"
t.integer "timeout", default: 3600, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.string "default_ref"
t.string "path"
t.boolean "always_build", default: false, null: false
t.integer "polling_interval"
t.boolean "public", default: false, null: false
t.string "ssh_url_to_repo"
t.integer "gitlab_id"
t.boolean "allow_git_fetch", default: true, null: false
t.string "email_recipients", default: "", null: false
t.boolean "email_add_pusher", default: true, null: false
t.boolean "email_only_broken_builds", default: true, null: false
t.string "skip_refs"
t.string "coverage_regex"
t.boolean "shared_runners_enabled", default: false
t.text "generated_yaml_config"
end
end
end
class RemoveOldProjectIdColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = true
DOWNTIME_REASON = 'Unused columns are being removed.'
def up
remove_index :ci_builds, :project_id if
index_exists?(:ci_builds, :project_id)
remove_column :ci_builds, :project_id
remove_column :ci_commits, :project_id
remove_column :ci_runner_projects, :project_id
remove_column :ci_triggers, :project_id
remove_column :ci_variables, :project_id
end
def down
add_column :ci_builds, :project_id, :integer
add_column :ci_commits, :project_id, :integer
add_column :ci_runner_projects, :project_id, :integer
add_column :ci_triggers, :project_id, :integer
add_column :ci_variables, :project_id, :integer
add_concurrent_index :ci_builds, :project_id
end
end
class RenameGlProjectIdToProjectId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = 'Renaming an actively used column.'
def change
rename_column :ci_builds, :gl_project_id, :project_id
rename_column :ci_commits, :gl_project_id, :project_id
rename_column :ci_runner_projects, :gl_project_id, :project_id
rename_column :ci_triggers, :gl_project_id, :project_id
rename_column :ci_variables, :gl_project_id, :project_id
end
end
class RenameCiCommitsToCiPipelines < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = 'Rename table ci_commits to ci_pipelines'
def change
rename_table 'ci_commits', 'ci_pipelines'
end
end
class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON =
'Remove unused columns in used tables.' \
' Downtime required in case Rails caches them'
def up
%w[ci_application_settings
ci_events
ci_jobs
ci_sessions
ci_taggings
ci_tags].each do |table|
drop_table(table)
end
remove_column :ci_pipelines, :push_data, :text
remove_column :ci_builds, :job_id, :integer
remove_column :ci_builds, :deploy, :boolean
end
def down
add_column :ci_builds, :deploy, :boolean
add_column :ci_builds, :job_id, :integer
add_column :ci_pipelines, :push_data, :text
create_table "ci_tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
create_table "ci_taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "ci_taggings", %w[taggable_id taggable_type context]
create_table "ci_sessions", force: :cascade do |t|
t.string "session_id", null: false
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_jobs", force: :cascade do |t|
t.integer "project_id", null: false
t.text "commands"
t.boolean "active", default: true, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.boolean "build_branches", default: true, null: false
t.boolean "build_tags", default: false, null: false
t.string "job_type", default: "parallel"
t.string "refs"
t.datetime "deleted_at"
end
create_table "ci_events", force: :cascade do |t|
t.integer "project_id"
t.integer "user_id"
t.integer "is_admin"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_application_settings", force: :cascade do |t|
t.boolean "all_broken_builds"
t.boolean "add_pusher"
t.datetime "created_at"
t.datetime "updated_at"
end
end
end
class AddClosedAtToIssues < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :issues, :closed_at, :datetime
end
end
class MigrateBuildEventsToPipelineEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::Database
DOWNTIME = false
def up
Gitlab::Database.with_connection_pool(2) do |pool|
threads = []
threads << Thread.new do
pool.with_connection do |connection|
Thread.current[:foreign_key_connection] = connection
execute(<<-SQL.strip_heredoc)
UPDATE services
SET properties = replace(properties,
'notify_only_broken_builds',
'notify_only_broken_pipelines')
, pipeline_events = #{true_value}
, build_events = #{false_value}
WHERE type IN
('SlackService', 'MattermostService', 'HipchatService')
AND build_events = #{true_value};
SQL
end
end
threads << Thread.new do
pool.with_connection do |connection|
Thread.current[:foreign_key_connection] = connection
execute(update_pipeline_services_sql)
end
end
threads.each(&:join)
end
end
def down
# Don't bother to migrate the data back
end
def connection
# Rails memoizes connection objects, but this causes them to be shared
# amongst threads; we don't want that.
Thread.current[:foreign_key_connection] || ActiveRecord::Base.connection
end
private
def update_pipeline_services_sql
if Gitlab::Database.postgresql?
<<-SQL
UPDATE services
SET type = 'PipelinesEmailService'
, properties = replace(properties,
'notify_only_broken_builds',
'notify_only_broken_pipelines')
, pipeline_events = #{true_value}
, build_events = #{false_value}
WHERE type = 'BuildsEmailService'
AND
(SELECT 1 FROM services pipeline_services
WHERE pipeline_services.project_id = services.project_id
AND pipeline_services.type = 'PipelinesEmailService' LIMIT 1)
IS NULL;
SQL
else
<<-SQL
UPDATE services build_services
LEFT OUTER JOIN services pipeline_services
ON build_services.project_id = pipeline_services.project_id
AND pipeline_services.type = 'PipelinesEmailService'
SET build_services.type = 'PipelinesEmailService'
, build_services.properties = replace(build_services.properties,
'notify_only_broken_builds',
'notify_only_broken_pipelines')
, build_services.pipeline_events = #{true_value}
, build_services.build_events = #{false_value}
WHERE build_services.type = 'BuildsEmailService'
AND pipeline_services.id IS NULL;
SQL
end.strip_heredoc
end
end
...@@ -185,15 +185,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -185,15 +185,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do
add_index "chat_teams", ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree add_index "chat_teams", ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
create_table "ci_application_settings", force: :cascade do |t|
t.boolean "all_broken_builds"
t.boolean "add_pusher"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_builds", force: :cascade do |t| create_table "ci_builds", force: :cascade do |t|
t.integer "project_id"
t.string "status" t.string "status"
t.datetime "finished_at" t.datetime "finished_at"
t.text "trace" t.text "trace"
...@@ -204,9 +196,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -204,9 +196,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do
t.float "coverage" t.float "coverage"
t.integer "commit_id" t.integer "commit_id"
t.text "commands" t.text "commands"
t.integer "job_id"
t.string "name" t.string "name"
t.boolean "deploy", default: false
t.text "options" t.text "options"
t.boolean "allow_failure", default: false, null: false t.boolean "allow_failure", default: false, null: false
t.string "stage" t.string "stage"
...@@ -219,7 +209,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -219,7 +209,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do
t.string "target_url" t.string "target_url"
t.string "description" t.string "description"
t.text "artifacts_file" t.text "artifacts_file"
t.integer "gl_project_id" t.integer "project_id"
t.text "artifacts_metadata" t.text "artifacts_metadata"
t.integer "erased_by_id" t.integer "erased_by_id"
t.datetime "erased_at" t.datetime "erased_at"
...@@ -238,25 +228,22 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -238,25 +228,22 @@ ActiveRecord::Schema.define(version: 20170317131326) do
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
add_index "ci_builds", ["gl_project_id"], name: "index_ci_builds_on_gl_project_id", using: :btree
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
create_table "ci_commits", force: :cascade do |t| create_table "ci_pipelines", force: :cascade do |t|
t.integer "project_id"
t.string "ref" t.string "ref"
t.string "sha" t.string "sha"
t.string "before_sha" t.string "before_sha"
t.text "push_data"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.boolean "tag", default: false t.boolean "tag", default: false
t.text "yaml_errors" t.text "yaml_errors"
t.datetime "committed_at" t.datetime "committed_at"
t.integer "gl_project_id" t.integer "project_id"
t.string "status" t.string "status"
t.datetime "started_at" t.datetime "started_at"
t.datetime "finished_at" t.datetime "finished_at"
...@@ -265,67 +252,20 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -265,67 +252,20 @@ ActiveRecord::Schema.define(version: 20170317131326) do
t.integer "lock_version" t.integer "lock_version"
end end
add_index "ci_commits", ["gl_project_id", "ref", "status"], name: "index_ci_commits_on_gl_project_id_and_ref_and_status", using: :btree add_index "ci_pipelines", ["project_id", "ref", "status"], name: "index_ci_pipelines_on_project_id_and_ref_and_status", using: :btree
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree
add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", using: :btree add_index "ci_pipelines", ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree
add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree add_index "ci_pipelines", ["status"], name: "index_ci_pipelines_on_status", using: :btree
add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree add_index "ci_pipelines", ["user_id"], name: "index_ci_pipelines_on_user_id", using: :btree
create_table "ci_events", force: :cascade do |t|
t.integer "project_id"
t.integer "user_id"
t.integer "is_admin"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_jobs", force: :cascade do |t|
t.integer "project_id", null: false
t.text "commands"
t.boolean "active", default: true, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.boolean "build_branches", default: true, null: false
t.boolean "build_tags", default: false, null: false
t.string "job_type", default: "parallel"
t.string "refs"
t.datetime "deleted_at"
end
create_table "ci_projects", force: :cascade do |t|
t.string "name"
t.integer "timeout", default: 3600, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.string "default_ref"
t.string "path"
t.boolean "always_build", default: false, null: false
t.integer "polling_interval"
t.boolean "public", default: false, null: false
t.string "ssh_url_to_repo"
t.integer "gitlab_id"
t.boolean "allow_git_fetch", default: true, null: false
t.string "email_recipients", default: "", null: false
t.boolean "email_add_pusher", default: true, null: false
t.boolean "email_only_broken_builds", default: true, null: false
t.string "skip_refs"
t.string "coverage_regex"
t.boolean "shared_runners_enabled", default: false
t.text "generated_yaml_config"
end
create_table "ci_runner_projects", force: :cascade do |t| create_table "ci_runner_projects", force: :cascade do |t|
t.integer "runner_id", null: false t.integer "runner_id", null: false
t.integer "project_id"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "gl_project_id" t.integer "project_id"
end end
add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree
add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
create_table "ci_runners", force: :cascade do |t| create_table "ci_runners", force: :cascade do |t|
...@@ -349,30 +289,6 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -349,30 +289,6 @@ ActiveRecord::Schema.define(version: 20170317131326) do
add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
create_table "ci_sessions", force: :cascade do |t|
t.string "session_id", null: false
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "ci_tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
create_table "ci_trigger_requests", force: :cascade do |t| create_table "ci_trigger_requests", force: :cascade do |t|
t.integer "trigger_id", null: false t.integer "trigger_id", null: false
t.text "variables" t.text "variables"
...@@ -385,28 +301,26 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -385,28 +301,26 @@ ActiveRecord::Schema.define(version: 20170317131326) do
create_table "ci_triggers", force: :cascade do |t| create_table "ci_triggers", force: :cascade do |t|
t.string "token" t.string "token"
t.integer "project_id"
t.datetime "deleted_at" t.datetime "deleted_at"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "gl_project_id" t.integer "project_id"
t.integer "owner_id" t.integer "owner_id"
t.string "description" t.string "description"
end end
add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree add_index "ci_triggers", ["project_id"], name: "index_ci_triggers_on_project_id", using: :btree
create_table "ci_variables", force: :cascade do |t| create_table "ci_variables", force: :cascade do |t|
t.integer "project_id"
t.string "key" t.string "key"
t.text "value" t.text "value"
t.text "encrypted_value" t.text "encrypted_value"
t.string "encrypted_value_salt" t.string "encrypted_value_salt"
t.string "encrypted_value_iv" t.string "encrypted_value_iv"
t.integer "gl_project_id" t.integer "project_id"
end end
add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree
create_table "deploy_keys_projects", force: :cascade do |t| create_table "deploy_keys_projects", force: :cascade do |t|
t.integer "deploy_key_id", null: false t.integer "deploy_key_id", null: false
...@@ -531,6 +445,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -531,6 +445,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do
t.text "description_html" t.text "description_html"
t.integer "time_estimate" t.integer "time_estimate"
t.integer "relative_position" t.integer "relative_position"
t.datetime "closed_at"
end end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
...@@ -1379,7 +1294,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do ...@@ -1379,7 +1294,7 @@ ActiveRecord::Schema.define(version: 20170317131326) do
add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "lists", "boards" add_foreign_key "lists", "boards"
add_foreign_key "lists", "labels" add_foreign_key "lists", "labels"
add_foreign_key "merge_request_metrics", "ci_commits", column: "pipeline_id", on_delete: :cascade add_foreign_key "merge_request_metrics", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade
......
...@@ -96,21 +96,15 @@ Sample Prometheus queries: ...@@ -96,21 +96,15 @@ Sample Prometheus queries:
> Introduced in GitLab 9.0. > Introduced in GitLab 9.0.
If your GitLab server is running within Kubernetes, an option is now available If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes in the cluster including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration][] to monitor them.
to monitor the health of each node in the cluster. This is particularly helpful
if your CI/CD environments run in the same cluster, and you would like enable
[Prometheus integration][] to monitor them.
When enabled, the bundled Prometheus server monitors Kubernetes and automatically To disable the monitoring of Kubernetes:
[collects metrics][prometheus-cadvisor-metrics] from each Node in the cluster.
To enable the Kubernetes monitoring:
1. Edit `/etc/gitlab/gitlab.rb` 1. Edit `/etc/gitlab/gitlab.rb`
1. Add or find and uncomment the following line: 1. Add or find and uncomment the following line and set it to `false`:
```ruby ```ruby
prometheus['monitor_kubernetes'] = true prometheus['monitor_kubernetes'] = false
``` ```
1. Save the file and [reconfigure GitLab][reconfigure] for the changes to 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
...@@ -165,4 +159,4 @@ The GitLab monitor exporter allows you to measure various GitLab metrics. ...@@ -165,4 +159,4 @@ The GitLab monitor exporter allows you to measure various GitLab metrics.
[reconfigure]: ../../restart_gitlab.md#omnibus-gitlab-reconfigure [reconfigure]: ../../restart_gitlab.md#omnibus-gitlab-reconfigure
[1261]: https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1261 [1261]: https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1261
[prometheus integration]: ../../../user/project/integrations/prometheus.md [prometheus integration]: ../../../user/project/integrations/prometheus.md
[rometheus-cadvisor-metrics]: https://github.com/google/cadvisor/blob/master/docs/storage/prometheus.md [prometheus-cadvisor-metrics]: https://github.com/google/cadvisor/blob/master/docs/storage/prometheus.md
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
## List repository branches ## List repository branches
Get a list of repository branches from a project, sorted by name alphabetically. Get a list of repository branches from a project, sorted by name alphabetically.
This endpoint can be accessed without authentication if the repository is
publicly accessible.
``` ```
GET /projects/:id/repository/branches GET /projects/:id/repository/branches
...@@ -48,7 +50,8 @@ Example response: ...@@ -48,7 +50,8 @@ Example response:
## Get single repository branch ## Get single repository branch
Get a single project repository branch. Get a single project repository branch. This endpoint can be accessed without
authentication if the repository is publicly accessible.
``` ```
GET /projects/:id/repository/branches/:branch GET /projects/:id/repository/branches/:branch
......
...@@ -139,43 +139,6 @@ Get Buildkite service settings for a project. ...@@ -139,43 +139,6 @@ Get Buildkite service settings for a project.
GET /projects/:id/services/buildkite GET /projects/:id/services/buildkite
``` ```
## Build-Emails
Get emails for GitLab CI builds.
### Create/Edit Build-Emails service
Set Build-Emails service for a project.
```
PUT /projects/:id/services/jobs-email
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `add_pusher` | boolean | no | Add pusher to recipients list |
| `notify_only_broken_jobs` | boolean | no | Notify only broken jobs |
### Delete Job-Emails service
Delete Build-Emails service for a project.
```
DELETE /projects/:id/services/jobs-email
```
### Get Job-Emails service settings
Get Build-Emails service settings for a project.
```
GET /projects/:id/services/jobs-email
```
## Campfire ## Campfire
Simple web-based real-time group chat Simple web-based real-time group chat
...@@ -580,8 +543,7 @@ Parameters: ...@@ -580,8 +543,7 @@ Parameters:
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses | | `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `add_pusher` | boolean | no | Add pusher to recipients list | | `add_pusher` | boolean | no | Add pusher to recipients list |
| `notify_only_broken_jobs` | boolean | no | Notify only broken pipelines | | `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
### Delete Pipeline-Emails service ### Delete Pipeline-Emails service
......
...@@ -207,15 +207,6 @@ you expected. ...@@ -207,15 +207,6 @@ you expected.
You are also able to view the status of any commit in the various pages in You are also able to view the status of any commit in the various pages in
GitLab, such as **Commits** and **Merge requests**. GitLab, such as **Commits** and **Merge requests**.
## Enabling build emails
If you want to receive e-mail notifications about the result status of the
jobs, you should explicitly enable the **Builds Emails** service under your
project's settings.
For more information read the
[Builds emails service documentation](../../user/project/integrations/builds_emails.md).
## Examples ## Examples
Visit the [examples README][examples] to see a list of examples using GitLab Visit the [examples README][examples] to see a list of examples using GitLab
......
This document was moved to [user/project/integrations/builds_emails.md](../user/project/integrations/builds_emails.md).
...@@ -170,12 +170,12 @@ Integration (CI) server. By using deploy keys, you don't have to setup a ...@@ -170,12 +170,12 @@ Integration (CI) server. By using deploy keys, you don't have to setup a
dummy user account. dummy user account.
If you are a project master or owner, you can add a deploy key in the If you are a project master or owner, you can add a deploy key in the
project settings under the section 'Deploy Keys'. Press the 'New Deploy project settings under the section 'Repository'. Specify a title for the new
Key' button and upload a public SSH key. After this, the machine that uses deploy key and paste a public SSH key. After this, the machine that uses
the corresponding private SSH key has read-only or read-write (if enabled) the corresponding private SSH key has read-only or read-write (if enabled)
access to the project. access to the project.
You can't add the same deploy key twice with the 'New Deploy Key' option. You can't add the same deploy key twice using the form.
If you want to add the same key to another project, please enable it in the If you want to add the same key to another project, please enable it in the
list that says 'Deploy keys from projects available to you'. All the deploy list that says 'Deploy keys from projects available to you'. All the deploy
keys of all the projects you have access to are available. This project keys of all the projects you have access to are available. This project
......
...@@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-13-stable-ee ...@@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-13-stable-ee
```bash ```bash
cd /home/git/gitlab-shell cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v3.6.6 sudo -u git -H git checkout v3.6.7
``` ```
### 6. Update gitlab-workhorse ### 6. Update gitlab-workhorse
......
# Enabling build emails
By enabling this service, you will be able to receive e-mail notifications about
the result status of your builds.
Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
and select the **Builds emails** service to configure it.
In the _Recipients_ area, provide a list of e-mails separated by comma.
Check the _Add pusher_ checkbox if you want the committer to also receive
e-mail notifications about each build's status.
If you enable the _Notify only broken builds_ option, e-mail notifications will
be sent only for failed builds.
...@@ -28,7 +28,6 @@ There, you will see a checkbox with the following events that can be triggered: ...@@ -28,7 +28,6 @@ There, you will see a checkbox with the following events that can be triggered:
- Merge request - Merge request
- Note - Note
- Tag push - Tag push
- Build
- Pipeline - Pipeline
- Wiki page - Wiki page
...@@ -41,7 +40,6 @@ At the end, fill in your Mattermost details: ...@@ -41,7 +40,6 @@ At the end, fill in your Mattermost details:
| ----- | ----------- | | ----- | ----------- |
| **Webhook** | The incoming webhook URL which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo… | | **Webhook** | The incoming webhook URL which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo… |
| **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. | | **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. |
| **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. | | **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
![Mattermost configuration](img/mattermost_configuration.png) ![Mattermost configuration](img/mattermost_configuration.png)
...@@ -32,7 +32,6 @@ Click on the service links to see further configuration instructions and details ...@@ -32,7 +32,6 @@ Click on the service links to see further configuration instructions and details
| Assembla | Project Management Software (Source Commits Endpoint) | | Assembla | Project Management Software (Source Commits Endpoint) |
| [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server | | [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server |
| Buildkite | Continuous integration and deployments | | Buildkite | Continuous integration and deployments |
| [Builds emails](builds_emails.md) | Email the builds status to a list of recipients |
| [Bugzilla](bugzilla.md) | Bugzilla issue tracker | | [Bugzilla](bugzilla.md) | Bugzilla issue tracker |
| Campfire | Simple web-based real-time group chat | | Campfire | Simple web-based real-time group chat |
| Custom Issue Tracker | Custom issue tracker | | Custom Issue Tracker | Custom issue tracker |
...@@ -48,6 +47,7 @@ Click on the service links to see further configuration instructions and details ...@@ -48,6 +47,7 @@ Click on the service links to see further configuration instructions and details
| [Kubernetes](kubernetes.md) | A containerized deployment service | | [Kubernetes](kubernetes.md) | A containerized deployment service |
| [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands | | [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands |
| [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost | | [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost |
| Pipelines emails | Email the pipeline status to a list of recipients |
| [Slack Notifications](slack.md) | Receive event notifications in Slack | | [Slack Notifications](slack.md) | Receive event notifications in Slack |
| [Slack slash commands](slack_slash_commands.md) | Slack chat and ChatOps slash commands | | [Slack slash commands](slack_slash_commands.md) | Slack chat and ChatOps slash commands |
| PivotalTracker | Project Management Software (Source Commits Endpoint) | | PivotalTracker | Project Management Software (Source Commits Endpoint) |
......
...@@ -25,7 +25,6 @@ There, you will see a checkbox with the following events that can be triggered: ...@@ -25,7 +25,6 @@ There, you will see a checkbox with the following events that can be triggered:
- Merge request - Merge request
- Note - Note
- Tag push - Tag push
- Build
- Pipeline - Pipeline
- Wiki page - Wiki page
...@@ -38,7 +37,6 @@ At the end, fill in your Slack details: ...@@ -38,7 +37,6 @@ At the end, fill in your Slack details:
| ----- | ----------- | | ----- | ----------- |
| **Webhook** | The [incoming webhook URL][slackhook] which you have to setup on Slack. | | **Webhook** | The [incoming webhook URL][slackhook] which you have to setup on Slack. |
| **Username** | Optional username which can be on messages sent to Slack. Fill this in if you want to change the username of the bot. | | **Username** | Optional username which can be on messages sent to Slack. Fill this in if you want to change the username of the bot. |
| **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. | | **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
After you are all done, click **Save changes** for the changes to take effect. After you are all done, click **Save changes** for the changes to take effect.
......
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