Commit ac1e1b05 authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-04-26

# Conflicts:
#	.gitlab-ci.yml
#	CHANGELOG.md
#	Gemfile.lock
#	app/views/projects/blob/_header.html.haml
#	config/database.yml.postgresql
#	db/schema.rb
#	scripts/prepare_build.sh
#	spec/features/merge_requests/diffs_spec.rb
#	spec/features/protected_branches_spec.rb
#	spec/javascripts/blob/blob_fork_suggestion_spec.js
#	spec/support/test_env.rb
#	yarn.lock
[ci skip]
parents 7e4151c8 8c687299
...@@ -50,7 +50,7 @@ eslint-report.html ...@@ -50,7 +50,7 @@ eslint-report.html
/tags /tags
/tmp/* /tmp/*
/vendor/bundle/* /vendor/bundle/*
/builds/* /builds*
/shared/* /shared/*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/webpack-report/ /webpack-report/
...@@ -54,7 +54,10 @@ stages: ...@@ -54,7 +54,10 @@ stages:
services: services:
- postgres:latest - postgres:latest
- redis:alpine - redis:alpine
<<<<<<< HEAD
- elasticsearch:5.1 - elasticsearch:5.1
=======
>>>>>>> upstream/master
.use-mysql: &use-mysql .use-mysql: &use-mysql
services: services:
...@@ -72,6 +75,16 @@ stages: ...@@ -72,6 +75,16 @@ stages:
- //@gitlab-org/gitlab-ee - //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee - //@gitlab/gitlab-ee
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only:
- /mysql/
- master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce
- tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee
.rspec-knapsack: &rspec-knapsack .rspec-knapsack: &rspec-knapsack
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
......
...@@ -545,7 +545,7 @@ Style/Proc: ...@@ -545,7 +545,7 @@ Style/Proc:
# branches, and conditions. # branches, and conditions.
Metrics/AbcSize: Metrics/AbcSize:
Enabled: true Enabled: true
Max: 60 Max: 57.08
# This cop checks if the length of a block exceeds some maximum value. # This cop checks if the length of a block exceeds some maximum value.
Metrics/BlockLength: Metrics/BlockLength:
...@@ -564,7 +564,7 @@ Metrics/ClassLength: ...@@ -564,7 +564,7 @@ Metrics/ClassLength:
# of test cases needed to validate a method. # of test cases needed to validate a method.
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Enabled: true Enabled: true
Max: 17 Max: 16
# Limit lines to 80 characters. # Limit lines to 80 characters.
Metrics/LineLength: Metrics/LineLength:
......
...@@ -11,6 +11,10 @@ entry. ...@@ -11,6 +11,10 @@ entry.
- Fix lastest commit status text on main project page. !10863 - Fix lastest commit status text on main project page. !10863
- Add index on ci_builds.updated_at. !10870 (blackst0ne) - Add index on ci_builds.updated_at. !10870 (blackst0ne)
- Fix 500 error due to trying to show issues from pending deleting projects. !10906 - Fix 500 error due to trying to show issues from pending deleting projects. !10906
<<<<<<< HEAD
=======
- Ensures that OAuth/LDAP/SAML users don't need to be confirmed.
>>>>>>> upstream/master
- Ensure replying to an individual note by email creates a note with its own discussion ID. - Ensure replying to an individual note by email creates a note with its own discussion ID.
- Fix OAuth, LDAP and SAML SSO when regular sign-ups are disabled. - Fix OAuth, LDAP and SAML SSO when regular sign-ups are disabled.
- Fix usage ping docs link from empty cohorts page. - Fix usage ping docs link from empty cohorts page.
......
...@@ -17,6 +17,8 @@ gem 'pg', '~> 0.18.2', group: :postgres ...@@ -17,6 +17,8 @@ gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.25.1.1' gem 'rugged', '~> 0.25.1.1'
gem 'faraday', '~> 0.11.0'
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.2' gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.2.0' gem 'doorkeeper', '~> 4.2.0'
...@@ -196,7 +198,7 @@ gem 'gemnasium-gitlab-service', '~> 0.2' ...@@ -196,7 +198,7 @@ gem 'gemnasium-gitlab-service', '~> 0.2'
gem 'slack-notifier', '~> 1.5.1' gem 'slack-notifier', '~> 1.5.1'
# Asana integration # Asana integration
gem 'asana', '~> 0.4.0' gem 'asana', '~> 0.6.0'
# FogBugz integration # FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
...@@ -356,7 +358,7 @@ gem 'html2text' ...@@ -356,7 +358,7 @@ gem 'html2text'
gem 'ruby-prof', '~> 0.16.2' gem 'ruby-prof', '~> 0.16.2'
# OAuth # OAuth
gem 'oauth2', '~> 1.2.0' gem 'oauth2', '~> 1.3.0'
# Soft deletion # Soft deletion
gem 'paranoia', '~> 2.2' gem 'paranoia', '~> 2.2'
......
...@@ -47,7 +47,7 @@ GEM ...@@ -47,7 +47,7 @@ GEM
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.5) allocations (1.0.5)
arel (6.0.4) arel (6.0.4)
asana (0.4.0) asana (0.6.0)
faraday (~> 0.9) faraday (~> 0.9)
faraday_middleware (~> 0.9) faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
...@@ -214,13 +214,18 @@ GEM ...@@ -214,13 +214,18 @@ GEM
factory_girl_rails (4.7.0) factory_girl_rails (4.7.0)
factory_girl (~> 4.7.0) factory_girl (~> 4.7.0)
railties (>= 3.0.0) railties (>= 3.0.0)
faraday (0.9.2) faraday (0.11.0)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
<<<<<<< HEAD
faraday_middleware (0.10.0) faraday_middleware (0.10.0)
faraday (>= 0.7.4, < 0.10) faraday (>= 0.7.4, < 0.10)
faraday_middleware-aws-signers-v4 (0.1.5) faraday_middleware-aws-signers-v4 (0.1.5)
aws-sdk (~> 2.1) aws-sdk (~> 2.1)
faraday (~> 0.9) faraday (~> 0.9)
=======
faraday_middleware (0.11.0.1)
faraday (>= 0.7.4, < 1.0)
>>>>>>> upstream/master
faraday_middleware-multi_json (0.0.6) faraday_middleware-multi_json (0.0.6)
faraday_middleware faraday_middleware
multi_json multi_json
...@@ -490,15 +495,15 @@ GEM ...@@ -490,15 +495,15 @@ GEM
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
numerizer (0.1.1) numerizer (0.1.1)
oauth (0.5.1) oauth (0.5.1)
oauth2 (1.2.0) oauth2 (1.3.1)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.12)
jwt (~> 1.0) jwt (~> 1.0)
multi_json (~> 1.3) multi_json (~> 1.3)
multi_xml (~> 0.5) multi_xml (~> 0.5)
rack (>= 1.2, < 3) rack (>= 1.2, < 3)
octokit (4.6.2) octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3) sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.4) oj (2.17.5)
omniauth (1.4.2) omniauth (1.4.2)
hashie (>= 1.2, < 4) hashie (>= 1.2, < 4)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
...@@ -752,7 +757,7 @@ GEM ...@@ -752,7 +757,7 @@ GEM
rack rack
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (4.2.7) sidekiq (4.2.10)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
...@@ -889,7 +894,7 @@ DEPENDENCIES ...@@ -889,7 +894,7 @@ DEPENDENCIES
after_commit_queue (~> 1.3.0) after_commit_queue (~> 1.3.0)
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.4.0) asana (~> 0.6.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
asciidoctor-plantuml (= 0.0.7) asciidoctor-plantuml (= 0.0.7)
attr_encrypted (~> 3.0.0) attr_encrypted (~> 3.0.0)
...@@ -931,7 +936,11 @@ DEPENDENCIES ...@@ -931,7 +936,11 @@ DEPENDENCIES
email_reply_trimmer (~> 0.1) email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0) email_spec (~> 1.6.0)
factory_girl_rails (~> 4.7.0) factory_girl_rails (~> 4.7.0)
<<<<<<< HEAD
faraday_middleware-aws-signers-v4 faraday_middleware-aws-signers-v4
=======
faraday (~> 0.11.0)
>>>>>>> upstream/master
ffaker (~> 2.4) ffaker (~> 2.4)
flay (~> 2.8.0) flay (~> 2.8.0)
fog-aws (~> 0.9) fog-aws (~> 0.9)
...@@ -988,7 +997,7 @@ DEPENDENCIES ...@@ -988,7 +997,7 @@ DEPENDENCIES
net-ldap net-ldap
net-ssh (~> 3.0.1) net-ssh (~> 3.0.1)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0) oauth2 (~> 1.3.0)
octokit (~> 4.6.2) octokit (~> 4.6.2)
oj (~> 2.17.4) oj (~> 2.17.4)
omniauth (~> 1.4.2) omniauth (~> 1.4.2)
......
...@@ -103,7 +103,7 @@ One small thing you also have to do when installing it yourself is to copy the e ...@@ -103,7 +103,7 @@ One small thing you also have to do when installing it yourself is to copy the e
cp config/unicorn.rb.example.development config/unicorn.rb cp config/unicorn.rb.example.development config/unicorn.rb
Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). Instructions on how to start GitLab and how to run the tests can be found in the [getting started section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#getting-started).
## Software stack ## Software stack
......
/* eslint-disable no-new */ /* eslint-disable no-new */
import Vue from 'vue'; import Vue from 'vue';
import VueResource from 'vue-resource'; import VueResource from 'vue-resource';
import NotebookLab from 'vendor/notebooklab'; import notebookLab from '../../notebook/index.vue';
Vue.use(VueResource); Vue.use(VueResource);
Vue.use(NotebookLab);
export default () => { export default () => {
const el = document.getElementById('js-notebook-viewer'); const el = document.getElementById('js-notebook-viewer');
...@@ -19,6 +18,9 @@ export default () => { ...@@ -19,6 +18,9 @@ export default () => {
json: {}, json: {},
}; };
}, },
components: {
notebookLab,
},
template: ` template: `
<div class="container-fluid md prepend-top-default append-bottom-default"> <div class="container-fluid md prepend-top-default append-bottom-default">
<div <div
......
...@@ -22,6 +22,7 @@ class PrometheusGraph { ...@@ -22,6 +22,7 @@ class PrometheusGraph {
const hasMetrics = $prometheusContainer.data('has-metrics'); const hasMetrics = $prometheusContainer.data('has-metrics');
this.docLink = $prometheusContainer.data('doc-link'); this.docLink = $prometheusContainer.data('doc-link');
this.integrationLink = $prometheusContainer.data('prometheus-integration'); this.integrationLink = $prometheusContainer.data('prometheus-integration');
this.state = '';
$(document).ajaxError(() => {}); $(document).ajaxError(() => {});
...@@ -38,8 +39,9 @@ class PrometheusGraph { ...@@ -38,8 +39,9 @@ class PrometheusGraph {
this.configureGraph(); this.configureGraph();
this.init(); this.init();
} else { } else {
const prevState = this.state;
this.state = '.js-getting-started'; this.state = '.js-getting-started';
this.updateState(); this.updateState(prevState);
} }
} }
...@@ -53,26 +55,26 @@ class PrometheusGraph { ...@@ -53,26 +55,26 @@ class PrometheusGraph {
} }
init() { init() {
this.getData().then((metricsResponse) => { return this.getData().then((metricsResponse) => {
let enoughData = true; let enoughData = true;
Object.keys(metricsResponse.metrics).forEach((key) => { if (typeof metricsResponse === 'undefined') {
let currentKey; enoughData = false;
if (key === 'cpu_values' || key === 'memory_values') {
currentKey = metricsResponse.metrics[key];
if (Object.keys(currentKey).length === 0) {
enoughData = false;
}
}
});
if (!enoughData) {
this.state = '.js-loading';
this.updateState();
} else { } else {
Object.keys(metricsResponse.metrics).forEach((key) => {
if (key === 'cpu_values' || key === 'memory_values') {
const currentData = (metricsResponse.metrics[key])[0];
if (currentData.values.length <= 2) {
enoughData = false;
}
}
});
}
if (enoughData) {
$(prometheusStatesContainer).hide();
$(prometheusParentGraphContainer).show();
this.transformData(metricsResponse); this.transformData(metricsResponse);
this.createGraph(); this.createGraph();
} }
}).catch(() => {
new Flash('An error occurred when trying to load metrics. Please try again.');
}); });
} }
...@@ -342,6 +344,8 @@ class PrometheusGraph { ...@@ -342,6 +344,8 @@ class PrometheusGraph {
getData() { getData() {
const maxNumberOfRequests = 3; const maxNumberOfRequests = 3;
this.state = '.js-loading';
this.updateState();
return gl.utils.backOff((next, stop) => { return gl.utils.backOff((next, stop) => {
$.ajax({ $.ajax({
url: metricsEndpoint, url: metricsEndpoint,
...@@ -352,12 +356,11 @@ class PrometheusGraph { ...@@ -352,12 +356,11 @@ class PrometheusGraph {
this.backOffRequestCounter = this.backOffRequestCounter += 1; this.backOffRequestCounter = this.backOffRequestCounter += 1;
if (this.backOffRequestCounter < maxNumberOfRequests) { if (this.backOffRequestCounter < maxNumberOfRequests) {
next(); next();
} else { } else if (this.backOffRequestCounter >= maxNumberOfRequests) {
stop({ stop(new Error('loading'));
status: resp.status,
metrics: data,
});
} }
} else if (!data.success) {
stop(new Error('loading'));
} else { } else {
stop({ stop({
status: resp.status, status: resp.status,
...@@ -373,8 +376,9 @@ class PrometheusGraph { ...@@ -373,8 +376,9 @@ class PrometheusGraph {
return resp.metrics; return resp.metrics;
}) })
.catch(() => { .catch(() => {
const prevState = this.state;
this.state = '.js-unable-to-connect'; this.state = '.js-unable-to-connect';
this.updateState(); this.updateState(prevState);
}); });
} }
...@@ -382,19 +386,20 @@ class PrometheusGraph { ...@@ -382,19 +386,20 @@ class PrometheusGraph {
Object.keys(metricsResponse.metrics).forEach((key) => { Object.keys(metricsResponse.metrics).forEach((key) => {
if (key === 'cpu_values' || key === 'memory_values') { if (key === 'cpu_values' || key === 'memory_values') {
const metricValues = (metricsResponse.metrics[key])[0]; const metricValues = (metricsResponse.metrics[key])[0];
if (metricValues !== undefined) { this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({
this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({ time: new Date(metric[0] * 1000),
time: new Date(metric[0] * 1000), value: metric[1],
value: metric[1], }));
}));
}
} }
}); });
} }
updateState() { updateState(prevState) {
const $statesContainer = $(prometheusStatesContainer); const $statesContainer = $(prometheusStatesContainer);
$(prometheusParentGraphContainer).hide(); $(prometheusParentGraphContainer).hide();
if (prevState) {
$(`${prevState}`, $statesContainer).addClass('hidden');
}
$(`${this.state}`, $statesContainer).removeClass('hidden'); $(`${this.state}`, $statesContainer).removeClass('hidden');
$(prometheusStatesContainer).show(); $(prometheusStatesContainer).show();
} }
......
<template>
<div class="cell">
<code-cell
type="input"
:raw-code="rawInputCode"
:count="cell.execution_count"
:code-css-class="codeCssClass" />
<output-cell
v-if="hasOutput"
:count="cell.execution_count"
:output="output"
:code-css-class="codeCssClass" />
</div>
</template>
<script>
import CodeCell from './code/index.vue';
import OutputCell from './output/index.vue';
export default {
components: {
'code-cell': CodeCell,
'output-cell': OutputCell,
},
props: {
cell: {
type: Object,
required: true,
},
codeCssClass: {
type: String,
required: false,
default: '',
},
},
computed: {
rawInputCode() {
if (this.cell.source) {
return this.cell.source.join('');
}
return '';
},
hasOutput() {
return this.cell.outputs.length;
},
output() {
return this.cell.outputs[0];
},
},
};
</script>
<style scoped>
.cell {
flex-direction: column;
}
</style>
<template>
<div :class="type">
<prompt
:type="promptType"
:count="count" />
<pre
class="language-python"
:class="codeCssClass"
ref="code"
v-text="code">
</pre>
</div>
</template>
<script>
import Prism from '../../lib/highlight';
import Prompt from '../prompt.vue';
export default {
components: {
prompt: Prompt,
},
props: {
count: {
type: Number,
required: false,
default: 0,
},
codeCssClass: {
type: String,
required: false,
default: '',
},
type: {
type: String,
required: true,
},
rawCode: {
type: String,
required: true,
},
},
computed: {
code() {
return this.rawCode;
},
promptType() {
const type = this.type.split('put')[0];
return type.charAt(0).toUpperCase() + type.slice(1);
},
},
mounted() {
Prism.highlightElement(this.$refs.code);
},
};
</script>
export { default as MarkdownCell } from './markdown.vue';
export { default as CodeCell } from './code.vue';
<template>
<div class="cell text-cell">
<prompt />
<div class="markdown" v-html="markdown"></div>
</div>
</template>
<script>
/* global katex */
import marked from 'marked';
import Prompt from './prompt.vue';
const renderer = new marked.Renderer();
/*
Regex to match KaTex blocks.
Supports the following:
\begin{equation}<math>\end{equation}
$$<math>$$
inline $<math>$
The matched text then goes through the KaTex renderer & then outputs the HTML
*/
const katexRegexString = `(
^\\\\begin{[a-zA-Z]+}\\s
|
^\\$\\$
|
\\s\\$(?!\\$)
)
(.+?)
(
\\s\\\\end{[a-zA-Z]+}$
|
\\$\\$$
|
\\$
)
`.replace(/\s/g, '').trim();
renderer.paragraph = (t) => {
let text = t;
let inline = false;
if (typeof katex !== 'undefined') {
const katexString = text.replace(/\\/g, '\\');
const matches = new RegExp(katexRegexString, 'gi').exec(katexString);
if (matches && matches.length > 0) {
if (matches[1].trim() === '$' && matches[3].trim() === '$') {
inline = true;
text = `${katexString.replace(matches[0], '')} ${katex.renderToString(matches[2])}`;
} else {
text = katex.renderToString(matches[2]);
}
}
}
return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`;
};
marked.setOptions({
sanitize: true,
renderer,
});
export default {
components: {
prompt: Prompt,
},
props: {
cell: {
type: Object,
required: true,
},
},
computed: {
markdown() {
return marked(this.cell.source.join(''));
},
},
};
</script>
<style>
.markdown .katex {
display: block;
text-align: center;
}
.markdown .inline-katex .katex {
display: inline;
text-align: initial;
}
</style>
<template>
<div class="output">
<prompt />
<div v-html="rawCode"></div>
</div>
</template>
<script>
import Prompt from '../prompt.vue';
export default {
props: {
rawCode: {
type: String,
required: true,
},
},
components: {
prompt: Prompt,
},
};
</script>
<template>
<div class="output">
<prompt />
<img
:src="'data:' + outputType + ';base64,' + rawCode" />
</div>
</template>
<script>
import Prompt from '../prompt.vue';
export default {
props: {
outputType: {
type: String,
required: true,
},
rawCode: {
type: String,
required: true,
},
},
components: {
prompt: Prompt,
},
};
</script>
<template>
<component :is="componentName"
type="output"
:outputType="outputType"
:count="count"
:raw-code="rawCode"
:code-css-class="codeCssClass" />
</template>
<script>
import CodeCell from '../code/index.vue';
import Html from './html.vue';
import Image from './image.vue';
export default {
props: {
codeCssClass: {
type: String,
required: false,
default: '',
},
count: {
type: Number,
required: false,
default: 0,
},
output: {
type: Object,
requred: true,
},
},
components: {
'code-cell': CodeCell,
'html-output': Html,
'image-output': Image,
},
data() {
return {
outputType: '',
};
},
computed: {
componentName() {
if (this.output.text) {
return 'code-cell';
} else if (this.output.data['image/png']) {
this.outputType = 'image/png';
return 'image-output';
} else if (this.output.data['text/html']) {
this.outputType = 'text/html';
return 'html-output';
} else if (this.output.data['image/svg+xml']) {
this.outputType = 'image/svg+xml';
return 'html-output';
}
this.outputType = 'text/plain';
return 'code-cell';
},
rawCode() {
if (this.output.text) {
return this.output.text.join('');
}
return this.dataForType(this.outputType);
},
},
methods: {
dataForType(type) {
let data = this.output.data[type];
if (typeof data === 'object') {
data = data.join('');
}
return data;
},
},
};
</script>
<template>
<div class="prompt">
<span v-if="type && count">
{{ type }} [{{ count }}]:
</span>
</div>
</template>
<script>
export default {
props: {
type: {
type: String,
required: false,
},
count: {
type: Number,
required: false,
},
},
};
</script>
<style scoped>
.prompt {
padding: 0 10px;
min-width: 7em;
font-family: monospace;
}
</style>
<template>
<div v-if="hasNotebook">
<component
v-for="(cell, index) in cells"
:is="cellType(cell.cell_type)"
:cell="cell"
:key="index"
:code-css-class="codeCssClass" />
</div>
</template>
<script>
import {
MarkdownCell,
CodeCell,
} from './cells';
export default {
components: {
'code-cell': CodeCell,
'markdown-cell': MarkdownCell,
},
props: {
notebook: {
type: Object,
required: true,
},
codeCssClass: {
type: String,
required: false,
default: '',
},
},
methods: {
cellType(type) {
return `${type}-cell`;
},
},
computed: {
cells() {
if (this.notebook.worksheets) {
const data = {
cells: [],
};
return this.notebook.worksheets.reduce((cellData, sheet) => {
const cellDataCopy = cellData;
cellDataCopy.cells = cellDataCopy.cells.concat(sheet.cells);
return cellDataCopy;
}, data).cells;
}
return this.notebook.cells;
},
hasNotebook() {
return Object.keys(this.notebook).length;
},
},
};
</script>
<style>
.cell,
.input,
.output {
display: flex;
width: 100%;
margin-bottom: 10px;
}
.cell pre {
margin: 0;
width: 100%;
}
</style>
import Prism from 'prismjs';
import 'prismjs/components/prism-python';
import 'prismjs/plugins/custom-class/prism-custom-class';
Prism.plugins.customClass.map({
comment: 'c',
error: 'err',
operator: 'o',
constant: 'kc',
namespace: 'kn',
keyword: 'k',
string: 's',
number: 'm',
'attr-name': 'na',
builtin: 'nb',
entity: 'ni',
function: 'nf',
tag: 'nt',
variable: 'nv',
});
export default Prism;
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser, showMenuAbove; var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser, showMenuAbove;
$dropdown = $(dropdown); $dropdown = $(dropdown);
options.projectId = $dropdown.data('project-id'); options.projectId = $dropdown.data('project-id');
options.groupId = $dropdown.data('group-id');
options.showCurrentUser = $dropdown.data('current-user'); options.showCurrentUser = $dropdown.data('current-user');
options.todoFilter = $dropdown.data('todo-filter'); options.todoFilter = $dropdown.data('todo-filter');
options.todoStateFilter = $dropdown.data('todo-state-filter'); options.todoStateFilter = $dropdown.data('todo-state-filter');
......
...@@ -108,8 +108,7 @@ ...@@ -108,8 +108,7 @@
} }
.award-control { .award-control {
margin: 3px 5px 3px 0; margin-right: 5px;
padding: .35em .4em;
outline: 0; outline: 0;
&.disabled { &.disabled {
......
...@@ -70,7 +70,7 @@ pre { ...@@ -70,7 +70,7 @@ pre {
} }
hr { hr {
margin: $gl-padding 0; margin: 24px 0;
border-top: 1px solid darken($gray-normal, 8%); border-top: 1px solid darken($gray-normal, 8%);
} }
......
...@@ -73,14 +73,6 @@ ...@@ -73,14 +73,6 @@
&.wiki { &.wiki {
padding: 30px $gl-padding; padding: 30px $gl-padding;
.highlight {
margin-bottom: 9px;
> pre {
margin: 0;
}
}
} }
&.blob-no-preview { &.blob-no-preview {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding $gl-btn-padding 14px; padding: $gl-padding $gl-btn-padding 0;
border-color: $white-normal; border-color: $white-normal;
color: $gl-text-color; color: $gl-text-color;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;
......
...@@ -8,6 +8,13 @@ ...@@ -8,6 +8,13 @@
img { img {
max-width: 100%; max-width: 100%;
margin: 0 0 8px;
}
p a:not(.no-attachment-icon) img {
// Remove bottom padding because
// <p> already has $gl-padding bottom
margin-bottom: 0;
} }
*:first-child:not(.katex-display) { *:first-child:not(.katex-display) {
...@@ -47,44 +54,50 @@ ...@@ -47,44 +54,50 @@
h1 { h1 {
font-size: 1.75em; font-size: 1.75em;
font-weight: 600; font-weight: 600;
margin: 16px 0 10px; margin: 24px 0 16px;
padding: 0 0 0.3em; padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark; border-bottom: 1px solid $white-dark;
color: $gl-text-color; color: $gl-text-color;
&:first-child {
margin-top: 0;
}
} }
h2 { h2 {
font-size: 1.5em; font-size: 1.5em;
font-weight: 600; font-weight: 600;
margin: 16px 0 10px; margin: 24px 0 16px;
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color; color: $gl-text-color;
} }
h3 { h3 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1.3em; font-size: 1.3em;
} }
h4 { h4 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1.2em; font-size: 1.2em;
} }
h5 { h5 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1em; font-size: 1em;
} }
h6 { h6 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 0.95em; font-size: 0.95em;
} }
blockquote { blockquote {
color: $gl-grayish-blue; color: $gl-grayish-blue;
font-size: inherit; font-size: inherit;
padding: 8px 21px; padding: 8px 24px;
margin: 12px 0; margin: 16px 0;
border-left: 3px solid $white-dark; border-left: 3px solid $white-dark;
} }
...@@ -95,19 +108,20 @@ ...@@ -95,19 +108,20 @@
blockquote p { blockquote p {
color: $gl-grayish-blue !important; color: $gl-grayish-blue !important;
margin: 0;
font-size: inherit; font-size: inherit;
line-height: 1.5; line-height: 1.5;
} }
p { p {
color: $gl-text-color; color: $gl-text-color;
margin: 6px 0 0; margin: 0 0 16px;
} }
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0; margin: 16px 0;
color: $gl-text-color; color: $gl-text-color;
th { th {
...@@ -120,7 +134,7 @@ ...@@ -120,7 +134,7 @@
} }
pre { pre {
margin: 12px 0; margin-bottom: 16px;
font-size: 13px; font-size: 13px;
line-height: 1.6em; line-height: 1.6em;
overflow-x: auto; overflow-x: auto;
...@@ -134,7 +148,7 @@ ...@@ -134,7 +148,7 @@
ul, ul,
ol { ol {
padding: 0; padding: 0;
margin: 3px 0 !important; margin: 0 0 16px !important;
} }
ul:dir(rtl), ul:dir(rtl),
......
...@@ -29,11 +29,5 @@ ...@@ -29,11 +29,5 @@
.description { .description {
margin-top: 6px; margin-top: 6px;
p {
&:last-child {
margin-bottom: 0;
}
}
} }
} }
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
.title { .title {
padding: 0; padding: 0;
margin: 0; margin-bottom: 16px;
border-bottom: none; border-bottom: none;
} }
...@@ -357,6 +357,8 @@ ...@@ -357,6 +357,8 @@
} }
.detail-page-description { .detail-page-description {
padding: 16px 0 0;
small { small {
color: $gray-darkest; color: $gray-darkest;
} }
...@@ -364,6 +366,8 @@ ...@@ -364,6 +366,8 @@
.edited-text { .edited-text {
color: $gray-darkest; color: $gray-darkest;
display: block;
margin: 0 0 16px;
.author_link { .author_link {
color: $gray-darkest; color: $gray-darkest;
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.note-edit-form { .note-edit-form {
.note-form-actions { .note-form-actions {
position: relative; position: relative;
margin-top: $gl-padding; margin: $gl-padding 0;
} }
.note-preview-holder { .note-preview-holder {
...@@ -387,6 +387,7 @@ ...@@ -387,6 +387,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
display: flex; display: flex;
width: 100%; width: 100%;
margin-bottom: 10px;
.comment-btn { .comment-btn {
flex-grow: 1; flex-grow: 1;
......
...@@ -102,13 +102,12 @@ ul.notes { ...@@ -102,13 +102,12 @@ ul.notes {
.note-awards { .note-awards {
.js-awards-block { .js-awards-block {
padding: 2px; margin-bottom: 16px;
margin-top: 10px;
} }
} }
.note-header { .note-header {
padding-bottom: 3px; padding-bottom: 8px;
padding-right: 20px; padding-right: 20px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
...@@ -151,6 +150,10 @@ ul.notes { ...@@ -151,6 +150,10 @@ ul.notes {
margin-left: 65px; margin-left: 65px;
} }
.note-header {
padding-bottom: 0;
}
&.timeline-entry::after { &.timeline-entry::after {
clear: none; clear: none;
} }
...@@ -386,6 +389,10 @@ ul.notes { ...@@ -386,6 +389,10 @@ ul.notes {
.note-headline-meta { .note-headline-meta {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;
.system-note-message {
white-space: normal;
}
} }
/** /**
......
...@@ -28,7 +28,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -28,7 +28,7 @@ class Admin::GroupsController < Admin::ApplicationController
if @group.save if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
redirect_to [:admin, @group], notice: 'Group was successfully created.' redirect_to [:admin, @group], notice: "Group '#{@group.name}' was successfully created."
else else
render "new" render "new"
end end
......
...@@ -23,6 +23,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -23,6 +23,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
@project_namespace = @project.namespace.becomes(Namespace)
@milestones = @milestones.includes(:project) @milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page]) @milestones = @milestones.page(params[:page])
end end
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
# current_user - which user use # current_user - which user use
# params: # params:
# scope: 'created-by-me' or 'assigned-to-me' or 'all' # scope: 'created-by-me' or 'assigned-to-me' or 'all'
# state: 'open' or 'closed' or 'all' # state: 'open', 'closed', 'merged', or 'all'
# group_id: integer # group_id: integer
# project_id: integer # project_id: integer
# milestone_title: string # milestone_title: string
......
##
# DEPRECATED
#
# These helpers are deprecated in favor of detailed CI/CD statuses.
#
# See 'detailed_status?` method and `Gitlab::Ci::Status` module.
#
module CiStatusHelper module CiStatusHelper
def ci_status_path(pipeline) def ci_status_path(pipeline)
project = pipeline.project project = pipeline.project
namespace_project_pipeline_path(project.namespace, project, pipeline) namespace_project_pipeline_path(project.namespace, project, pipeline)
end end
# Is used by Commit and Merge Request Widget
def ci_label_for_status(status) def ci_label_for_status(status)
if detailed_status?(status) if detailed_status?(status)
return status.label return status.label
...@@ -22,6 +28,23 @@ module CiStatusHelper ...@@ -22,6 +28,23 @@ module CiStatusHelper
end end
end end
def ci_text_for_status(status)
if detailed_status?(status)
return status.text
end
case status
when 'success'
'passed'
when 'success_with_warnings'
'passed'
when 'manual'
'blocked'
else
status
end
end
def ci_status_for_statuseable(subject) def ci_status_for_statuseable(subject)
status = subject.try(:status) || 'not found' status = subject.try(:status) || 'not found'
status.humanize status.humanize
......
...@@ -7,6 +7,11 @@ module IconsHelper ...@@ -7,6 +7,11 @@ module IconsHelper
# font-awesome-rails gem, but should we ever use a different icon pack in the # font-awesome-rails gem, but should we ever use a different icon pack in the
# future we won't have to change hundreds of method calls. # future we won't have to change hundreds of method calls.
def icon(names, options = {}) def icon(names, options = {})
if (options.keys & %w[aria-hidden aria-label]).empty?
# Add `aria-hidden` if there are no aria's set
options['aria-hidden'] = true
end
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options) options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end end
......
...@@ -56,11 +56,12 @@ module MergeRequestsHelper ...@@ -56,11 +56,12 @@ module MergeRequestsHelper
end end
def issues_sentence(issues) def issues_sentence(issues)
# Sorting based on the `#123` or `group/project#123` reference will sort # Issuable sorter will sort local issues, then issues from the same
# local issues first. # namespace, then all other issues.
issues.map do |issue| issues = Gitlab::IssuableSorter.sort(@project, issues).map do |issue|
issue.to_reference(@project) issue.to_reference(@project)
end.sort.to_sentence end
issues.to_sentence
end end
def mr_closes_issues def mr_closes_issues
......
...@@ -75,29 +75,32 @@ module Ci ...@@ -75,29 +75,32 @@ module Ci
pipeline.update_duration pipeline.update_duration
end end
before_transition any => [:manual] do |pipeline|
pipeline.update_duration
end
before_transition canceled: any - [:canceled] do |pipeline| before_transition canceled: any - [:canceled] do |pipeline|
pipeline.auto_canceled_by = nil pipeline.auto_canceled_by = nil
end end
after_transition [:created, :pending] => :running do |pipeline| after_transition [:created, :pending] => :running do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) } pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end end
after_transition any => [:success] do |pipeline| after_transition any => [:success] do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) } pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end end
after_transition [:created, :pending, :running] => :success do |pipeline| after_transition [:created, :pending, :running] => :success do |pipeline|
pipeline.run_after_commit { PipelineSuccessWorker.perform_async(id) } pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) }
end end
after_transition do |pipeline, transition| after_transition do |pipeline, transition|
next if transition.loopback? next if transition.loopback?
pipeline.run_after_commit do pipeline.run_after_commit do
PipelineHooksWorker.perform_async(id) PipelineHooksWorker.perform_async(pipeline.id)
Ci::ExpirePipelineCacheService.new(project, nil) ExpirePipelineCacheWorker.perform_async(pipeline.id)
.execute(pipeline)
end end
end end
...@@ -385,6 +388,11 @@ module Ci ...@@ -385,6 +388,11 @@ module Ci
.select { |merge_request| merge_request.head_pipeline.try(:id) == self.id } .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end end
# All the merge requests for which the current pipeline runs/ran against
def all_merge_requests
@all_merge_requests ||= project.merge_requests.where(source_branch: ref)
end
def detailed_status(current_user) def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory Gitlab::Ci::Status::Pipeline::Factory
.new(self, current_user) .new(self, current_user)
......
...@@ -163,7 +163,20 @@ module Routable ...@@ -163,7 +163,20 @@ module Routable
end end
end end
# Every time `project.namespace.becomes(Namespace)` is called for polymorphic_path,
# a new instance is instantiated, and we end up duplicating the same query to retrieve
# the route. Caching this per request ensures that even if we have multiple instances,
# we will not have to duplicate work, avoiding N+1 queries in some cases.
def full_path def full_path
return uncached_full_path unless RequestStore.active?
key = "routable/full_path/#{self.class.name}/#{self.id}"
RequestStore[key] ||= uncached_full_path
end
private
def uncached_full_path
if route && route.path.present? if route && route.path.present?
@full_path ||= route.path @full_path ||= route.path
else else
...@@ -173,8 +186,6 @@ module Routable ...@@ -173,8 +186,6 @@ module Routable
end end
end end
private
def full_name_changed? def full_name_changed?
name_changed? || parent_changed? name_changed? || parent_changed?
end end
......
...@@ -10,4 +10,8 @@ class IndividualNoteDiscussion < Discussion ...@@ -10,4 +10,8 @@ class IndividualNoteDiscussion < Discussion
def individual_note? def individual_note?
true true
end end
def reply_attributes
super.tap { |attrs| attrs.delete(:discussion_id) }
end
end end
...@@ -198,22 +198,23 @@ class MergeRequest < ActiveRecord::Base ...@@ -198,22 +198,23 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args) merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args)
end end
def diffs(diff_options = nil) def diffs(diff_options = {})
if compare if compare
compare.diffs(diff_options) # When saving MR diffs, `no_collapse` is implicitly added (because we need
# to save the entire contents to the DB), so add that here for
# consistency.
compare.diffs(diff_options.merge(no_collapse: true))
else else
merge_request_diff.diffs(diff_options) merge_request_diff.diffs(diff_options)
end end
end end
def diff_size def diff_size
# The `#diffs` method ends up at an instance of a class inheriting from # Calling `merge_request_diff.diffs.real_size` will also perform
# `Gitlab::Diff::FileCollection::Base`, so use those options as defaults # highlighting, which we don't need here.
# here too, to get the same diff size without performing highlighting. return real_size if merge_request_diff
#
opts = Gitlab::Diff::FileCollection::Base.default_options.merge(diff_options || {})
raw_diffs(opts).size diffs.real_size
end end
def diff_base_commit def diff_base_commit
......
...@@ -260,7 +260,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -260,7 +260,7 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:state] = :empty new_attributes[:state] = :empty
else else
diff_collection = compare.diffs(Commit.max_diff_options) diff_collection = compare.diffs(Commit.max_diff_options)
new_attributes[:real_size] = compare.diffs.real_size new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any? if diff_collection.any?
new_diffs = dump_diffs(diff_collection) new_diffs = dump_diffs(diff_collection)
......
...@@ -15,8 +15,12 @@ class OutOfContextDiscussion < Discussion ...@@ -15,8 +15,12 @@ class OutOfContextDiscussion < Discussion
def self.override_discussion_id(note) def self.override_discussion_id(note)
discussion_id(note) discussion_id(note)
end end
def self.note_class def self.note_class
Note Note
end end
def reply_attributes
super.tap { |attrs| attrs.delete(:discussion_id) }
end
end end
module Ci
class ExpirePipelineCacheService < BaseService
attr_reader :pipeline
def execute(pipeline)
@pipeline = pipeline
store = Gitlab::EtagCaching::Store.new
store.touch(project_pipelines_path)
store.touch(commit_pipelines_path) if pipeline.commit
store.touch(new_merge_request_pipelines_path)
merge_requests_pipelines_paths.each { |path| store.touch(path) }
Gitlab::Cache::Ci::ProjectPipelineStatus.update_for_pipeline(@pipeline)
end
private
def project_pipelines_path
Gitlab::Routing.url_helpers.namespace_project_pipelines_path(
project.namespace,
project,
format: :json)
end
def commit_pipelines_path
Gitlab::Routing.url_helpers.pipelines_namespace_project_commit_path(
project.namespace,
project,
pipeline.commit.id,
format: :json)
end
def new_merge_request_pipelines_path
Gitlab::Routing.url_helpers.new_namespace_project_merge_request_path(
project.namespace,
project,
format: :json)
end
def merge_requests_pipelines_paths
pipeline.merge_requests.collect do |merge_request|
Gitlab::Routing.url_helpers.pipelines_namespace_project_merge_request_path(
project.namespace,
project,
merge_request,
format: :json)
end
end
end
end
...@@ -6,18 +6,16 @@ module Users ...@@ -6,18 +6,16 @@ module Users
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
raise Gitlab::Access::AccessDeniedError unless can_create_user? raise Gitlab::Access::AccessDeniedError unless skip_authorization || can_create_user?
user = User.new(build_user_params) user_params = build_user_params(skip_authorization: skip_authorization)
user = User.new(user_params)
if current_user&.admin? if current_user&.admin?
if params[:reset_password] @reset_token = user.generate_reset_token if params[:reset_password]
user.generate_reset_token
params[:force_random_password] = true
end
if params[:force_random_password] if user_params[:force_random_password]
random_password = Devise.friendly_token.first(Devise.password_length.min) random_password = Devise.friendly_token.first(Devise.password_length.min)
user.password = user.password_confirmation = random_password user.password = user.password_confirmation = random_password
end end
...@@ -81,7 +79,7 @@ module Users ...@@ -81,7 +79,7 @@ module Users
] ]
end end
def build_user_params def build_user_params(skip_authorization:)
if current_user&.admin? if current_user&.admin?
user_params = params.slice(*admin_create_params) user_params = params.slice(*admin_create_params)
user_params[:created_by_id] = current_user&.id user_params[:created_by_id] = current_user&.id
...@@ -90,11 +88,20 @@ module Users ...@@ -90,11 +88,20 @@ module Users
user_params.merge!(force_random_password: true, password_expires_at: nil) user_params.merge!(force_random_password: true, password_expires_at: nil)
end end
else else
user_params = params.slice(*signup_params) allowed_signup_params = signup_params
user_params[:skip_confirmation] = !current_application_settings.send_user_confirmation_email allowed_signup_params << :skip_confirmation if skip_authorization
user_params = params.slice(*allowed_signup_params)
if user_params[:skip_confirmation].nil?
user_params[:skip_confirmation] = skip_user_confirmation_email_from_setting
end
end end
user_params user_params
end end
def skip_user_confirmation_email_from_setting
!current_application_settings.send_user_confirmation_email
end
end end
end end
...@@ -6,8 +6,8 @@ module Users ...@@ -6,8 +6,8 @@ module Users
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
user = Users::BuildService.new(current_user, params).execute user = Users::BuildService.new(current_user, params).execute(skip_authorization: skip_authorization)
@reset_token = user.generate_reset_token if user.recently_sent_password_reset? @reset_token = user.generate_reset_token if user.recently_sent_password_reset?
......
...@@ -15,27 +15,39 @@ module Users ...@@ -15,27 +15,39 @@ module Users
end end
def execute def execute
# Block the user before moving records to prevent a data race. transition = user.block_transition
# For example, if the user creates an issue after `migrate_issues`
# runs and before the user is destroyed, the destroy will fail with
# an exception.
user.block
user.transaction do user.transaction do
# Block the user before moving records to prevent a data race.
# For example, if the user creates an issue after `migrate_issues`
# runs and before the user is destroyed, the destroy will fail with
# an exception.
user.block
# Reverse the user block if record migration fails
if !migrate_records && transition
transition.rollback
user.save!
end
end
user.reload
end
private
def migrate_records
user.transaction(requires_new: true) do
@ghost_user = User.ghost @ghost_user = User.ghost
migrate_issues migrate_issues
migrate_merge_requests migrate_merge_requests
migrate_notes migrate_notes
migrate_abuse_reports migrate_abuse_reports
migrate_award_emoji migrate_award_emojis
end end
user.reload
end end
private
def migrate_issues def migrate_issues
user.issues.update_all(author_id: ghost_user.id) user.issues.update_all(author_id: ghost_user.id)
end end
...@@ -52,7 +64,7 @@ module Users ...@@ -52,7 +64,7 @@ module Users
user.reported_abuse_reports.update_all(reporter_id: ghost_user.id) user.reported_abuse_reports.update_all(reporter_id: ghost_user.id)
end end
def migrate_award_emoji def migrate_award_emojis
user.award_emoji.update_all(user_id: ghost_user.id) user.award_emoji.update_all(user_id: ghost_user.id)
end end
end end
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.bs-callout.bs-callout-warning.clearfix .bs-callout.bs-callout-warning.clearfix
%p %p
User cohorts are only shown when the User cohorts are only shown when the
= link_to 'usage ping', help_page_path('user/admin_area/usage_statistics'), target: '_blank' = link_to 'usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping'), target: '_blank'
is enabled. To enable it and see user cohorts, is enabled. To enable it and see user cohorts,
visit visit
= succeed '.' do = succeed '.' do
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
Snippets Snippets
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit members#show integrations#show repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span %span
Settings Settings
......
- ref = local_assigns.fetch(:ref) - ref = local_assigns.fetch(:ref)
- status = commit.status(ref) - status = commit.status(ref)
- if status - if status
= link_to pipelines_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do = link_to pipelines_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do
= ci_icon_for_status(status) = ci_icon_for_status(status)
= ci_label_for_status(status) = ci_text_for_status(status)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message" = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
= delete_blob_link = delete_blob_link
= render 'projects/fork_suggestion' = render 'projects/fork_suggestion'
<<<<<<< HEAD
- if license_allows_file_locks? - if license_allows_file_locks?
:javascript :javascript
...@@ -49,3 +50,5 @@ ...@@ -49,3 +50,5 @@
'#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}', '#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
'#{@path}' '#{@path}'
); );
=======
>>>>>>> upstream/master
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' = 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
.detail-page-description.content-block{ class: ('hide-bottom-border' unless @issue.description.present? ) } .detail-page-description.content-block
.issue-title-data.hidden{ "data" => { "initial-title" => markdown_field(@issue, :title), .issue-title-data.hidden{ "data" => { "initial-title" => markdown_field(@issue, :title),
"endpoint" => rendered_title_namespace_project_issue_path(@project.namespace, @project, @issue), "endpoint" => rendered_title_namespace_project_issue_path(@project.namespace, @project, @issue),
} } } }
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
%a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" }
= icon('angle-double-left') = icon('angle-double-left')
.detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) } .detail-page-description.milestone-detail
%h2.title %h2.title
= markdown_field(@milestone, :title) = markdown_field(@milestone, :title)
%div %div
......
- page_title @service.title, "Services" - page_title @service.title, "Services"
= render "projects/settings/head"
= render 'form' = render 'form'
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%span %span
Members Members
- if can_edit - if can_edit
= nav_link(controller: :integrations) do = nav_link(controller: [:integrations, :services]) do
= link_to project_settings_integrations_path(@project), title: 'Integrations' do = link_to project_settings_integrations_path(@project), title: 'Integrations' do
%span %span
Integrations Integrations
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
- if params[:assignee_id].present? - if params[:assignee_id].present?
= hidden_field_tag(:assignee_id, params[:assignee_id]) = hidden_field_tag(:assignee_id, params[:assignee_id])
= dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user.try(:username), null_user: true, current_user: true, project_id: @project.try(:id), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user.try(:username), null_user: true, current_user: true, project_id: @project.try(:id), group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter .filter-item.inline.milestone-filter
= render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, board: board, show_started: true = render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, board: board, show_started: true
......
-# @project is present when viewing Project's milestone -# @project is present when viewing Project's milestone
- project = @project || issuable.project - project = @project || issuable.project
- namespace = @project_namespace || project.namespace.becomes(Namespace)
- assignee = issuable.assignee - assignee = issuable.assignee
- issuable_type = issuable.class.table_name - issuable_type = issuable.class.table_name
- base_url_args = [project.namespace.becomes(Namespace), project, issuable_type] - base_url_args = [namespace, project]
- issuable_type_args = base_url_args + [issuable_type]
- issuable_url_args = base_url_args + [issuable]
- can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable) - can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable)
%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'is-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-id' => issuable.id, 'data-url' => polymorphic_path(issuable) } %li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'is-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-id' => issuable.id, 'data-url' => polymorphic_path(issuable_url_args) }
%span %span
- if show_project_name - if show_project_name
%strong #{project.name} &middot; %strong #{project.name} &middot;
...@@ -13,17 +16,17 @@ ...@@ -13,17 +16,17 @@
%strong #{project.name_with_namespace} &middot; %strong #{project.name_with_namespace} &middot;
- if issuable.is_a?(Issue) - if issuable.is_a?(Issue)
= confidential_icon(issuable) = confidential_icon(issuable)
= link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title = link_to_gfm issuable.title, issuable_url_args, title: issuable.title
.issuable-detail .issuable-detail
= link_to [project.namespace.becomes(Namespace), project, issuable] do = link_to [project.namespace.becomes(Namespace), project, issuable] do
%span.issuable-number= issuable.to_reference %span.issuable-number= issuable.to_reference
- issuable.labels.each do |label| - issuable.labels.each do |label|
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do = link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do
- render_colored_label(label) - render_colored_label(label)
%span.assignee-icon %span.assignee-icon
- if assignee - if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), = link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
- image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
class ExpirePipelineCacheWorker
include Sidekiq::Worker
include PipelineQueue
def perform(pipeline_id)
pipeline = Ci::Pipeline.find_by(id: pipeline_id)
return unless pipeline
project = pipeline.project
store = Gitlab::EtagCaching::Store.new
store.touch(project_pipelines_path(project))
store.touch(commit_pipelines_path(project, pipeline.commit)) if pipeline.commit
store.touch(new_merge_request_pipelines_path(project))
each_pipelines_merge_request_path(project, pipeline) do |path|
store.touch(path)
end
Gitlab::Cache::Ci::ProjectPipelineStatus.update_for_pipeline(pipeline)
end
private
def project_pipelines_path(project)
Gitlab::Routing.url_helpers.namespace_project_pipelines_path(
project.namespace,
project,
format: :json)
end
def commit_pipelines_path(project, commit)
Gitlab::Routing.url_helpers.pipelines_namespace_project_commit_path(
project.namespace,
project,
commit.id,
format: :json)
end
def new_merge_request_pipelines_path(project)
Gitlab::Routing.url_helpers.new_namespace_project_merge_request_path(
project.namespace,
project,
format: :json)
end
def each_pipelines_merge_request_path(project, pipeline)
pipeline.all_merge_requests.each do |merge_request|
path = Gitlab::Routing.url_helpers.pipelines_namespace_project_merge_request_path(
project.namespace,
project,
merge_request,
format: :json)
yield(path)
end
end
end
---
title: Database SSL support for backup script.
merge_request: 9715
author: Guillaume Simon
---
title: Change issues list in MR to natural sorting
merge_request: 7110
author: Jeff Stubler
---
title: Show group name on flash container when group is created from Admin area.
merge_request: 10905
author:
---
title: Add issues/:iid/closed_by api endpoint
merge_request:
author: mhasbini
---
title: Cleanup markdown spacing
merge_request:
author:
---
title: Decrease ABC threshold to 57.08
merge_request: 10724
author: Rydkin Maxim
---
title: Remove unnecessary test helpers includes
merge_request: 10567
author: Jacopo Beschi @jacopo-beschi
---
title: Add Slack slash command api to services documentation and rearrange order and
cases
merge_request: 10757
author: TM Lee
---
title: Don't display the is_admin flag in most API responses
merge_request: 10846
author:
---
title: Allow OAuth clients to push code
merge_request: 10677
author:
---
title: Decrease Cyclomatic Complexity threshold to 16
merge_request: 10928
author: Rydkin Maxim
---
title: Fixes an issue preventing screen readers from reading some icons
merge_request:
author:
---
title: Add index on ci_builds.user_id
merge_request: 10874
author: blackst0ne
---
title: Fixed spacing of discussion submit buttons
merge_request:
author:
---
title: Fix commenting on an existing discussion on an unchanged line that is no longer
in the diff
merge_request:
author:
---
title: Add index to webhooks type column
merge_request:
author:
---
title: Fix dead link to GDK on the README page
merge_request:
author: Dino Maric
---
title: Fixed group issues assignee dropdown loading all users
merge_request:
author:
---
title: Fixed Prometheus monitoring graphs not showing empty states in certain scenarios
merge_request:
author:
---
title: 'API: Filter merge requests by milestone and labels'
merge_request: Robert Schilling
author: 10924
---
title: Show sizes correctly in merge requests when diffs overflow
merge_request:
author:
---
title: Fix updating merge_when_build_succeeds via merge API endpoint
merge_request: 10873
author:
---
title: Upgrade Sidekiq to 4.2.10
merge_request:
author:
---
title: Cache Routable#full_path in RequestStore to reduce duplicate route loads
merge_request:
author:
...@@ -10,10 +10,13 @@ production: ...@@ -10,10 +10,13 @@ production:
# password: # password:
# host: localhost # host: localhost
# port: 5432 # port: 5432
<<<<<<< HEAD
# load_balancing: # load_balancing:
# hosts: # hosts:
# - host1.example.com # - host1.example.com
# - host2.example.com # - host2.example.com
=======
>>>>>>> upstream/master
# #
# Development specific # Development specific
......
...@@ -592,6 +592,11 @@ production: &base ...@@ -592,6 +592,11 @@ production: &base
# If you use non-standard ssh port you need to specify it # If you use non-standard ssh port you need to specify it
# ssh_port: 22 # ssh_port: 22
workhorse:
# File that contains the secret key for verifying access for gitlab-workhorse.
# Default is '.gitlab_workhorse_secret' relative to Rails.root (i.e. root of the GitLab app).
# secret_file: /home/git/gitlab/.gitlab_workhorse_secret
## Git settings ## Git settings
# CAUTION! # CAUTION!
# Use the default values unless you really know what you are doing # Use the default values unless you really know what you are doing
......
...@@ -463,6 +463,12 @@ Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user ...@@ -463,6 +463,12 @@ Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user
Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user
Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.__send__(:build_gitlab_shell_ssh_path_prefix) Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.__send__(:build_gitlab_shell_ssh_path_prefix)
#
# Workhorse
#
Settings['workhorse'] ||= Settingslogic.new({})
Settings.workhorse['secret_file'] ||= Rails.root.join('.gitlab_workhorse_secret')
# #
# Repositories # Repositories
# #
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexOnCiBuildsUpdatedAt < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :ci_builds, :updated_at
end
def down
remove_concurrent_index :ci_builds, :updated_at if index_exists?(:ci_builds, :updated_at)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexOnCiBuildsUserId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :ci_builds, :user_id
end
def down
remove_concurrent_index :ci_builds, :user_id if index_exists?(:ci_builds, :user_id)
end
end
class AddIndexToWebHooksType < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :web_hooks, :type
end
def down
remove_concurrent_index :web_hooks, :type
end
end
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170421113144) do ActiveRecord::Schema.define(version: 20170421113144) do
=======
ActiveRecord::Schema.define(version: 20170424142900) do
>>>>>>> upstream/master
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -289,6 +293,8 @@ ActiveRecord::Schema.define(version: 20170421113144) do ...@@ -289,6 +293,8 @@ ActiveRecord::Schema.define(version: 20170421113144) do
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
add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree
add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
create_table "ci_pipelines", force: :cascade do |t| create_table "ci_pipelines", force: :cascade do |t|
t.string "ref" t.string "ref"
...@@ -1577,6 +1583,7 @@ ActiveRecord::Schema.define(version: 20170421113144) do ...@@ -1577,6 +1583,7 @@ ActiveRecord::Schema.define(version: 20170421113144) do
end end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
add_index "web_hooks", ["type"], name: "index_web_hooks_on_type", using: :btree
add_foreign_key "approver_groups", "namespaces", column: "group_id", on_delete: :cascade add_foreign_key "approver_groups", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "boards", "projects" add_foreign_key "boards", "projects"
......
# Gitaly # Gitaly
[Gitaly](https://gitlab.com/gitlab-org/gitlay) (introduced in GitLab [Gitaly](https://gitlab.com/gitlab-org/gitaly) (introduced in GitLab
9.0) is a service that provides high-level RPC access to Git 9.0) is a service that provides high-level RPC access to Git
repositories. As of GitLab 9.1 it is still an optional component with repositories. As of GitLab 9.1 it is still an optional component with
limited scope. limited scope.
......
...@@ -48,7 +48,6 @@ Example of response ...@@ -48,7 +48,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"location": null, "location": null,
"name": "Administrator", "name": "Administrator",
...@@ -106,7 +105,6 @@ Example of response ...@@ -106,7 +105,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"location": null, "location": null,
"name": "Administrator", "name": "Administrator",
...@@ -195,7 +193,6 @@ Example of response ...@@ -195,7 +193,6 @@ Example of response
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root", "web_url": "http://localhost:3000/root",
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"is_admin": true,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
......
...@@ -834,6 +834,67 @@ Example response: ...@@ -834,6 +834,67 @@ Example response:
} }
``` ```
## List merge requests that will close issue on merge
Get all the merge requests that will close issue when merged.
```
GET /projects/:id/issues/:issue_iid/closed_by
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
| `issue_iid` | integer | yes | The internal ID of a project issue |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/11/closed_by
```
Example response:
```json
[
{
"id": 6471,
"iid": 6432,
"project_id": 1,
"title": "add a test for cgi lexer options",
"description": "closes #11",
"state": "opened",
"created_at": "2017-04-06T18:33:34.168Z",
"updated_at": "2017-04-09T20:10:24.983Z",
"target_branch": "master",
"source_branch": "feature.custom-highlighting",
"upvotes": 0,
"downvotes": 0,
"author": {
"name": "Administrator",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "https://gitlab.example.com/root"
},
"assignee": null,
"source_project_id": 1,
"target_project_id": 1,
"labels": [],
"work_in_progress": false,
"milestone": null,
"merge_when_pipeline_succeeds": false,
"merge_status": "unchecked",
"sha": "5a62481d563af92b8e32d735f2fa63b94e806835",
"merge_commit_sha": null,
"user_notes_count": 1,
"should_remove_source_branch": null,
"force_remove_source_branch": false,
"web_url": "https://gitlab.example.com/gitlab-org/gitlab-test/merge_requests/6432"
}
]
```
## Comments on issues ## Comments on issues
Comments are done via the [notes](notes.md) resource. Comments are done via the [notes](notes.md) resource.
...@@ -57,7 +57,6 @@ Example of response ...@@ -57,7 +57,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -101,7 +100,6 @@ Example of response ...@@ -101,7 +100,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -120,7 +118,7 @@ Example of response ...@@ -120,7 +118,7 @@ Example of response
Get a list of jobs for a pipeline. Get a list of jobs for a pipeline.
``` ```
GET /projects/:id/pipeline/:pipeline_id/jobs GET /projects/:id/pipelines/:pipeline_id/jobs
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -173,7 +171,6 @@ Example of response ...@@ -173,7 +171,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -217,7 +214,6 @@ Example of response ...@@ -217,7 +214,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -284,7 +280,6 @@ Example of response ...@@ -284,7 +280,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
......
...@@ -26,7 +26,6 @@ Parameters: ...@@ -26,7 +26,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon", "avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2015-09-03T07:24:01.670Z", "created_at": "2015-09-03T07:24:01.670Z",
"is_admin": false,
"bio": null, "bio": null,
"skype": "", "skype": "",
"linkedin": "", "linkedin": "",
......
...@@ -11,15 +11,21 @@ GET /projects/:id/merge_requests ...@@ -11,15 +11,21 @@ GET /projects/:id/merge_requests
GET /projects/:id/merge_requests?state=opened GET /projects/:id/merge_requests?state=opened
GET /projects/:id/merge_requests?state=all GET /projects/:id/merge_requests?state=all
GET /projects/:id/merge_requests?iids[]=42&iids[]=43 GET /projects/:id/merge_requests?iids[]=42&iids[]=43
GET /projects/:id/merge_requests?milestone=release
GET /projects/:id/merge_requests?labels=bug,reproduced
``` ```
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `iid` (optional) - Return the request having the given `iid` | --------- | ---- | -------- | ----------- |
- `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed` | `id` | integer | yes | The ID of a project |
- `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | `iids` | Array[integer] | no | Return the request having the given `iid` |
- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` | `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, or `merged`|
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
| `milestone` | string | no | Return merge requests for a specific milestone |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
```json ```json
[ [
......
...@@ -869,6 +869,17 @@ Parameters: ...@@ -869,6 +869,17 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `file` | string | yes | The file to be uploaded | | `file` | string | yes | The file to be uploaded |
To upload a file from your filesystem, use the `--form` argument. This causes
cURL to post data using the header `Content-Type: multipart/form-data`.
The `file=` parameter must point to a file on your filesystem and be preceded
by `@`. For example:
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "file=@dk.png" https://gitlab.example.com/api/v3/projects/5/uploads
```
Returned object:
```json ```json
{ {
"alt": "dk", "alt": "dk",
...@@ -878,8 +889,8 @@ Parameters: ...@@ -878,8 +889,8 @@ Parameters:
``` ```
**Note**: The returned `url` is relative to the project path. **Note**: The returned `url` is relative to the project path.
In Markdown contexts, the link is automatically expanded when the format in `markdown` is used. In Markdown contexts, the link is automatically expanded when the format in
`markdown` is used.
## Project members ## Project members
......
...@@ -490,41 +490,98 @@ Remove all previously JIRA settings from a project. ...@@ -490,41 +490,98 @@ Remove all previously JIRA settings from a project.
DELETE /projects/:id/services/jira DELETE /projects/:id/services/jira
``` ```
## Mattermost Slash Commands ## Slack slash commands
Ability to receive slash commands from a Mattermost chat instance. Ability to receive slash commands from a Slack chat instance.
### Create/Edit Mattermost Slash Command service ### Get Slack slash command service settings
Set Mattermost Slash Command for a project. Get Slack slash command service settings for a project.
``` ```
PUT /projects/:id/services/mattermost-slash-commands GET /projects/:id/services/slack-slash-commands
```
Example response:
```json
{
"id": 4,
"title": "Slack slash commands",
"created_at": "2017-06-27T05:51:39-07:00",
"updated_at": "2017-06-27T05:51:39-07:00",
"active": true,
"push_events": true,
"issues_events": true,
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"build_events": true,
"pipeline_events": true,
"properties": {
"token": "9koXpg98eAheJpvBs5tK"
}
}
```
### Create/Edit Slack slash command service
Set Slack slash command for a project.
```
PUT /projects/:id/services/slack-slash-commands
``` ```
Parameters: Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `token` | string | yes | The Mattermost token | | `token` | string | yes | The Slack token |
### Delete Mattermost Slash Command service ### Delete Slack slash command service
Delete Mattermost Slash Command service for a project. Delete Slack slash command service for a project.
``` ```
DELETE /projects/:id/services/mattermost-slash-commands DELETE /projects/:id/services/slack-slash-commands
``` ```
### Get Mattermost Slash Command service settings ## Mattermost slash commands
Ability to receive slash commands from a Mattermost chat instance.
### Get Mattermost slash command service settings
Get Mattermost Slash Command service settings for a project. Get Mattermost slash command service settings for a project.
``` ```
GET /projects/:id/services/mattermost-slash-commands GET /projects/:id/services/mattermost-slash-commands
``` ```
### Create/Edit Mattermost slash command service
Set Mattermost slash command for a project.
```
PUT /projects/:id/services/mattermost-slash-commands
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | yes | The Mattermost token |
### Delete Mattermost slash command service
Delete Mattermost slash command service for a project.
```
DELETE /projects/:id/services/mattermost-slash-commands
```
## Pipeline-Emails ## Pipeline-Emails
Get emails for GitLab CI pipelines. Get emails for GitLab CI pipelines.
......
...@@ -64,7 +64,6 @@ GET /users ...@@ -64,7 +64,6 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -97,7 +96,6 @@ GET /users ...@@ -97,7 +96,6 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg",
"web_url": "http://localhost:3000/jack_smith", "web_url": "http://localhost:3000/jack_smith",
"created_at": "2012-05-23T08:01:01Z", "created_at": "2012-05-23T08:01:01Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -171,7 +169,6 @@ Parameters: ...@@ -171,7 +169,6 @@ Parameters:
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -202,7 +199,6 @@ Parameters: ...@@ -202,7 +199,6 @@ Parameters:
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -327,7 +323,6 @@ GET /user ...@@ -327,7 +323,6 @@ GET /user
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
## Backend howtos ## Backend howtos
- [Architecture](architecture.md) of GitLab - [Architecture](architecture.md) of GitLab
- [CI setup](ci_setup.md) for testing GitLab
- [Gotchas](gotchas.md) to avoid - [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md) - [How to dump production data to staging](db_dump.md)
- [Instrumentation](instrumentation.md) - [Instrumentation](instrumentation.md)
......
# CI setup
This document describes what services we use for testing GitLab and GitLab CI.
We currently use four CI services to test GitLab:
1. GitLab CI on [GitHost.io](https://gitlab-ce.githost.io/projects/4/) for the [GitLab.com repo](https://gitlab.com/gitlab-org/gitlab-ce)
2. GitLab CI at ci.gitlab.org to test the private GitLab B.V. repo at dev.gitlab.org
3. [Semephore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
4. [Mock CI Service](../user/project/integrations/mock_ci.md) for local development
| Software @ configuration being tested | GitLab CI (ci.gitlab.org) | GitLab CI (GitHost.io) | Semaphore |
|---------------------------------------|---------------------------|---------------------------------------------------------------------------|-----------|
| GitLab CE @ MySQL | ✓ | ✓ [Core team can trigger builds](https://gitlab-ce.githost.io/projects/4) | |
| GitLab CE @ PostgreSQL | | | ✓ [Core team can trigger builds](https://semaphoreapp.com/gitlabhq/gitlabhq/branches/master) |
| GitLab EE @ MySQL | ✓ | | |
| GitLab CI @ MySQL | ✓ | | |
| GitLab CI @ PostgreSQL | | | ✓ |
| GitLab CI Runner | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ |
Core team has access to trigger builds if needed for GitLab CE.
We use [these build scripts](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) for testing with GitLab CI.
# Build configuration on [Semaphore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for testing the [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
- Language: Ruby
- Ruby version: 2.1.8
- database.yml: pg
Build commands
```bash
sudo apt-get install cmake libicu-dev -y (Setup)
bundle install --deployment --path vendor/bundle (Setup)
cp config/gitlab.yml.example config/gitlab.yml (Setup)
bundle exec rake db:create (Setup)
bundle exec rake spinach (Thread #1)
bundle exec rake spec (thread #2)
bundle exec rake rubocop (thread #3)
bundle exec rake brakeman (thread #4)
bundle exec rake jasmine:ci (thread #5)
```
Use rubygems mirror.
...@@ -75,7 +75,7 @@ sharing a Merge Request with a reviewer or a maintainer. ...@@ -75,7 +75,7 @@ sharing a Merge Request with a reviewer or a maintainer.
1. Follow the steps in [Vue.js Best Practices](vue.md) 1. Follow the steps in [Vue.js Best Practices](vue.md)
1. Follow the style guide. 1. Follow the style guide.
1. Only a handful of people are allowed to merge Vue related features. 1. Only a handful of people are allowed to merge Vue related features.
Reach out to @jschatz, @iamphill, @fatihacet or @filipa early in this process. Reach out to one of Vue experts early in this process.
--- ---
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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