Merge branch 'master' into bootstrap4

This commit is contained in:
Clement Ho 2018-04-16 16:19:03 -05:00
commit 7fd3c8da74
25 changed files with 301 additions and 151 deletions

View file

@ -45,4 +45,4 @@ When removing columns, tables, indexes or other structures:
- [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) - [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
- [ ] Internationalization required/considered - [ ] Internationalization required/considered
- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan - [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan
- [ ] End-to-end tests pass (`package-qa` manual pipeline job) - [ ] End-to-end tests pass (`package-and-qa` manual pipeline job)

View file

@ -206,7 +206,7 @@ GEM
railties (>= 3.0.0) railties (>= 3.0.0)
faraday (0.12.2) faraday (0.12.2)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
faraday_middleware (0.11.0.1) faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0) faraday (>= 0.7.4, < 1.0)
faraday_middleware-multi_json (0.0.6) faraday_middleware-multi_json (0.0.6)
faraday_middleware faraday_middleware

View file

@ -1,3 +1,4 @@
<script>
import successSvg from 'icons/_icon_status_success.svg'; import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg'; import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
@ -7,7 +8,10 @@ import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
name: 'MRWidgetReadyToMerge', name: 'ReadyToMerge',
components: {
statusIcon,
},
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
service: { type: Object, required: true }, service: { type: Object, required: true },
@ -26,9 +30,6 @@ export default {
warningSvg, warningSvg,
}; };
}, },
components: {
statusIcon,
},
computed: { computed: {
shouldShowMergeWhenPipelineSucceedsText() { shouldShowMergeWhenPipelineSucceedsText() {
return this.mr.isPipelineActive; return this.mr.isPipelineActive;
@ -217,7 +218,10 @@ export default {
}); });
}, },
}, },
template: ` };
</script>
<template>
<div class="mr-widget-body media"> <div class="mr-widget-body media">
<status-icon :status="iconClass" /> <status-icon :status="iconClass" />
<div class="media-body"> <div class="media-body">
@ -232,7 +236,8 @@ export default {
<i <i
v-if="isMakingRequest" v-if="isMakingRequest"
class="fa fa-spinner fa-spin" class="fa fa-spinner fa-spin"
aria-hidden="true" /> aria-hidden="true"
></i>
{{ mergeButtonText }} {{ mergeButtonText }}
</button> </button>
<button <button
@ -244,7 +249,8 @@ export default {
aria-label="Select merge moment"> aria-label="Select merge moment">
<i <i
class="fa fa-chevron-down" class="fa fa-chevron-down"
aria-hidden="true" /> aria-hidden="true"
></i>
</button> </button>
<ul <ul
v-if="shouldShowMergeOptionsDropdown" v-if="shouldShowMergeOptionsDropdown"
@ -331,22 +337,27 @@ export default {
<div class="commit-message-container"> <div class="commit-message-container">
<div class="max-width-marker"></div> <div class="max-width-marker"></div>
<textarea <textarea
id="commit-message"
v-model="commitMessage" v-model="commitMessage"
class="form-control js-commit-message" class="form-control js-commit-message"
required="required" required="required"
rows="14" rows="14"
name="Commit message"></textarea> name="Commit message"></textarea>
</div> </div>
<p class="hint">Try to keep the first line under 52 characters and the others under 72</p> <p class="hint">
Try to keep the first line under 52 characters and the others under 72
</p>
<div class="hint"> <div class="hint">
<a <a
@click.prevent="updateCommitMessage" @click.prevent="updateCommitMessage"
href="#">{{commitMessageLinkTitle}}</a> href="#"
>
{{ commitMessageLinkTitle }}
</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
`, </template>
};

View file

@ -27,7 +27,7 @@ export { default as ConflictsState } from './components/states/mr_widget_conflic
export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue'; export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue'; export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
export { default as ReadyToMergeState } from './components/states/mr_widget_ready_to_merge'; export { default as ReadyToMergeState } from './components/states/ready_to_merge.vue';
export { default as ShaMismatchState } from './components/states/sha_mismatch.vue'; export { default as ShaMismatchState } from './components/states/sha_mismatch.vue';
export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue'; export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue';
export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue'; export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue';

View file

