Commit 62772e4d authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'ce-to-ee-2018-07-06' into 'master'

CE upstream - 2018-07-06 00:21 UTC

Closes gitaly#1254, #6753, and #6561

See merge request gitlab-org/gitlab-ee!6401
parents 9429048d db07a722
...@@ -150,6 +150,10 @@ All documentation can be found on [doc.gitlab.com/ee/](http://doc.gitlab.com/ee/ ...@@ -150,6 +150,10 @@ All documentation can be found on [doc.gitlab.com/ee/](http://doc.gitlab.com/ee/
Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help. Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help.
## Why?
[Read here](https://about.gitlab.com/why/)
## Is it any good? ## Is it any good?
[Yes](https://news.ycombinator.com/item?id=3067434) [Yes](https://news.ycombinator.com/item?id=3067434)
......
...@@ -85,9 +85,9 @@ export const allDiscussions = (state, getters) => { ...@@ -85,9 +85,9 @@ export const allDiscussions = (state, getters) => {
export const resolvedDiscussionsById = state => { export const resolvedDiscussionsById = state => {
const map = {}; const map = {};
state.discussions.forEach(n => { state.discussions.filter(d => d.resolvable).forEach(n => {
if (n.notes) { if (n.notes) {
const resolved = n.notes.every(note => note.resolved && !note.system); const resolved = n.notes.filter(note => note.resolvable).every(note => note.resolved);
if (resolved) { if (resolved) {
map[n.id] = n; map[n.id] = n;
......
...@@ -242,7 +242,7 @@ class KubernetesService < DeploymentService ...@@ -242,7 +242,7 @@ class KubernetesService < DeploymentService
end end
def deprecation_validation def deprecation_validation
return if active_changed?(from: true, to: false) return if active_changed?(from: true, to: false) || (new_record? && !active?)
if deprecated? if deprecated?
errors[:base] << deprecation_message errors[:base] << deprecation_message
......
...@@ -286,9 +286,9 @@ class Service < ActiveRecord::Base ...@@ -286,9 +286,9 @@ class Service < ActiveRecord::Base
def self.build_from_template(project_id, template) def self.build_from_template(project_id, template)
service = template.dup service = template.dup
service.active = false unless service.valid?
service.template = false service.template = false
service.project_id = project_id service.project_id = project_id
service.active = false if service.active? && !service.valid?
service service
end end
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#{time_ago_with_tooltip(@build.artifacts_expire_at)} #{time_ago_with_tooltip(@build.artifacts_expire_at)}
- elsif @build.has_expiring_artifacts? - elsif @build.has_expiring_artifacts?
%p.build-detail-row %p.build-detail-row
The artifacts will be removed in The artifacts will be removed
%span= time_ago_with_tooltip @build.artifacts_expire_at #{time_ago_with_tooltip(@build.artifacts_expire_at)}
- if @build.artifacts? - if @build.artifacts?
.btn-group.d-flex{ role: :group } .btn-group.d-flex{ role: :group }
......
...@@ -117,6 +117,9 @@ ...@@ -117,6 +117,9 @@
%li %li
JaCoCo (Java/Kotlin) JaCoCo (Java/Kotlin)
%code Total.*?([0-9]{1,3})% %code Total.*?([0-9]{1,3})%
%li
go test -cover (Go)
%code coverage: \d+.\d+% of statements
= f.submit _('Save changes'), class: "btn btn-save" = f.submit _('Save changes'), class: "btn btn-save"
......
---
title: Fix double "in" in time to artifact deletion message
merge_request: 20357
author: "@bbodenmiller"
type: fixed
---
title: Deactivate new KubernetesService created from active template to prevent project creation from failing
merge_request:
author:
type: fixed
...@@ -530,6 +530,39 @@ GET /projects/:id ...@@ -530,6 +530,39 @@ GET /projects/:id
} }
``` ```
If the project is a fork, and you provide a valid token to authenticate, the
`forked_from_project` field will appear in the response.
```json
{
"id":3,
...
"forked_from_project":{
"id":13083,
"description":"GitLab Community Edition",
"name":"GitLab Community Edition",
"name_with_namespace":"GitLab.org / GitLab Community Edition",
"path":"gitlab-ce",
"path_with_namespace":"gitlab-org/gitlab-ce",
"created_at":"2013-09-26T06:02:36.000Z",
"default_branch":"master",
"tag_list":[],
"ssh_url_to_repo":"git@gitlab.com:gitlab-org/gitlab-ce.git",
"http_url_to_repo":"https://gitlab.com/gitlab-org/gitlab-ce.git",
"web_url":"https://gitlab.com/gitlab-org/gitlab-ce",
"avatar_url":"https://assets.gitlab-static.net/uploads/-/system/project/avatar/13083/logo-extra-whitespace.png",
"star_count":3812,
"forks_count":3561,
"last_activity_at":"2018-01-02T11:40:26.570Z"
}
...
}
```
## Get project users ## Get project users
Get the users list of a project. Get the users list of a project.
......
...@@ -32,6 +32,12 @@ module QA ...@@ -32,6 +32,12 @@ module QA
end end
def self.configure! def self.configure!
RSpec.configure do |config|
config.define_derived_metadata(file_path: %r{/qa/specs/features/}) do |metadata|
metadata[:type] = :feature
end
end
return if Capybara.drivers.include?(:chrome) return if Capybara.drivers.include?(:chrome)
Capybara.register_driver :chrome do |app| Capybara.register_driver :chrome do |app|
......
...@@ -80,6 +80,13 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont ...@@ -80,6 +80,13 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
render_discussions_json(merge_request, example.description) render_discussions_json(merge_request, example.description)
end end
it 'merge_requests/resolved_diff_discussion.json' do |example|
note = create(:discussion_note_on_merge_request, :resolved, project: project, author: admin, position: position, noteable: merge_request)
create(:system_note, project: project, author: admin, noteable: merge_request, discussion_id: note.discussion.id)
render_discussions_json(merge_request, example.description)
end
context 'with image diff' do context 'with image diff' do
let(:merge_request2) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, title: "Added images") } let(:merge_request2) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, title: "Added images") }
let(:image_path) { "files/images/ee_repo_logo.png" } let(:image_path) { "files/images/ee_repo_logo.png" }
......
...@@ -32,12 +32,12 @@ describe('DiscussionCounter component', () => { ...@@ -32,12 +32,12 @@ describe('DiscussionCounter component', () => {
{ {
...discussionMock, ...discussionMock,
id: discussionMock.id, id: discussionMock.id,
notes: [{ ...discussionMock.notes[0], resolved: true }], notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }],
}, },
{ {
...discussionMock, ...discussionMock,
id: discussionMock.id + 1, id: discussionMock.id + 1,
notes: [{ ...discussionMock.notes[0], resolved: false }], notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: false }],
}, },
]; ];
const firstDiscussionId = discussionMock.id + 1; const firstDiscussionId = discussionMock.id + 1;
......
...@@ -4,22 +4,23 @@ import noteableDiscussion from '~/notes/components/noteable_discussion.vue'; ...@@ -4,22 +4,23 @@ import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
import '~/behaviors/markdown/render_gfm'; import '~/behaviors/markdown/render_gfm';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data'; import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json';
describe('noteable_discussion component', () => { describe('noteable_discussion component', () => {
const Component = Vue.extend(noteableDiscussion);
let store; let store;
let vm; let vm;
beforeEach(() => { preloadFixtures(discussionWithTwoUnresolvedNotes);
const Component = Vue.extend(noteableDiscussion);
beforeEach(() => {
store = createStore(); store = createStore();
store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock); store.dispatch('setNotesData', notesDataMock);
vm = new Component({ vm = new Component({
store, store,
propsData: { propsData: { discussion: discussionMock },
discussion: discussionMock,
},
}).$mount(); }).$mount();
}); });
...@@ -84,7 +85,9 @@ describe('noteable_discussion component', () => { ...@@ -84,7 +85,9 @@ describe('noteable_discussion component', () => {
}); });
it('is true if there are two unresolved discussions', done => { it('is true if there are two unresolved discussions', done => {
spyOnProperty(vm, 'unresolvedDiscussions').and.returnValue([{}, {}]); const discussion = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
discussion.notes[0].resolved = false;
vm.$store.dispatch('setInitialNotes', [discussion, discussion]);
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
...@@ -105,12 +108,12 @@ describe('noteable_discussion component', () => { ...@@ -105,12 +108,12 @@ describe('noteable_discussion component', () => {
{ {
...discussionMock, ...discussionMock,
id: discussionMock.id + 1, id: discussionMock.id + 1,
notes: [{ ...discussionMock.notes[0], resolved: true }], notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }],
}, },
{ {
...discussionMock, ...discussionMock,
id: discussionMock.id + 2, id: discussionMock.id + 2,
notes: [{ ...discussionMock.notes[0], resolved: false }], notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: false }],
}, },
]; ];
const nextDiscussionId = discussionMock.id + 2; const nextDiscussionId = discussionMock.id + 2;
......
...@@ -303,6 +303,7 @@ export const discussionMock = { ...@@ -303,6 +303,7 @@ export const discussionMock = {
}, },
], ],
individual_note: false, individual_note: false,
resolvable: true,
}; };
export const loggedOutnoteableData = { export const loggedOutnoteableData = {
......
...@@ -7,9 +7,13 @@ import { ...@@ -7,9 +7,13 @@ import {
collapseNotesMock, collapseNotesMock,
} from '../mock_data'; } from '../mock_data';
const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json';
describe('Getters Notes Store', () => { describe('Getters Notes Store', () => {
let state; let state;
preloadFixtures(discussionWithTwoUnresolvedNotes);
beforeEach(() => { beforeEach(() => {
state = { state = {
discussions: [individualNote], discussions: [individualNote],
...@@ -22,12 +26,26 @@ describe('Getters Notes Store', () => { ...@@ -22,12 +26,26 @@ describe('Getters Notes Store', () => {
noteableData: noteableDataMock, noteableData: noteableDataMock,
}; };
}); });
describe('discussions', () => { describe('discussions', () => {
it('should return all discussions in the store', () => { it('should return all discussions in the store', () => {
expect(getters.discussions(state)).toEqual([individualNote]); expect(getters.discussions(state)).toEqual([individualNote]);
}); });
}); });
describe('resolvedDiscussionsById', () => {
it('ignores unresolved system notes', () => {
const [discussion] = getJSONFixture(discussionWithTwoUnresolvedNotes);
discussion.notes[0].resolved = true;
discussion.notes[1].resolved = false;
state.discussions.push(discussion);
expect(getters.resolvedDiscussionsById(state)).toEqual({
[discussion.id]: discussion,
});
});
});
describe('Collapsed notes', () => { describe('Collapsed notes', () => {
const stateCollapsedNotes = { const stateCollapsedNotes = {
discussions: collapseNotesMock, discussions: collapseNotesMock,
......
...@@ -78,7 +78,7 @@ describe Service do ...@@ -78,7 +78,7 @@ describe Service do
context 'when template is invalid' do context 'when template is invalid' do
it 'sets service template to inactive when template is invalid' do it 'sets service template to inactive when template is invalid' do
project = create(:project) project = create(:project)
template = JiraService.new(template: true, active: true) template = KubernetesService.new(template: true, active: true)
template.save(validate: false) template.save(validate: false)
service = described_class.build_from_template(project.id, template) service = described_class.build_from_template(project.id, template)
......
...@@ -24,7 +24,9 @@ module WaitForRequests ...@@ -24,7 +24,9 @@ module WaitForRequests
# Wait for client-side AJAX requests # Wait for client-side AJAX requests
def wait_for_requests def wait_for_requests
wait_for('JS requests complete') { finished_all_js_requests? } wait_for('JS requests complete', max_wait_time: 2 * Capybara.default_max_wait_time) do
finished_all_js_requests?
end
end end
# Wait for active Rack requests and client-side AJAX requests # Wait for active Rack requests and client-side AJAX requests
......
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