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

Merge branch 'ce-to-ee-2018-08-19' into 'master'

CE upstream - 2018-08-19 06:21 UTC

See merge request gitlab-org/gitlab-ee!6941
parents 89b4075d 4a556dcb
<script>
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
components: {
CiIcon,
Icon,
},
directives: {
tooltip,
},
props: {
jobs: {
type: Array,
required: true,
},
},
};
</script>
<template>
<div class="builds-container">
<div
class="build-job"
>
<a
v-tooltip
v-for="job in jobs"
:key="job.id"
:href="job.path"
:title="job.tooltip"
:class="{ active: job.active, retried: job.retried }"
>
<icon
v-if="job.active"
name="arrow-right"
class="js-arrow-right"
/>
<ci-icon :status="job.status" />
<span>
<template v-if="job.name">
{{ job.name }}
</template>
<template v-else>
{{ job.id }}
</template>
</span>
<icon
v-if="job.retried"
name="retry"
class="js-retry-icon"
/>
</a>
</div>
</div>
</template>
<script>
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import { sprintf, __ } from '~/locale';
export default {
components: {
CiIcon,
Icon,
},
props: {
pipelineId: {
type: Number,
required: true,
},
pipelinePath: {
type: String,
required: true,
},
pipelineRef: {
type: String,
required: true,
},
pipelineRefPath: {
type: String,
required: true,
},
stages: {
type: Array,
required: true,
},
pipelineStatus: {
type: Object,
required: true,
},
},
data() {
return {
selectedStage: this.stages.length > 0 ? this.stages[0].name : __('More'),
};
},
computed: {
pipelineLink() {
return sprintf(__('Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}'), {
pipelineLinkStart: `<a href=${this.pipelinePath} class="js-pipeline-path link-commit">`,
pipelineId: this.pipelineId,
pipelineLinkEnd: '</a>',
pipelineLinkRefStart: `<a href=${this.pipelineRefPath} class="link-commit ref-name">`,
pipelineRef: this.pipelineRef,
pipelineLinkRefEnd: '</a>',
}, false);
},
},
methods: {
onStageClick(stage) {
// todo: consider moving into store
this.selectedStage = stage.name;
// update dropdown with jobs
// jobs container is a new component.
this.$emit('requestSidebarStageDropdown', stage);
},
},
};
</script>
<template>
<div class="block-last">
<ci-icon :status="pipelineStatus" />
<p v-html="pipelineLink"></p>
<div class="dropdown">
<button
type="button"
data-toggle="dropdown"
>
{{ selectedStage }}
<icon name="chevron-down" />
</button>
<ul class="dropdown-menu">
<li
v-for="(stage, index) in stages"
:key="index"
>
<button
type="button"
class="stage-item"
@click="onStageClick(stage)"
>
{{ stage.name }}
</button>
</li>
</ul>
</div>
</div>
</template>
---
title: Creates vue components for stage dropdowns and job list container for job log
view
merge_request:
author:
type: other
---
title: Bump Gitaly to 0.117.1 for Rouge update
merge_request: 21277
author:
type: security
......@@ -55,14 +55,14 @@ Below is an example of an NFS mount point defined in `/etc/fstab` we use on
GitLab.com:
```
10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
```
Notice several options that you should consider using:
| Setting | Description |
| ------- | ----------- |
| `nobootwait` | Don't halt boot process waiting for this mount to become available
| `nofail` | Don't halt boot process waiting for this mount to become available
| `lookupcache=positive` | Tells the NFS client to honor `positive` cache results but invalidates any `negative` cache results. Negative cache results cause problems with Git. Specifically, a `git push` can fail to register uniformly across all NFS clients. The negative cache causes the clients to 'remember' that the files did not exist previously.
## A single NFS mount
......
......@@ -57,7 +57,13 @@ description: 'Learn how to contribute to GitLab.'
- [Merge request performance guidelines](merge_request_performance_guidelines.md)
for ensuring merge requests do not negatively impact GitLab performance
## Databases guides
## Database guides
### Tooling
- [Understanding EXPLAIN plans](understanding_explain_plans.md)
- [explain.depesz.com](https://explain.depesz.com/) for visualising the output
of `EXPLAIN`
### Migrations
......
......@@ -258,6 +258,31 @@ end
[`extend ::Gitlab::Utils::Override`]: utilities.md#override
##### Overriding CE class methods
The same applies to class methods, except we want to use
`ActiveSupport::Concern` and put `extend ::Gitlab::Utils::Override`
within the block of `class_methods`. Here's an example:
```ruby
module EE
module Groups
module GroupMembersController
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :admin_not_required_endpoints
def admin_not_required_endpoints
super.concat(%i[update override])
end
end
end
end
end
```
#### Use self-descriptive wrapper methods
When it's not possible/logical to modify the implementation of a
......@@ -665,6 +690,9 @@ module EE
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :update_params_at_least_one_of
def update_params_at_least_one_of
super.push(*%i[
squash
......
This diff is collapsed.
......@@ -5206,6 +5206,9 @@ msgstr ""
msgid "Pipeline"
msgstr ""
msgid "Pipeline %{pipelineLinkStart} #%{pipelineId} %{pipelineLinkEnd} from %{pipelineLinkRefStart} %{pipelineRef} %{pipelineLinkRefEnd}"
msgstr ""
msgid "Pipeline Health"
msgstr ""
......
import Vue from 'vue';
import component from '~/jobs/components/jobs_container.vue';
import mountComponent from '../helpers/vue_mount_component_helper';
describe('Artifacts block', () => {
const Component = Vue.extend(component);
let vm;
const retried = {
status: {
details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
path: 'job/233432756',
id: '233432756',
tooltip: 'build - passed',
retried: true,
};
const active = {
name: 'test',
status: {
details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
path: 'job/2322756',
id: '2322756',
tooltip: 'build - passed',
active: true,
};
const job = {
name: 'build',
status: {
details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
path: 'job/232153',
id: '232153',
tooltip: 'build - passed',
};
afterEach(() => {
vm.$destroy();
});
it('renders list of jobs', () => {
vm = mountComponent(Component, {
jobs: [job, retried, active],
});
expect(vm.$el.querySelectorAll('a').length).toEqual(3);
});
it('renders arrow right when job is active', () => {
vm = mountComponent(Component, {
jobs: [active],
});
expect(vm.$el.querySelector('a .js-arrow-right')).not.toBeNull();
});
it('does not render arrow right when job is not active', () => {
vm = mountComponent(Component, {
jobs: [job],
});
expect(vm.$el.querySelector('a .js-arrow-right')).toBeNull();
});
it('renders job name when present', () => {
vm = mountComponent(Component, {
jobs: [job],
});
expect(vm.$el.querySelector('a').textContent.trim()).toContain(job.name);
expect(vm.$el.querySelector('a').textContent.trim()).not.toContain(job.id);
});
it('renders job id when job name is not available', () => {
vm = mountComponent(Component, {
jobs: [retried],
});
expect(vm.$el.querySelector('a').textContent.trim()).toContain(retried.id);
});
it('links to the job page', () => {
vm = mountComponent(Component, {
jobs: [job],
});
expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(job.path);
});
it('renders retry icon when job was retried', () => {
vm = mountComponent(Component, {
jobs: [retried],
});
expect(vm.$el.querySelector('.js-retry-icon')).not.toBeNull();
});
it('does not render retry icon when job was not retried', () => {
vm = mountComponent(Component, {
jobs: [job],
});
expect(vm.$el.querySelector('.js-retry-icon')).toBeNull();
});
});
import Vue from 'vue';
import component from '~/jobs/components/stages_dropdown.vue';
import mountComponent from '../helpers/vue_mount_component_helper';
describe('Artifacts block', () => {
const Component = Vue.extend(component);
let vm;
beforeEach(() => {
vm = mountComponent(Component, {
pipelineId: 28029444,
pipelinePath: 'pipeline/28029444',
pipelineRef: '50101-truncated-job-information',
pipelineRefPath: 'commits/50101-truncated-job-information',
stages: [
{
name: 'build',
},
{
name: 'test',
},
],
pipelineStatus: {
details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
});
});
afterEach(() => {
vm.$destroy();
});
it('renders pipeline status', () => {
expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
});
it('renders pipeline link', () => {
expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
'pipeline/28029444',
);
});
it('renders dropdown with stages', () => {
expect(vm.$el.querySelector('.dropdown button').textContent).toContain('build');
});
it('updates selected stage on click', done => {
vm.$el.querySelectorAll('.stage-item')[1].click();
vm
.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.dropdown button').textContent).toContain('test');
})
.then(done)
.catch(done.fail);
});
});
......@@ -542,7 +542,7 @@ describe Gitlab::Git::Repository, :seed_helper do
Gitlab::Shell.new.remove_repository('default', 'my_project')
end
shared_examples 'repository mirror fecthing' do
shared_examples 'repository mirror fetching' do
it 'fetches a repository as a mirror remote' do
subject
......@@ -569,11 +569,11 @@ describe Gitlab::Git::Repository, :seed_helper do
end
context 'with gitaly enabled' do
it_behaves_like 'repository mirror fecthing'
it_behaves_like 'repository mirror fetching'
end
context 'with gitaly enabled', :skip_gitaly_mock do
it_behaves_like 'repository mirror fecthing'
it_behaves_like 'repository mirror fetching'
end
def new_repository_path
......
......@@ -83,11 +83,6 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(response).to have_gitlab_http_status(403)
end
end
it "returns a 404 error if hook id is not available" do
get api("/projects/#{project.id}/hooks/1234", user)
expect(response).to have_gitlab_http_status(404)
end
end
describe "POST /projects/:id/hooks" do
......
......@@ -42,7 +42,7 @@ describe API::ProjectImport do
expect(response).to have_gitlab_http_status(201)
end
it 'does not shedule an import for a nampespace that does not exist' do
it 'does not schedule an import for a namespace that does not exist' do
expect_any_instance_of(Project).not_to receive(:import_schedule)
expect(::Projects::CreateService).not_to receive(:new)
......
......@@ -20,7 +20,6 @@ describe API::Projects do
let(:admin) { create(:admin) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
let(:project2) { create(:project, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
let(:project3) do
......@@ -575,7 +574,7 @@ describe API::Projects do
expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
end
it 'sets a project as allowing outdated diff discussions to automatically resolve' do
it 'sets a project as not allowing outdated diff discussions to automatically resolve' do
project = attributes_for(:project, resolve_outdated_diff_discussions: false)
post api('/projects', user), project
......@@ -583,7 +582,7 @@ describe API::Projects do
expect(json_response['resolve_outdated_diff_discussions']).to be_falsey
end
it 'sets a project as allowing outdated diff discussions to automatically resolve if resolve_outdated_diff_discussions' do
it 'sets a project as allowing outdated diff discussions to automatically resolve' do
project = attributes_for(:project, resolve_outdated_diff_discussions: true)
post api('/projects', user), project
......@@ -698,7 +697,7 @@ describe API::Projects do
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end
it 'returns projects filetered by minimal access level' do
it 'returns projects filtered by minimal access level' do
private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
private_project1.add_developer(user2)
......@@ -789,7 +788,7 @@ describe API::Projects do
expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing outdated diff discussions to automatically resolve' do
it 'sets a project as not allowing outdated diff discussions to automatically resolve' do
project = attributes_for(:project, resolve_outdated_diff_discussions: false)
post api("/projects/user/#{user.id}", admin), project
......@@ -1169,100 +1168,6 @@ describe API::Projects do
end
end
describe 'GET /projects/:id/snippets' do
before do
snippet
end
it 'returns an array of project snippets' do
get api("/projects/#{project.id}/snippets", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(snippet.title)
end
end
describe 'GET /projects/:id/snippets/:snippet_id' do
it 'returns a project snippet' do
get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq(snippet.title)
end
it 'returns a 404 error if snippet id not found' do
get api("/projects/#{project.id}/snippets/1234", user)
expect(response).to have_gitlab_http_status(404)
end
end
describe 'POST /projects/:id/snippets' do
it 'creates a new project snippet' do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('api test')
end
it 'returns a 400 error if invalid snippet is given' do
post api("/projects/#{project.id}/snippets", user)
expect(status).to eq(400)
end
end
describe 'PUT /projects/:id/snippets/:snippet_id' do
it 'updates an existing project snippet' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
code: 'updated code'
expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq('example')
expect(snippet.reload.content).to eq('updated code')
end
it 'updates an existing project snippet with new title' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
title: 'other api test'
expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq('other api test')
end
end
describe 'DELETE /projects/:id/snippets/:snippet_id' do
before do
snippet
end
it 'deletes existing project snippet' do
expect do
delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
expect(response).to have_gitlab_http_status(204)
end.to change { Snippet.count }.by(-1)
end
it 'returns 404 when deleting unknown snippet id' do
delete api("/projects/#{project.id}/snippets/1234", user)
expect(response).to have_gitlab_http_status(404)
end
it_behaves_like '412 response' do
let(:request) { api("/projects/#{project.id}/snippets/#{snippet.id}", user) }
end
end
describe 'GET /projects/:id/snippets/:snippet_id/raw' do
it 'gets a raw project snippet' do
get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
expect(response).to have_gitlab_http_status(200)
end
it 'returns a 404 error if raw project snippet not found' do
get api("/projects/#{project.id}/snippets/5555/raw", user)
expect(response).to have_gitlab_http_status(404)
end
end
describe 'fork management' do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) }
......@@ -1285,7 +1190,7 @@ describe API::Projects do
expect(project_fork_target.forked?).to be_truthy
end
it 'refreshes the forks count cachce' do
it 'refreshes the forks count cache' do
expect(project_fork_source.forks_count).to be_zero
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
......
......@@ -102,14 +102,6 @@ shared_examples_for 'group and project milestones' do |route_definition|
expect(json_response['iid']).to eq(milestone.iid)
end
it 'returns a milestone by id' do
get api(resource_route, user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq(milestone.title)
expect(json_response['iid']).to eq(milestone.iid)
end
it 'returns 401 error if user not authenticated' do
get api(resource_route)
......
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