@ -23,9 +23,12 @@ class InternalId < ActiveRecord::Base
# #
# The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL). # The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL).
# As such, the increment is atomic and safe to be called concurrently. # As such, the increment is atomic and safe to be called concurrently.
def increment_and_save! #
# If a `maximum_iid` is passed in, this overrides the incremented value if it's
# greater than that. This can be used to correct the increment value if necessary.
def increment_and_save!(maximum_iid)
lock! lock!
self.last_value = (last_value || 0) + 1 self.last_value = [(last_value || 0) + 1, (maximum_iid || 0) + 1].max
save! save!
last_value last_value
end end
@ -89,7 +92,16 @@ class InternalId < ActiveRecord::Base
# and increment its last value # and increment its last value
# #
# Note this will acquire a ROW SHARE lock on the InternalId record # Note this will acquire a ROW SHARE lock on the InternalId record
(lookup || create_record).increment_and_save!
# Note we always calculate the maximum iid present here and
# pass it in to correct the InternalId entry if it's last_value is off.
#
# This can happen in a transition phase where both `AtomicInternalId` and
# `NonatomicInternalId` code runs (e.g. during a deploy).
#
# This is subject to be cleaned up with the 10.8 release:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/45389.
(lookup || create_record).increment_and_save!(maximum_iid)
end end
end end
@ -115,11 +127,15 @@ class InternalId < ActiveRecord::Base
InternalId.create!( InternalId.create!(
**scope, **scope,
usage: usage_value, usage: usage_value,
last_value: init.call(subject) || 0 last_value: maximum_iid
) )
end end
rescue ActiveRecord::RecordNotUnique rescue ActiveRecord::RecordNotUnique
lookup lookup
end end
def maximum_iid
@maximum_iid ||= init.call(subject) || 0
end
end end
end end

View file

@ -0,0 +1,5 @@
---
title: Update faraday_middlewar to 0.12.2
merge_request: 18397
author: Takuya Noguchi
type: security

View file

@ -0,0 +1,5 @@
---
title: Move ReadyToMerge vue component
merge_request: 17545
author: George Tsiolis
type: performance

View file

@ -33,6 +33,10 @@ Follow the steps below to set up a custom hook:
For an installation from source the path is usually For an installation from source the path is usually
`/home/git/gitlab/plugins/`. For Omnibus installs the path is `/home/git/gitlab/plugins/`. For Omnibus installs the path is
usually `/opt/gitlab/embedded/service/gitlab-rails/plugins`. usually `/opt/gitlab/embedded/service/gitlab-rails/plugins`.
For [highly available] configurations, your hook file should exist on each
application server.
1. Inside the `plugins` directory, create a file with a name of your choice, 1. Inside the `plugins` directory, create a file with a name of your choice,
without spaces or special characters. without spaces or special characters.
1. Make the hook file executable and make sure it's owned by the git user. 1. Make the hook file executable and make sure it's owned by the git user.
@ -78,3 +82,4 @@ Validating plugins from /plugins directory
[system hooks]: ../system_hooks/system_hooks.md [system hooks]: ../system_hooks/system_hooks.md
[webhooks]: ../user/project/integrations/webhooks.md [webhooks]: ../user/project/integrations/webhooks.md
[highly available]: ./high_availability/README.md

View file

@ -30,6 +30,7 @@ sast:container:
- mv clair-scanner_linux_amd64 clair-scanner - mv clair-scanner_linux_amd64 clair-scanner
- chmod +x clair-scanner - chmod +x clair-scanner
- touch clair-whitelist.yml - touch clair-whitelist.yml
- while( ! wget -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; done
- ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
artifacts: artifacts:
paths: [gl-sast-container-report.json] paths: [gl-sast-container-report.json]

View file

@ -72,6 +72,18 @@ concurrent: 10
## ##
checkInterval: 30 checkInterval: 30
## For RBAC support:
rbac:
create: false
## Run the gitlab-bastion container with the ability to deploy/manage containers of jobs
## cluster-wide or only within namespace
clusterWideAccess: false
## Use the following Kubernetes Service Account name if RBAC is disabled in this Helm chart (see rbac.create)
##
# serviceAccountName: default
## Configuration for the Pods that that the runner launches for each new job ## Configuration for the Pods that that the runner launches for each new job
## ##
runners: runners:
@ -116,6 +128,12 @@ runners:
``` ```
### Enabling RBAC support
If your cluster has RBAC enabled, you can choose to either have the chart create its own sevice account or provide one.
To have the chart create the service account for you, set `rbac.create` to true.
### Controlling maximum Runner concurrency ### Controlling maximum Runner concurrency
A single GitLab Runner deployed on Kubernetes is able to execute multiple jobs in parallel by automatically starting additional Runner pods. The [`concurrent` setting](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) controls the maximum number of pods allowed at a single time, and defaults to `10`. A single GitLab Runner deployed on Kubernetes is able to execute multiple jobs in parallel by automatically starting additional Runner pods. The [`concurrent` setting](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) controls the maximum number of pods allowed at a single time, and defaults to `10`.

View file

@ -15,6 +15,10 @@ GitLab [administrators](../README.md#administrator-documentation) receive all pe
To add or import a user, you can follow the To add or import a user, you can follow the
[project members documentation](../user/project/members/index.md). [project members documentation](../user/project/members/index.md).
## Principles behind permissions
See our [product handbook on permissions](https://about.gitlab.com/handbook/product#permissions-in-gitlab)
## Project members permissions ## Project members permissions
The following table depicts the various user permission levels in a project. The following table depicts the various user permission levels in a project.

View file

@ -45,6 +45,7 @@ and time spent on
templates for issue and merge request description fields for your project templates for issue and merge request description fields for your project
- [Slash commands (quick actions)](quick_actions.md): Textual shortcuts for - [Slash commands (quick actions)](quick_actions.md): Textual shortcuts for
common actions on issues or merge requests common actions on issues or merge requests
- [Web IDE](web_ide/index.md)
**GitLab CI/CD:** **GitLab CI/CD:**

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,34 @@
# Web IDE
> Introduced in [GitLab Ultimate][ee] 10.4.
> Brought to [GitLab CE][ce] in 10.7.
The Web IDE makes it faster and easier to contribute changes to your projects
by providing an advanced editor with commit staging.
## Open the Web IDE
The Web IDE can be opened when viewing a file, from the repository file list,
and from merge requests.
![Open Web IDE](img/open_web_ide.png)
## Commit changes
Changed files are shown on the right in the commit panel. All changes are
automatically staged. To commit your changes, add a commit message and click
the 'Commit Button'.
![Commit changes](img/commit_changes.png)
## Comparing changes
Before you commit your changes, you can compare them with the previous commit
by switching to the review mode or selecting the file from the staged files
list.
An additional review mode is available when you open a merge request, which
shows you a preview of the merge request diff if you commit your changes.
[ee]: https://about.gitlab.com/products/
[ce]: https://about.gitlab.com/products/

View file

@ -2,7 +2,7 @@ module QA
module Page module Page
module MergeRequest module MergeRequest
class Show < Page::Base class Show < Page::Base
view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js' do view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do
element :merge_button element :merge_button
element :fast_forward_message, 'Fast-forward merge without a merge commit' element :fast_forward_message, 'Fast-forward merge without a merge commit'
end end

View file

@ -1,3 +1,18 @@
export default function removeBreakLine (data) { /**
return data.replace(/\r?\n|\r/g, ' '); * Replaces line break with an empty space
} * @param {*} data
*/
export const removeBreakLine = data => data.replace(/\r?\n|\r/g, ' ');
/**
* Removes line breaks, spaces and trims the given text
* @param {String} str
* @returns {String}
*/
export const trimText = str =>
str
.replace(/\r?\n|\r/g, '')
.replace(/\s\s+/g, ' ')
.trim();
export const removeWhitespace = str => str.replace(/\s\s+/g, ' ');

View file

@ -3,6 +3,7 @@ import store from '~/ide/stores';
import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue'; import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers'; import { file } from '../../helpers';
import { removeWhitespace } from '../../../helpers/vue_component_helper';
describe('Multi-file editor commit sidebar list collapsed', () => { describe('Multi-file editor commit sidebar list collapsed', () => {
let vm; let vm;
@ -23,6 +24,6 @@ describe('Multi-file editor commit sidebar list collapsed', () => {
}); });
it('renders added & modified files count', () => { it('renders added & modified files count', () => {
expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toBe('1 1'); expect(removeWhitespace(vm.$el.textContent).trim()).toBe('1 1');
}); });
}); });

View file

@ -1,7 +1,7 @@
import Vue from 'vue'; import Vue from 'vue';
import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue'; import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import removeBreakLine from 'spec/helpers/vue_component_helper'; import { removeBreakLine } from 'spec/helpers/vue_component_helper';
describe('MRWidgetConflicts', () => { describe('MRWidgetConflicts', () => {
let Component; let Component;

View file

@ -1,7 +1,7 @@
import Vue from 'vue'; import Vue from 'vue';
import pipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue'; import pipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import removeBreakLine from 'spec/helpers/vue_component_helper'; import { removeBreakLine } from 'spec/helpers/vue_component_helper';
describe('MRWidgetPipelineBlocked', () => { describe('MRWidgetPipelineBlocked', () => {
let vm; let vm;

View file

@ -1,12 +1,12 @@
import Vue from 'vue'; import Vue from 'vue';
import readyToMergeComponent from '~/vue_merge_request_widget/components/states/mr_widget_ready_to_merge'; import ReadyToMerge from '~/vue_merge_request_widget/components/states/ready_to_merge.vue';
import eventHub from '~/vue_merge_request_widget/event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
import * as simplePoll from '~/lib/utils/simple_poll'; import * as simplePoll from '~/lib/utils/simple_poll';
const commitMessage = 'This is the commit message'; const commitMessage = 'This is the commit message';
const commitMessageWithDescription = 'This is the commit message description'; const commitMessageWithDescription = 'This is the commit message description';
const createComponent = (customConfig = {}) => { const createComponent = (customConfig = {}) => {
const Component = Vue.extend(readyToMergeComponent); const Component = Vue.extend(ReadyToMerge);
const mr = { const mr = {
isPipelineActive: false, isPipelineActive: false,
pipeline: null, pipeline: null,
@ -36,7 +36,7 @@ const createComponent = (customConfig = {}) => {
}); });
}; };
describe('MRWidgetReadyToMerge', () => { describe('ReadyToMerge', () => {
let vm; let vm;
beforeEach(() => { beforeEach(() => {
@ -49,7 +49,7 @@ describe('MRWidgetReadyToMerge', () => {
describe('props', () => { describe('props', () => {
it('should have props', () => { it('should have props', () => {
const { mr, service } = readyToMergeComponent.props; const { mr, service } = ReadyToMerge.props;
expect(mr.type instanceof Object).toBeTruthy(); expect(mr.type instanceof Object).toBeTruthy();
expect(mr.required).toBeTruthy(); expect(mr.required).toBeTruthy();

View file

@ -1,7 +1,7 @@
import Vue from 'vue'; import Vue from 'vue';
import ShaMismatch from '~/vue_merge_request_widget/components/states/sha_mismatch.vue'; import ShaMismatch from '~/vue_merge_request_widget/components/states/sha_mismatch.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import removeBreakLine from 'spec/helpers/vue_component_helper'; import { removeBreakLine } from 'spec/helpers/vue_component_helper';
describe('ShaMismatch', () => { describe('ShaMismatch', () => {
let vm; let vm;

View file

@ -5,7 +5,7 @@ describe InternalId do
let(:usage) { :issues } let(:usage) { :issues }
let(:issue) { build(:issue, project: project) } let(:issue) { build(:issue, project: project) }
let(:scope) { { project: project } } let(:scope) { { project: project } }
let(:init) { ->(s) { s.project.issues.size } } let(:init) { ->(s) { s.project.issues.maximum(:iid) } }
context 'validations' do context 'validations' do
it { is_expected.to validate_presence_of(:usage) } it { is_expected.to validate_presence_of(:usage) }
@ -39,6 +39,29 @@ describe InternalId do
end end
end end
context 'with an InternalId record present and existing issues with a higher internal id' do
# This can happen if the old NonatomicInternalId is still in use
before do
issues = Array.new(rand(1..10)).map { create(:issue, project: project) }
issue = issues.last
issue.iid = issues.map { |i| i.iid }.max + 1
issue.save
end
let(:maximum_iid) { project.issues.map { |i| i.iid }.max }
it 'updates last_value to the maximum internal id present' do
subject
expect(described_class.find_by(project: project, usage: described_class.usages[usage.to_s]).last_value).to eq(maximum_iid + 1)
end
it 'returns next internal id correctly' do
expect(subject).to eq(maximum_iid + 1)
end
end
context 'with concurrent inserts on table' do context 'with concurrent inserts on table' do
it 'looks up the record if it was created concurrently' do it 'looks up the record if it was created concurrently' do
args = { **scope, usage: described_class.usages[usage.to_s] } args = { **scope, usage: described_class.usages[usage.to_s] }
@ -81,7 +104,8 @@ describe InternalId do
describe '#increment_and_save!' do describe '#increment_and_save!' do
let(:id) { create(:internal_id) } let(:id) { create(:internal_id) }
subject { id.increment_and_save! } let(:maximum_iid) { nil }
subject { id.increment_and_save!(maximum_iid) }
it 'returns incremented iid' do it 'returns incremented iid' do
value = id.last_value value = id.last_value
@ -102,5 +126,14 @@ describe InternalId do
expect(subject).to eq(1) expect(subject).to eq(1)
end end
end end
context 'with maximum_iid given' do
let(:id) { create(:internal_id, last_value: 1) }
let(:maximum_iid) { id.last_value + 10 }
it 'returns maximum_iid instead' do
expect(subject).to eq(12)
end
end
end end
end end

View file

@ -315,6 +315,7 @@ production:
mv clair-scanner_linux_amd64 clair-scanner mv clair-scanner_linux_amd64 clair-scanner
chmod +x clair-scanner chmod +x clair-scanner
touch clair-whitelist.yml touch clair-whitelist.yml
while( ! wget -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; done
./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
} }