Commit f11aa51e authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master' into build-chunks-on-object-storage

parents 93a964d4 4c1a2a9b
...@@ -285,7 +285,8 @@ review-docs-deploy-manual: ...@@ -285,7 +285,8 @@ review-docs-deploy-manual:
- ./$SCRIPT_NAME deploy - ./$SCRIPT_NAME deploy
when: manual when: manual
only: only:
- branches - branches@gitlab-org/gitlab-ce
- branches@gitlab-org/gitlab-ee
<<: *except-docs-and-qa <<: *except-docs-and-qa
# Always trigger a docs build in gitlab-docs only on docs-only branches. # Always trigger a docs build in gitlab-docs only on docs-only branches.
...@@ -298,6 +299,8 @@ review-docs-deploy: ...@@ -298,6 +299,8 @@ review-docs-deploy:
- ./$SCRIPT_NAME deploy - ./$SCRIPT_NAME deploy
only: only:
- /(^docs[\/-].*|.*-docs$)/ - /(^docs[\/-].*|.*-docs$)/
- branches@gitlab-org/gitlab-ce
- branches@gitlab-org/gitlab-ee
<<: *except-qa <<: *except-qa
# Cleanup remote environment of gitlab-docs # Cleanup remote environment of gitlab-docs
......
...@@ -39,6 +39,7 @@ Set the title to: `[Security] Description of the original issue` ...@@ -39,6 +39,7 @@ Set the title to: `[Security] Description of the original issue`
- [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details) - [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details)
### Summary ### Summary
#### Links #### Links
| Description | Link | | Description | Link |
......
Add a description of your merge request here. Merge requests without an adequate Add a description of your merge request here. Merge requests without an adequate
description will not be reviewed until one is added. description will not be reviewed until one is added.
## Database Checklist ## Database checklist
When adding migrations: When adding migrations:
...@@ -31,7 +31,7 @@ When removing columns, tables, indexes or other structures: ...@@ -31,7 +31,7 @@ When removing columns, tables, indexes or other structures:
- [ ] Removed these in a post-deployment migration - [ ] Removed these in a post-deployment migration
- [ ] Made sure the application no longer uses (or ignores) these structures - [ ] Made sure the application no longer uses (or ignores) these structures
## General Checklist ## General checklist
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary - [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html) - [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html)
......
...@@ -35,7 +35,7 @@ We're hiring developers, support people, and production engineers all the time, ...@@ -35,7 +35,7 @@ We're hiring developers, support people, and production engineers all the time,
There are two editions of GitLab: There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license. - GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/products/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/products/). - GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/pricing/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
## Website ## Website
......
...@@ -39,12 +39,12 @@ export default { ...@@ -39,12 +39,12 @@ export default {
<div class="diff-viewer"> <div class="diff-viewer">
<template v-if="isTextFile"> <template v-if="isTextFile">
<inline-diff-view <inline-diff-view
v-if="isInlineView" v-show="isInlineView"
:diff-file="diffFile" :diff-file="diffFile"
:diff-lines="diffFile.highlightedDiffLines || []" :diff-lines="diffFile.highlightedDiffLines || []"
/> />
<parallel-diff-view <parallel-diff-view
v-else-if="isParallelView" v-show="isParallelView"
:diff-file="diffFile" :diff-file="diffFile"
:diff-lines="diffFile.parallelDiffLines || []" :diff-lines="diffFile.parallelDiffLines || []"
/> />
......
...@@ -10,6 +10,9 @@ import { ...@@ -10,6 +10,9 @@ import {
NEW_NO_NEW_LINE_TYPE, NEW_NO_NEW_LINE_TYPE,
LINE_HOVER_CLASS_NAME, LINE_HOVER_CLASS_NAME,
LINE_UNFOLD_CLASS_NAME, LINE_UNFOLD_CLASS_NAME,
INLINE_DIFF_VIEW_TYPE,
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
} from '../constants'; } from '../constants';
export default { export default {
...@@ -25,6 +28,11 @@ export default { ...@@ -25,6 +28,11 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
diffViewType: {
type: String,
required: false,
default: INLINE_DIFF_VIEW_TYPE,
},
showCommentButton: { showCommentButton: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -57,13 +65,19 @@ export default { ...@@ -57,13 +65,19 @@ export default {
}, },
}, },
computed: { computed: {
...mapGetters(['isLoggedIn', 'isInlineView']), ...mapGetters(['isLoggedIn']),
normalizedLine() { normalizedLine() {
if (this.isInlineView) { let normalizedLine;
return this.line;
if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) {
normalizedLine = this.line;
} else if (this.linePosition === LINE_POSITION_LEFT) {
normalizedLine = this.line.left;
} else if (this.linePosition === LINE_POSITION_RIGHT) {
normalizedLine = this.line.right;
} }
return this.lineType === OLD_LINE_TYPE ? this.line.left : this.line.right; return normalizedLine;
}, },
isMatchLine() { isMatchLine() {
return this.normalizedLine.type === MATCH_LINE_TYPE; return this.normalizedLine.type === MATCH_LINE_TYPE;
...@@ -72,10 +86,10 @@ export default { ...@@ -72,10 +86,10 @@ export default {
return this.normalizedLine.type === CONTEXT_LINE_TYPE; return this.normalizedLine.type === CONTEXT_LINE_TYPE;
}, },
isMetaLine() { isMetaLine() {
const { type } = this.normalizedLine;
return ( return (
this.normalizedLine.type === OLD_NO_NEW_LINE_TYPE || type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
this.normalizedLine.type === NEW_NO_NEW_LINE_TYPE ||
this.normalizedLine.type === EMPTY_CELL_TYPE
); );
}, },
classNameMap() { classNameMap() {
......
<script>
import { mapGetters } from 'vuex';
import DiffTableCell from './diff_table_cell.vue';
import {
NEW_LINE_TYPE,
OLD_LINE_TYPE,
CONTEXT_LINE_TYPE,
CONTEXT_LINE_CLASS_NAME,
PARALLEL_DIFF_VIEW_TYPE,
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
} from '../constants';
export default {
components: {
DiffTableCell,
},
props: {
diffFile: {
type: Object,
required: true,
},
line: {
type: Object,
required: true,
},
isBottom: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
isHover: false,
};
},
computed: {
...mapGetters(['isInlineView']),
isContextLine() {
return this.line.type === CONTEXT_LINE_TYPE;
},
classNameMap() {
return {
[this.line.type]: this.line.type,
[CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
[PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
};
},
inlineRowId() {
const { lineCode, oldLine, newLine } = this.line;
return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`;
},
},
created() {
this.newLineType = NEW_LINE_TYPE;
this.oldLineType = OLD_LINE_TYPE;
this.linePositionLeft = LINE_POSITION_LEFT;
this.linePositionRight = LINE_POSITION_RIGHT;
},
methods: {
handleMouseMove(e) {
// To show the comment icon on the gutter we need to know if we hover the line.
// Current table structure doesn't allow us to do this with CSS in both of the diff view types
this.isHover = e.type === 'mouseover';
},
},
};
</script>
<template>
<tr
:id="inlineRowId"
:class="classNameMap"
class="line_holder"
@mouseover="handleMouseMove"
@mouseout="handleMouseMove"
>
<diff-table-cell
:diff-file="diffFile"
:line="line"
:line-type="oldLineType"
:is-bottom="isBottom"
:is-hover="isHover"
:show-comment-button="true"
class="diff-line-num old_line"
/>
<diff-table-cell
:diff-file="diffFile"
:line="line"
:line-type="newLineType"
:is-bottom="isBottom"
:is-hover="isHover"
class="diff-line-num new_line"
/>
<diff-table-cell
:class="line.type"
:diff-file="diffFile"
:line="line"
:is-content-line="true"
/>
</tr>
</template>
<script> <script>
import diffContentMixin from '../mixins/diff_content'; import { mapGetters } from 'vuex';
import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue'; import inlineDiffCommentRow from './inline_diff_comment_row.vue';
import { trimFirstCharOfLineContent } from '../store/utils';
export default { export default {
components: { components: {
inlineDiffCommentRow, inlineDiffCommentRow,
inlineDiffTableRow,
},
props: {
diffFile: {
type: Object,
required: true,
},
diffLines: {
type: Array,
required: true,
},
},
computed: {
...mapGetters(['commit']),
normalizedDiffLines() {
return this.diffLines.map(line => (line.richText ? trimFirstCharOfLineContent(line) : line));
},
diffLinesLength() {
return this.normalizedDiffLines.length;
},
commitId() {
return this.commit && this.commit.id;
},
userColorScheme() {
return window.gon.user_color_scheme;
},
}, },
mixins: [diffContentMixin],
}; };
</script> </script>
...@@ -19,7 +46,7 @@ export default { ...@@ -19,7 +46,7 @@ export default {
<template <template
v-for="(line, index) in normalizedDiffLines" v-for="(line, index) in normalizedDiffLines"
> >
<diff-table-row <inline-diff-table-row
:diff-file="diffFile" :diff-file="diffFile"
:line="line" :line="line"
:is-bottom="index + 1 === diffLinesLength" :is-bottom="index + 1 === diffLinesLength"
......
...@@ -35,30 +35,21 @@ export default { ...@@ -35,30 +35,21 @@ export default {
}, },
data() { data() {
return { return {
isHover: false,
isLeftHover: false, isLeftHover: false,
isRightHover: false, isRightHover: false,
}; };
}, },
computed: { computed: {
...mapGetters(['isInlineView', 'isParallelView']), ...mapGetters(['isParallelView']),
isContextLine() { isContextLine() {
return this.line.left return this.line.left.type === CONTEXT_LINE_TYPE;
? this.line.left.type === CONTEXT_LINE_TYPE
: this.line.type === CONTEXT_LINE_TYPE;
}, },
classNameMap() { classNameMap() {
return { return {
[this.line.type]: this.line.type,
[CONTEXT_LINE_CLASS_NAME]: this.isContextLine, [CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
[PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView, [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
}; };
}, },
inlineRowId() {
const { lineCode, oldLine, newLine } = this.line;
return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`;
},
parallelViewLeftLineType() { parallelViewLeftLineType() {
if (this.line.right.type === NEW_NO_NEW_LINE_TYPE) { if (this.line.right.type === NEW_NO_NEW_LINE_TYPE) {
return OLD_NO_NEW_LINE_TYPE; return OLD_NO_NEW_LINE_TYPE;
...@@ -72,14 +63,11 @@ export default { ...@@ -72,14 +63,11 @@ export default {
this.oldLineType = OLD_LINE_TYPE; this.oldLineType = OLD_LINE_TYPE;
this.linePositionLeft = LINE_POSITION_LEFT; this.linePositionLeft = LINE_POSITION_LEFT;
this.linePositionRight = LINE_POSITION_RIGHT; this.linePositionRight = LINE_POSITION_RIGHT;
this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE;
}, },
methods: { methods: {
handleMouseMove(e) { handleMouseMove(e) {
const isHover = e.type === 'mouseover'; const isHover = e.type === 'mouseover';
if (this.isInlineView) {
this.isHover = isHover;
} else {
const hoveringCell = e.target.closest('td'); const hoveringCell = e.target.closest('td');
const allCellsInHoveringRow = Array.from(e.currentTarget.children); const allCellsInHoveringRow = Array.from(e.currentTarget.children);
const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell); const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell);
...@@ -89,7 +77,6 @@ export default { ...@@ -89,7 +77,6 @@ export default {
} else { } else {
this.isLeftHover = isHover; this.isLeftHover = isHover;
} }
}
}, },
// Prevent text selecting on both sides of parallel diff view // Prevent text selecting on both sides of parallel diff view
// Backport of the same code from legacy diff notes. // Backport of the same code from legacy diff notes.
...@@ -110,40 +97,6 @@ export default { ...@@ -110,40 +97,6 @@ export default {
<template> <template>
<tr <tr
v-if="isInlineView"
:id="inlineRowId"
:class="classNameMap"
class="line_holder"
@mouseover="handleMouseMove"
@mouseout="handleMouseMove"
>
<diff-table-cell
:diff-file="diffFile"
:line="line"
:line-type="oldLineType"
:is-bottom="isBottom"
:is-hover="isHover"
:show-comment-button="true"
class="diff-line-num old_line"
/>
<diff-table-cell
:diff-file="diffFile"
:line="line"
:line-type="newLineType"
:is-bottom="isBottom"
:is-hover="isHover"
class="diff-line-num new_line"
/>
<diff-table-cell
:class="line.type"
:diff-file="diffFile"
:line="line"
:is-content-line="true"
/>
</tr>
<tr
v-else
:class="classNameMap" :class="classNameMap"
class="line_holder" class="line_holder"
@mouseover="handleMouseMove" @mouseover="handleMouseMove"
...@@ -157,6 +110,7 @@ export default { ...@@ -157,6 +110,7 @@ export default {
:is-bottom="isBottom" :is-bottom="isBottom"
:is-hover="isLeftHover" :is-hover="isLeftHover"
:show-comment-button="true" :show-comment-button="true"
:diff-view-type="parallelDiffViewType"
class="diff-line-num old_line" class="diff-line-num old_line"
/> />
<diff-table-cell <diff-table-cell
...@@ -164,7 +118,9 @@ export default { ...@@ -164,7 +118,9 @@ export default {
:diff-file="diffFile" :diff-file="diffFile"
:line="line" :line="line"
:is-content-line="true" :is-content-line="true"
:line-position="linePositionLeft"
:line-type="parallelViewLeftLineType" :line-type="parallelViewLeftLineType"
:diff-view-type="parallelDiffViewType"
class="line_content parallel left-side" class="line_content parallel left-side"
@mousedown.native="handleParallelLineMouseDown" @mousedown.native="handleParallelLineMouseDown"
/> />
...@@ -176,6 +132,7 @@ export default { ...@@ -176,6 +132,7 @@ export default {
:is-bottom="isBottom" :is-bottom="isBottom"
:is-hover="isRightHover" :is-hover="isRightHover"
:show-comment-button="true" :show-comment-button="true"
:diff-view-type="parallelDiffViewType"
class="diff-line-num new_line" class="diff-line-num new_line"
/> />
<diff-table-cell <diff-table-cell
...@@ -183,7 +140,9 @@ export default { ...@@ -183,7 +140,9 @@ export default {
:diff-file="diffFile" :diff-file="diffFile"
:line="line" :line="line"
:is-content-line="true" :is-content-line="true"
:line-position="linePositionRight"
:line-type="line.right.type" :line-type="line.right.type"
:diff-view-type="parallelDiffViewType"
class="line_content parallel right-side" class="line_content parallel right-side"
@mousedown.native="handleParallelLineMouseDown" @mousedown.native="handleParallelLineMouseDown"
/> />
......
<script> <script>
import diffContentMixin from '../mixins/diff_content'; import { mapGetters } from 'vuex';
import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue'; import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
import { EMPTY_CELL_TYPE } from '../constants'; import { EMPTY_CELL_TYPE } from '../constants';
import { trimFirstCharOfLineContent } from '../store/utils';
export default { export default {
components: { components: {
parallelDiffTableRow,
parallelDiffCommentRow, parallelDiffCommentRow,
}, },
mixins: [diffContentMixin], props: {
diffFile: {
type: Object,
required: true,
},
diffLines: {
type: Array,
required: true,
},
},
computed: { computed: {
...mapGetters(['commit']),
parallelDiffLines() { parallelDiffLines() {
return this.normalizedDiffLines.map(line => { return this.diffLines.map(line => {
if (!line.left) { if (line.left) {
Object.assign(line, { left: trimFirstCharOfLineContent(line.left) });
} else {
Object.assign(line, { left: { type: EMPTY_CELL_TYPE } }); Object.assign(line, { left: { type: EMPTY_CELL_TYPE } });
} else if (!line.right) { }
if (line.right) {
Object.assign(line, { right: trimFirstCharOfLineContent(line.right) });
} else {
Object.assign(line, { right: { type: EMPTY_CELL_TYPE } }); Object.assign(line, { right: { type: EMPTY_CELL_TYPE } });
} }
return line; return line;
}); });
}, },
diffLinesLength() {
return this.parallelDiffLines.length;
},
commitId() {
return this.commit && this.commit.id;
},
userColorScheme() {
return window.gon.user_color_scheme;
},
}, },
}; };
</script> </script>
...@@ -35,7 +63,7 @@ export default { ...@@ -35,7 +63,7 @@ export default {
<template <template
v-for="(line, index) in parallelDiffLines" v-for="(line, index) in parallelDiffLines"
> >
<diff-table-row <parallel-diff-table-row
:diff-file="diffFile" :diff-file="diffFile"
:line="line" :line="line"
:is-bottom="index + 1 === diffLinesLength" :is-bottom="index + 1 === diffLinesLength"
......
import { mapGetters } from 'vuex';
import diffDiscussions from '../components/diff_discussions.vue';
import diffLineGutterContent from '../components/diff_line_gutter_content.vue';
import diffLineNoteForm from '../components/diff_line_note_form.vue';
import diffTableRow from '../components/diff_table_row.vue';
import { trimFirstCharOfLineContent } from '../store/utils';
export default {
props: {
diffFile: {
type: Object,
required: true,
},
diffLines: {
type: Array,
required: true,
},
},
components: {
diffDiscussions,
diffTableRow,
diffLineNoteForm,
diffLineGutterContent,
},
computed: {
...mapGetters(['commit']),
commitId() {
return this.commit && this.commit.id;
},
userColorScheme() {
return window.gon.user_color_scheme;
},
normalizedDiffLines() {
return this.diffLines.map(line => {
if (line.richText) {
return trimFirstCharOfLineContent(line);
}
if (line.left) {
Object.assign(line, { left: trimFirstCharOfLineContent(line.left) });
}
if (line.right) {
Object.assign(line, { right: trimFirstCharOfLineContent(line.right) });
}
return line;
});
},
diffLinesLength() {
return this.normalizedDiffLines.length;
},
fileHash() {
return this.diffFile.fileHash;
},
},
};
export const SET_BASE_CONFIG = 'SET_BASE_CONFIG'; export const SET_BASE_CONFIG = 'SET_BASE_CONFIG';
export const SET_LOADING = 'SET_LOADING'; export const SET_LOADING = 'SET_LOADING';
export const SET_DIFF_DATA = 'SET_DIFF_DATA'; export const SET_DIFF_DATA = 'SET_DIFF_DATA';
export const SET_DIFF_FILES = 'SET_DIFF_FILES';
export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE'; export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE';
export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS'; export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const ADD_COMMENT_FORM_LINE = 'ADD_COMMENT_FORM_LINE'; export const ADD_COMMENT_FORM_LINE = 'ADD_COMMENT_FORM_LINE';
......
...@@ -20,12 +20,6 @@ export default { ...@@ -20,12 +20,6 @@ export default {
}); });
}, },
[types.SET_DIFF_FILES](state, diffFiles) {
Object.assign(state, {
diffFiles: convertObjectPropsToCamelCase(diffFiles, { deep: true }),
});
},
[types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) { [types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) {
Object.assign(state, { Object.assign(state, {
mergeRequestDiffs: convertObjectPropsToCamelCase(mergeRequestDiffs, { deep: true }), mergeRequestDiffs: convertObjectPropsToCamelCase(mergeRequestDiffs, { deep: true }),
......
...@@ -268,8 +268,6 @@ ...@@ -268,8 +268,6 @@
.navbar-sub-nav, .navbar-sub-nav,
.navbar-nav { .navbar-nav {
align-items: center;
> li { > li {
> a:hover, > a:hover,
> a:focus { > a:focus {
......
...@@ -52,8 +52,7 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -52,8 +52,7 @@ class Admin::HooksController < Admin::ApplicationController
end end
def hook_logs def hook_logs
@hook_logs ||= @hook_logs ||= hook.web_hook_logs.recent.page(params[:page])
Kaminari.paginate_array(hook.web_hook_logs.order(created_at: :desc)).page(params[:page])
end end
def hook_params def hook_params
......
...@@ -58,8 +58,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -58,8 +58,7 @@ class Projects::HooksController < Projects::ApplicationController
end end
def hook_logs def hook_logs
@hook_logs ||= @hook_logs ||= hook.web_hook_logs.recent.page(params[:page])
Kaminari.paginate_array(hook.web_hook_logs.order(created_at: :desc)).page(params[:page])
end end
def hook_params def hook_params
......
...@@ -62,7 +62,11 @@ class SessionsController < Devise::SessionsController ...@@ -62,7 +62,11 @@ class SessionsController < Devise::SessionsController
return unless captcha_enabled? return unless captcha_enabled?
return unless Gitlab::Recaptcha.load_configurations! return unless Gitlab::Recaptcha.load_configurations!
unless verify_recaptcha if verify_recaptcha
increment_successful_login_captcha_counter
else
increment_failed_login_captcha_counter
self.resource = resource_class.new self.resource = resource_class.new
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
flash.delete :recaptcha_error flash.delete :recaptcha_error
...@@ -71,6 +75,20 @@ class SessionsController < Devise::SessionsController ...@@ -71,6 +75,20 @@ class SessionsController < Devise::SessionsController
end end
end end
def increment_failed_login_captcha_counter
Gitlab::Metrics.counter(
:failed_login_captcha_total,
'Number of failed CAPTCHA attempts for logins'.freeze
).increment
end
def increment_successful_login_captcha_counter
Gitlab::Metrics.counter(
:successful_login_captcha_total,
'Number of successful CAPTCHA attempts for logins'.freeze
).increment
end
def log_failed_login def log_failed_login
Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}") Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}")
end end
......
...@@ -7,6 +7,11 @@ class WebHookLog < ActiveRecord::Base ...@@ -7,6 +7,11 @@ class WebHookLog < ActiveRecord::Base
validates :web_hook, presence: true validates :web_hook, presence: true
def self.recent
where('created_at >= ?', 2.days.ago.beginning_of_day)
.order(created_at: :desc)
end
def success? def success?
response_status =~ /^2/ response_status =~ /^2/
end end
......
...@@ -67,11 +67,11 @@ class BambooService < CiService ...@@ -67,11 +67,11 @@ class BambooService < CiService
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
get_path("updateAndBuild.action?buildKey=#{build_key}") get_path("updateAndBuild.action", { buildKey: build_key })
end end
def calculate_reactive_cache(sha, ref) def calculate_reactive_cache(sha, ref)
response = get_path("rest/api/latest/result?label=#{sha}") response = get_path("rest/api/latest/result/byChangeset/#{sha}")
{ build_page: read_build_page(response), commit_status: read_commit_status(response) } { build_page: read_build_page(response), commit_status: read_commit_status(response) }
end end
...@@ -113,14 +113,16 @@ class BambooService < CiService ...@@ -113,14 +113,16 @@ class BambooService < CiService
URI.join("#{bamboo_url}/", path).to_s URI.join("#{bamboo_url}/", path).to_s
end end
def get_path(path) def get_path(path, query_params = {})
url = build_url(path) url = build_url(path)
if username.blank? && password.blank? if username.blank? && password.blank?
Gitlab::HTTP.get(url, verify: false) Gitlab::HTTP.get(url, verify: false, query: query_params)
else else
url << '&os_authType=basic' query_params[:os_authType] = 'basic'
Gitlab::HTTP.get(url, verify: false, Gitlab::HTTP.get(url,
verify: false,
query: query_params,
basic_auth: { basic_auth: {
username: username, username: username,
password: password password: password
......
...@@ -27,6 +27,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -27,6 +27,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def statistics_buttons(show_auto_devops_callout:) def statistics_buttons(show_auto_devops_callout:)
[ [
readme_anchor_data,
changelog_anchor_data, changelog_anchor_data,
license_anchor_data, license_anchor_data,
contribution_guide_anchor_data, contribution_guide_anchor_data,
...@@ -212,11 +213,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -212,11 +213,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end end
def readme_anchor_data def readme_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.readme.blank? if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
OpenStruct.new(enabled: false, OpenStruct.new(enabled: false,
label: _('Add Readme'), label: _('Add Readme'),
link: add_readme_path) link: add_readme_path)
elsif repository.readme.present? elsif repository.readme
OpenStruct.new(enabled: true, OpenStruct.new(enabled: true,
label: _('Readme'), label: _('Readme'),
link: default_view != 'readme' ? readme_path : '#readme') link: default_view != 'readme' ? readme_path : '#readme')
......
...@@ -35,7 +35,7 @@ class BuildDetailsEntity < JobEntity ...@@ -35,7 +35,7 @@ class BuildDetailsEntity < JobEntity
def build_failed_issue_options def build_failed_issue_options
{ title: "Job Failed ##{build.id}", { title: "Job Failed ##{build.id}",
description: "Job [##{build.id}](#{project_job_path(project, build)}) failed for #{build.sha}:\n" } description: "Job [##{build.id}](#{project_job_url(project, build)}) failed for #{build.sha}:\n" }
end end
def current_user def current_user
......
...@@ -81,6 +81,13 @@ class FileUploader < GitlabUploader ...@@ -81,6 +81,13 @@ class FileUploader < GitlabUploader
apply_context!(uploader_context) apply_context!(uploader_context)
end end
def initialize_copy(from)
super
@secret = self.class.generate_secret
@upload = nil # calling record_upload would delete the old upload if set
end
# enforce the usage of Hashed storage when storing to # enforce the usage of Hashed storage when storing to
# remote store as the FileMover doesn't support OS # remote store as the FileMover doesn't support OS
def base_dir(store = nil) def base_dir(store = nil)
...@@ -144,6 +151,27 @@ class FileUploader < GitlabUploader ...@@ -144,6 +151,27 @@ class FileUploader < GitlabUploader
@secret ||= self.class.generate_secret @secret ||= self.class.generate_secret
end end
# return a new uploader with a file copy on another project
def self.copy_to(uploader, to_project)
moved = uploader.dup.tap do |u|
u.model = to_project
end
moved.copy_file(uploader.file)
moved
end
def copy_file(file)
to_path = if file_storage?
File.join(self.class.root, store_path)
else
store_path
end
self.file = file.copy_to(to_path)
record_upload # after_store is not triggered
end
private private
def apply_context!(uploader_context) def apply_context!(uploader_context)
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
- if current_user - if current_user
= render 'layouts/header/new_dropdown' = render 'layouts/header/new_dropdown'
- if header_link?(:search) - if header_link?(:search)
%li.nav-item.d-none.d-sm-none.d-md-block %li.nav-item.d-none.d-sm-none.d-md-block.m-auto
= render 'layouts/search' unless current_controller?(:search) = render 'layouts/search' unless current_controller?(:search)
%li.nav-item.d-inline-block.d-sm-none.d-md-none %li.nav-item.d-inline-block.d-sm-none.d-md-none
= link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
- cronjob:ci_archive_traces_cron - cronjob:ci_archive_traces_cron
- cronjob:trending_projects - cronjob:trending_projects
- cronjob:issue_due_scheduler - cronjob:issue_due_scheduler
- cronjob:prune_web_hook_logs
- gcp_cluster:cluster_install_app - gcp_cluster:cluster_install_app
- gcp_cluster:cluster_provision - gcp_cluster:cluster_provision
......
# frozen_string_literal: true
# Worker that deletes a fixed number of outdated rows from the "web_hook_logs"
# table.
class PruneWebHookLogsWorker
include ApplicationWorker
include CronjobQueue
# The maximum number of rows to remove in a single job.
DELETE_LIMIT = 50_000
def perform
# MySQL doesn't allow "DELETE FROM ... WHERE id IN ( ... )" if the inner
# query refers to the same table. To work around this we wrap the IN body in
# another sub query.
WebHookLog
.where(
'id IN (SELECT id FROM (?) ids_to_remove)',
WebHookLog
.select(:id)
.where('created_at < ?', 90.days.ago.beginning_of_day)
.limit(DELETE_LIMIT)
)
.delete_all
end
end
---
title: Use monospaced font for MR diff commit link ref on GFM
merge_request:
author:
type: other
---
title: Fix link to job when creating a new issue from a failed job
merge_request: 20328
author:
type: fixed
---
title: Add readme button to non-empty project page
merge_request: 20104
author:
type: fixed
---
title: Line separator to the left of the 'Admin area' wrench icon had vanished
merge_request: 20282
author: bitsapien
type: fixed
---
title: Fix merge request diffs when created with gitaly_diff_between enabled
merge_request:
author:
type: fixed
---
title: Don't show context button for diffs of deleted files.
merge_request:
author:
type: fixed
---
title: Prune web hook logs older than 90 days
merge_request:
author:
type: added
---
title: Fix Bamboo CI status not showing for branch plans
merge_request:
author:
type: fixed
---
title: Implement upload copy when moving an issue with upload on object storage
merge_request: 20191
author:
type: fixed
---
title: Fixed pagination of web hook logs
merge_request:
author:
type: performance
# See https://github.com/jnicklas/carrierwave#using-amazon-s3
# for more options
# If you change this file in a Merge Request, please also create
# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
#
production:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
bucket: mygitlab.production.us
region: us-east-1
development:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
bucket: mygitlab.development.us
region: us-east-1
test:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
bucket: mygitlab.test.us
region: us-east-1
...@@ -338,6 +338,10 @@ Settings.cron_jobs['issue_due_scheduler_worker'] ||= Settingslogic.new({}) ...@@ -338,6 +338,10 @@ Settings.cron_jobs['issue_due_scheduler_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['issue_due_scheduler_worker']['cron'] ||= '50 00 * * *' Settings.cron_jobs['issue_due_scheduler_worker']['cron'] ||= '50 00 * * *'
Settings.cron_jobs['issue_due_scheduler_worker']['job_class'] = 'IssueDueSchedulerWorker' Settings.cron_jobs['issue_due_scheduler_worker']['job_class'] = 'IssueDueSchedulerWorker'
Settings.cron_jobs['prune_web_hook_logs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['prune_web_hook_logs_worker']['cron'] ||= '0 */1 * * *'
Settings.cron_jobs['prune_web_hook_logs_worker']['job_class'] = 'PruneWebHookLogsWorker'
# #
# Sidekiq # Sidekiq
# #
......
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
aws_file = Rails.root.join('config', 'aws.yml')
if File.exist?(aws_file)
AWS_CONFIG = YAML.load(File.read(aws_file))[Rails.env]
CarrierWave.configure do |config|
config.fog_provider = 'fog/aws'
config.fog_credentials = {
provider: 'AWS', # required
aws_access_key_id: AWS_CONFIG['access_key_id'], # required
aws_secret_access_key: AWS_CONFIG['secret_access_key'], # required
region: AWS_CONFIG['region'], # optional, defaults to 'us-east-1'
}
# required
config.fog_directory = AWS_CONFIG['bucket']
# optional, defaults to true
config.fog_public = false
# optional, defaults to {}
config.fog_attributes = { 'Cache-Control' => 'max-age=315576000' }
# optional time (in seconds) that authenticated urls will be valid.
# when fog_public is false and provider is AWS or Google, defaults to 600
config.fog_authenticated_url_expiration = 1 << 29
end
end
require './spec/support/sidekiq' require './spec/support/sidekiq'
def create_group_with_parents(user, full_path)
parent_path = nil
group = nil
until full_path.blank?
path, _, full_path = full_path.partition('/')
if parent_path
parent = Group.find_by_full_path(parent_path)
parent_path += '/'
parent_path += path
group = Groups::CreateService.new(user, path: path, parent_id: parent.id).execute
else
parent_path = path
group = Group.find_by_full_path(parent_path) ||
Groups::CreateService.new(user, path: path).execute
end
end
group
end
Sidekiq::Testing.inline! do Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do Gitlab::Seeder.quiet do
flag = 'SEED_NESTED_GROUPS' flag = 'SEED_NESTED_GROUPS'
...@@ -48,7 +23,8 @@ Sidekiq::Testing.inline! do ...@@ -48,7 +23,8 @@ Sidekiq::Testing.inline! do
full_path = url.sub('https://android.googlesource.com/', '') full_path = url.sub('https://android.googlesource.com/', '')
full_path = full_path.sub(/\.git\z/, '') full_path = full_path.sub(/\.git\z/, '')
full_path, _, project_path = full_path.rpartition('/') full_path, _, project_path = full_path.rpartition('/')
group = Group.find_by_full_path(full_path) || create_group_with_parents(user, full_path) group = Group.find_by_full_path(full_path) ||
Groups::NestedCreateService.new(user, group_path: full_path).execute
params = { params = {
import_url: url, import_url: url,
......
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AlterWebHookLogsIndexes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
# "created_at" comes first so the Sidekiq worker pruning old webhook logs can
# use a composite index index.
#
# We leave the old standalone index on "web_hook_id" in place so future code
# that doesn't care about "created_at" can still use that index.
COLUMNS_TO_INDEX = %i[created_at web_hook_id]
def up
add_concurrent_index(:web_hook_logs, COLUMNS_TO_INDEX)
end
def down
remove_concurrent_index(:web_hook_logs, COLUMNS_TO_INDEX)
end
end
class EnqueueDeleteDiffFilesWorkers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
class MergeRequestDiff < ActiveRecord::Base
self.table_name = 'merge_request_diffs'
belongs_to :merge_request
include EachBatch
end
DOWNTIME = false
BATCH_SIZE = 1000
MIGRATION = 'DeleteDiffFiles'
DELAY_INTERVAL = 8.minutes
TMP_INDEX = 'tmp_partial_diff_id_with_files_index'.freeze
disable_ddl_transaction!
def up
# We add temporary index, to make iteration over batches more performant.
# Conditional here is to avoid the need of doing that in a separate
# migration file to make this operation idempotent.
#
unless index_exists_by_name?(:merge_request_diffs, TMP_INDEX)
add_concurrent_index(:merge_request_diffs, :id, where: "(state NOT IN ('without_files', 'empty'))", name: TMP_INDEX)
end
diffs_with_files = MergeRequestDiff.where.not(state: ['without_files', 'empty'])
# explain (analyze, buffers) example for the iteration:
#
# Index Only Scan using tmp_index_20013 on merge_request_diffs (cost=0.43..1630.19 rows=60567 width=4) (actual time=0.047..9.572 rows=56976 loops=1)
# Index Cond: ((id >= 764586) AND (id < 835298))
# Heap Fetches: 8
# Buffers: shared hit=18188
# Planning time: 0.752 ms
# Execution time: 12.430 ms
#
diffs_with_files.each_batch(of: BATCH_SIZE) do |relation, outer_index|
ids = relation.pluck(:id)
ids.each_with_index do |diff_id, inner_index|
# This will give some space between batches of workers.
interval = DELAY_INTERVAL * outer_index + inner_index.minutes
# A single `merge_request_diff` can be associated with way too many
# `merge_request_diff_files`. It's better to avoid batching these and
# schedule one at a time.
#
# Considering roughly 6M jobs, this should take ~30 days to process all
# of them.
#
BackgroundMigrationWorker.perform_in(interval, MIGRATION, [diff_id])
end
end
# We remove temporary index, because it is not required during standard
# operations and runtime.
#
remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
end
def down
if index_exists_by_name?(:merge_request_diffs, TMP_INDEX)
remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
end
end
end
class AddPartialIndexToProjectsForLastRepositoryCheckAt < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
INDEX_NAME = "index_projects_on_last_repository_check_at"
def up
add_concurrent_index(:projects, :last_repository_check_at, where: "last_repository_check_at IS NOT NULL", name: INDEX_NAME)
end
def down
remove_concurrent_index(:projects, :last_repository_check_at, where: "last_repository_check_at IS NOT NULL", name: INDEX_NAME)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# 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.
ActiveRecord::Schema.define(version: 20180626125654) do ActiveRecord::Schema.define(version: 20180629191052) do
# 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"
...@@ -1646,6 +1646,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do ...@@ -1646,6 +1646,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "projects", ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree add_index "projects", ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
add_index "projects", ["last_repository_check_at"], name: "index_projects_on_last_repository_check_at", where: "(last_repository_check_at IS NOT NULL)", using: :btree
add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree
add_index "projects", ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree add_index "projects", ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree
add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
...@@ -2144,6 +2145,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do ...@@ -2144,6 +2145,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
add_index "web_hook_logs", ["created_at", "web_hook_id"], name: "index_web_hook_logs_on_created_at_and_web_hook_id", using: :btree
add_index "web_hook_logs", ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree add_index "web_hook_logs", ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree
create_table "web_hooks", force: :cascade do |t| create_table "web_hooks", force: :cascade do |t|
......
...@@ -228,7 +228,7 @@ straight away. ...@@ -228,7 +228,7 @@ straight away.
### GitLab self-hosted ### GitLab self-hosted
With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Core, Starter, Premium, and Ultimate. With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/pricing/): Core, Starter, Premium, and Ultimate.
Every feature available in Core is also available in Starter, Premium, and Ultimate. Every feature available in Core is also available in Starter, Premium, and Ultimate.
Starter features are also available in Premium and Ultimate, and Premium features are also Starter features are also available in Premium and Ultimate, and Premium features are also
......
...@@ -11,7 +11,7 @@ Regular users don't have access to GitLab administration tools and settings. ...@@ -11,7 +11,7 @@ Regular users don't have access to GitLab administration tools and settings.
GitLab has two product distributions: the open source GitLab has two product distributions: the open source
[GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce), [GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce),
and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee), and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee),
available through [different subscriptions](https://about.gitlab.com/products/). available through [different subscriptions](https://about.gitlab.com/pricing/).
You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/), You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/),
but the features you'll have access to depend on the subscription you choose but the features you'll have access to depend on the subscription you choose
......
...@@ -91,9 +91,9 @@ _The artifacts are stored by default in ...@@ -91,9 +91,9 @@ _The artifacts are stored by default in
- [Introduced][ee-1762] in [GitLab Premium][eep] 9.4. - [Introduced][ee-1762] in [GitLab Premium][eep] 9.4.
- Since version 9.5, artifacts are [browsable], when object storage is enabled. - Since version 9.5, artifacts are [browsable], when object storage is enabled.
9.4 lacks this feature. 9.4 lacks this feature.
> Available in [GitLab Premium](https://about.gitlab.com/products/) and > Available in [GitLab Premium](https://about.gitlab.com/pricing/) and
[GitLab.com Silver](https://about.gitlab.com/gitlab-com/). [GitLab.com Silver](https://about.gitlab.com/gitlab-com/).
> Since version 10.6, available in [GitLab CE](https://about.gitlab.com/products/) > Since version 10.6, available in [GitLab CE](https://about.gitlab.com/pricing/)
> Since version 11.0, we support direct_upload to S3. > Since version 11.0, we support direct_upload to S3.
If you don't want to use the local disk where GitLab is installed to store the If you don't want to use the local disk where GitLab is installed to store the
......
...@@ -48,6 +48,8 @@ The following metrics are available: ...@@ -48,6 +48,8 @@ The following metrics are available:
| filesystem_circuitbreaker_latency_seconds | Gauge | 9.5 | Time spent validating if a storage is accessible | | filesystem_circuitbreaker_latency_seconds | Gauge | 9.5 | Time spent validating if a storage is accessible |
| filesystem_circuitbreaker | Gauge | 9.5 | Whether or not the circuit for a certain shard is broken or not | | filesystem_circuitbreaker | Gauge | 9.5 | Whether or not the circuit for a certain shard is broken or not |
| circuitbreaker_storage_check_duration_seconds | Histogram | 10.3 | Time a single storage probe took | | circuitbreaker_storage_check_duration_seconds | Histogram | 10.3 | Time a single storage probe took |
| failed_login_captcha_total | Gauge | 11.0 | Counter of failed CAPTCHA attempts during login |
| successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login |
### Ruby metrics ### Ruby metrics
......
...@@ -46,4 +46,4 @@ configuration to reflect that change. ...@@ -46,4 +46,4 @@ configuration to reflect that change.
[cli]: https://github.com/codeclimate/codeclimate [cli]: https://github.com/codeclimate/codeclimate
[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor [dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
...@@ -63,4 +63,4 @@ are still maintained they have been deprecated with GitLab 11.0 and may be remov ...@@ -63,4 +63,4 @@ are still maintained they have been deprecated with GitLab 11.0 and may be remov
in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml` in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change. configuration to reflect that change.
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
...@@ -60,4 +60,4 @@ so, the CI job must be named `dast` and the artifact path must be ...@@ -60,4 +60,4 @@ so, the CI job must be named `dast` and the artifact path must be
`gl-dast-report.json`. `gl-dast-report.json`.
[Learn more about DAST results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html). [Learn more about DAST results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
...@@ -219,7 +219,7 @@ removed with one of the future versions of GitLab. You are advised to ...@@ -219,7 +219,7 @@ removed with one of the future versions of GitLab. You are advised to
[ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017 [ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017
[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229 [ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
[variables]: ../variables/README.md [variables]: ../variables/README.md
[predef]: ../variables/README.md#predefined-variables-environment-variables [predef]: ../variables/README.md#predefined-variables-environment-variables
[registry]: ../../user/project/container_registry.md [registry]: ../../user/project/container_registry.md
...@@ -553,7 +553,7 @@ Below you can find supported syntax reference: ...@@ -553,7 +553,7 @@ Below you can find supported syntax reference:
`/pattern/i` to make a pattern case-insensitive. `/pattern/i` to make a pattern case-insensitive.
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI variables" [ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI variables"
[eep]: https://about.gitlab.com/products/ "Available only in GitLab Premium" [eep]: https://about.gitlab.com/pricing/ "Available only in GitLab Premium"
[envs]: ../environments.md [envs]: ../environments.md
[protected branches]: ../../user/project/protected_branches.md [protected branches]: ../../user/project/protected_branches.md
[protected tags]: ../../user/project/protected_tags.md [protected tags]: ../../user/project/protected_tags.md
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## Software delivery ## Software delivery
There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/products/). There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/pricing/).
New versions of GitLab are released in stable branches and the master branch is for bleeding edge development. New versions of GitLab are released in stable branches and the master branch is for bleeding edge development.
......
...@@ -192,7 +192,7 @@ Portions of this page are modifications based on work created and shared by the ...@@ -192,7 +192,7 @@ Portions of this page are modifications based on work created and shared by the
[material design]: https://material.io/guidelines/ [material design]: https://material.io/guidelines/
[features]: https://about.gitlab.com/features/ "GitLab features page" [features]: https://about.gitlab.com/features/ "GitLab features page"
[products]: https://about.gitlab.com/products/ "GitLab products page" [products]: https://about.gitlab.com/pricing/ "GitLab products page"
[serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia" [serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia"
[android project]: http://source.android.com/ [android project]: http://source.android.com/
[creative commons]: http://creativecommons.org/licenses/by/2.5/ [creative commons]: http://creativecommons.org/licenses/by/2.5/
...@@ -71,7 +71,7 @@ For most installations, only two parameters are required: ...@@ -71,7 +71,7 @@ For most installations, only two parameters are required:
Other common configuration options: Other common configuration options:
- `baseIP`: the desired [external IP address](#external-ip-recommended) - `baseIP`: the desired [external IP address](#external-ip-recommended)
- `gitlab`: Choose the [desired edition](https://about.gitlab.com/products), either `ee` or `ce`. `ce` is the default. - `gitlab`: Choose the [desired edition](https://about.gitlab.com/pricing), either `ee` or `ce`. `ce` is the default.
- `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart - `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart
- `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), with `acs` also supported for the [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/). - `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), with `acs` also supported for the [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/).
......
...@@ -840,5 +840,5 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/ ...@@ -840,5 +840,5 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[postgresql]: https://www.postgresql.org/ [postgresql]: https://www.postgresql.org/
[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml [Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
[GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md [GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507 [ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
...@@ -7,7 +7,7 @@ description: 'Read through the GitLab User documentation to learn how to use, co ...@@ -7,7 +7,7 @@ description: 'Read through the GitLab User documentation to learn how to use, co
Welcome to GitLab! We're glad to have you here! Welcome to GitLab! We're glad to have you here!
As a GitLab user you'll have access to all the features As a GitLab user you'll have access to all the features
your [subscription](https://about.gitlab.com/products/) your [subscription](https://about.gitlab.com/pricing/)
includes, except [GitLab administrator](../README.md#administrator-documentation) includes, except [GitLab administrator](../README.md#administrator-documentation)
settings, unless you have admin privileges to install, configure, settings, unless you have admin privileges to install, configure,
and upgrade your GitLab instance. and upgrade your GitLab instance.
......
...@@ -313,4 +313,4 @@ Read through the documentation on [LDAP users permissions](https://docs.gitlab.c ...@@ -313,4 +313,4 @@ Read through the documentation on [LDAP users permissions](https://docs.gitlab.c
[ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994 [ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994
[new-mod]: project/new_ci_build_permissions_model.md [new-mod]: project/new_ci_build_permissions_model.md
[ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998 [ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998
[eep]: https://about.gitlab.com/products/ [eep]: https://about.gitlab.com/pricing/
...@@ -406,5 +406,5 @@ the deployment variables above, ensuring any pods you create are labelled with ...@@ -406,5 +406,5 @@ the deployment variables above, ensuring any pods you create are labelled with
- [Connecting and deploying to an Amazon EKS cluster](eks_and_gitlab/index.md) - [Connecting and deploying to an Amazon EKS cluster](eks_and_gitlab/index.md)
[permissions]: ../../permissions.md [permissions]: ../../permissions.md
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
[Auto DevOps]: ../../../topics/autodevops/index.md [Auto DevOps]: ../../../topics/autodevops/index.md
...@@ -41,7 +41,11 @@ service in GitLab. ...@@ -41,7 +41,11 @@ service in GitLab.
1. Click 'Atlassian Bamboo CI' 1. Click 'Atlassian Bamboo CI'
1. Select the 'Active' checkbox. 1. Select the 'Active' checkbox.
1. Enter the base URL of your Bamboo server. 'https://bamboo.example.com' 1. Enter the base URL of your Bamboo server. 'https://bamboo.example.com'
1. Enter the build key from your Bamboo build plan. Build keys are typically made up from the Project Key and Plan Key that are set on project/plan creation and seperated with a '-' for example **PROJ-PLAN**. This is a short, all capital letter, identifier that is unique. When viewing a plan within Bamboo, the build key is also shown in the browser URL for example https://bamboo.example.com/browse/PROJ-PLAN 1. Enter the build key from your Bamboo build plan. Build keys are typically made
up from the Project Key and Plan Key that are set on project/plan creation and
separated with a dash (`-`), for example **PROJ-PLAN**. This is a short, all
uppercase identifier that is unique. When viewing a plan within Bamboo, the
build key is also shown in the browser URL, for example `https://bamboo.example.com/browse/PROJ-PLAN`.
1. If necessary, enter username and password for a Bamboo user that has 1. If necessary, enter username and password for a Bamboo user that has
access to trigger the build plan. Leave these fields blank if you do not require access to trigger the build plan. Leave these fields blank if you do not require
authentication. authentication.
......
...@@ -6,6 +6,10 @@ Starting from GitLab 8.5: ...@@ -6,6 +6,10 @@ Starting from GitLab 8.5:
- the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key - the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key
- the `project.http_url` key is deprecated in favor of the `project.git_http_url` key - the `project.http_url` key is deprecated in favor of the `project.git_http_url` key
>**Note:**
Starting from GitLab 11.1, the logs of web hooks are automatically removed after
one month.
Project webhooks allow you to trigger a URL if for example new code is pushed or Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events a new issue is created. You can configure webhooks to listen for specific events
like pushes, issues or merge requests. GitLab will send a POST request with data like pushes, issues or merge requests. GitLab will send a POST request with data
......
...@@ -70,7 +70,7 @@ beginning of the development lifecycle until deployed to production ...@@ -70,7 +70,7 @@ beginning of the development lifecycle until deployed to production
### Use cases for Multiple Issue Boards ### Use cases for Multiple Issue Boards
With [Multiple Issue Boards](#multiple-issue-boards), available only in With [Multiple Issue Boards](#multiple-issue-boards), available only in
[GitLab Enterprise Edition](https://about.gitlab.com/products/), [GitLab Enterprise Edition](https://about.gitlab.com/pricing/),
each team can have their own board to organize their workflow individually. each team can have their own board to organize their workflow individually.
#### Scrum team #### Scrum team
......
...@@ -8,7 +8,7 @@ It allows you, your team, and your collaborators to share ...@@ -8,7 +8,7 @@ It allows you, your team, and your collaborators to share
and discuss proposals before and while implementing them. and discuss proposals before and while implementing them.
GitLab Issues and the GitLab Issue Tracker are available in all GitLab Issues and the GitLab Issue Tracker are available in all
[GitLab Products](https://about.gitlab.com/products/) as [GitLab Products](https://about.gitlab.com/pricing/) as
part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/). part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
## Use cases ## Use cases
...@@ -35,7 +35,7 @@ your project public, open to collaboration. ...@@ -35,7 +35,7 @@ your project public, open to collaboration.
### Streamline collaboration ### Streamline collaboration
With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html), With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
available in [GitLab Starter](https://about.gitlab.com/products/) available in [GitLab Starter](https://about.gitlab.com/pricing/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed. you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they All assignees are shown across your workflows and receive notifications (as they
would as single assignees), simplifying communication and ownership. would as single assignees), simplifying communication and ownership.
...@@ -139,7 +139,7 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue ...@@ -139,7 +139,7 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md) Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature. to find out more about this feature.
With [GitLab Starter](https://about.gitlab.com/products/), you can also With [GitLab Starter](https://about.gitlab.com/pricing/), you can also
create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards). create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
### External Issue Tracker ### External Issue Tracker
......
...@@ -47,7 +47,7 @@ Often multiple people likely work on the same issue together, ...@@ -47,7 +47,7 @@ Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams which can especially be difficult to track in large teams
where there is shared ownership of an issue. where there is shared ownership of an issue.
In [GitLab Starter](https://about.gitlab.com/products/), you can also In [GitLab Starter](https://about.gitlab.com/pricing/), you can also
select multiple assignees to an issue. select multiple assignees to an issue.
Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html). Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html).
......
...@@ -325,4 +325,4 @@ git checkout origin/merge-requests/1 ...@@ -325,4 +325,4 @@ git checkout origin/merge-requests/1
``` ```
[protected branches]: ../protected_branches.md [protected branches]: ../protected_branches.md
[ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition" [ee]: https://about.gitlab.com/pricing/ "GitLab Enterprise Edition"
...@@ -82,7 +82,7 @@ your implementation with your team. ...@@ -82,7 +82,7 @@ your implementation with your team.
You can live preview changes submitted to a new branch with You can live preview changes submitted to a new branch with
[Review Apps](../../../ci/review_apps/index.md). [Review Apps](../../../ci/review_apps/index.md).
With [GitLab Starter](https://about.gitlab.com/products/), you can also request With [GitLab Starter](https://about.gitlab.com/pricing/), you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers. [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
To create, delete, and [branches](branches/index.md) via GitLab's UI: To create, delete, and [branches](branches/index.md) via GitLab's UI:
...@@ -165,12 +165,12 @@ Find it under your project's **Repository > Compare**. ...@@ -165,12 +165,12 @@ Find it under your project's **Repository > Compare**.
## Locked files ## Locked files
> Available in [GitLab Premium](https://about.gitlab.com/products/). > Available in [GitLab Premium](https://about.gitlab.com/pricing/).
Lock your files to prevent any conflicting changes. Lock your files to prevent any conflicting changes.
[File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in [File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in
[GitLab Premium](https://about.gitlab.com/products/). [GitLab Premium](https://about.gitlab.com/pricing/).
## Repository's API ## Repository's API
......
...@@ -42,7 +42,7 @@ Set up your project's merge request settings: ...@@ -42,7 +42,7 @@ Set up your project's merge request settings:
### Service Desk ### Service Desk
Enable [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) for your project to offer customer support. Service Desk is available in [GitLab Premium](https://about.gitlab.com/products/). Enable [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) for your project to offer customer support. Service Desk is available in [GitLab Premium](https://about.gitlab.com/pricing/).
### Export project ### Export project
......
...@@ -236,5 +236,5 @@ See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-ce/merge_r ...@@ -236,5 +236,5 @@ See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-ce/merge_r
[reconfigure gitlab]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab" [reconfigure gitlab]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: ../../administration/restart_gitlab.md#installations-from-source "How to restart GitLab" [restart gitlab]: ../../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
[eep]: https://about.gitlab.com/products/ "GitLab Premium" [eep]: https://about.gitlab.com/pricing/ "GitLab Premium"
[ee-2760]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2760 [ee-2760]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2760
...@@ -25,7 +25,10 @@ module Banzai ...@@ -25,7 +25,10 @@ module Banzai
extras = super extras = super
if commit_ref = object_link_commit_ref(object, matches) if commit_ref = object_link_commit_ref(object, matches)
return extras.unshift(commit_ref) klass = reference_class(:commit, tooltip: false)
commit_ref_tag = %(<span class="#{klass}">#{commit_ref}</span>)
return extras.unshift(commit_ref_tag)
end end
path = matches[:path] if matches.names.include?("path") path = matches[:path] if matches.names.include?("path")
......
...@@ -65,8 +65,12 @@ module Banzai ...@@ -65,8 +65,12 @@ module Banzai
context[:skip_project_check] context[:skip_project_check]
end end
def reference_class(type) def reference_class(type, tooltip: true)
"gfm gfm-#{type} has-tooltip" gfm_klass = "gfm gfm-#{type}"
return gfm_klass unless tooltip
"#{gfm_klass} has-tooltip"
end end
# Ensure that a :project key exists in context # Ensure that a :project key exists in context
......
...@@ -250,7 +250,7 @@ module Gitlab ...@@ -250,7 +250,7 @@ module Gitlab
last_line = lines.last last_line = lines.last
if last_line.new_pos < total_blob_lines(blob) if last_line.new_pos < total_blob_lines(blob) && !deleted_file?
match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos) match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos)
lines.push(match_line) lines.push(match_line)
end end
......
...@@ -23,11 +23,8 @@ module Gitlab ...@@ -23,11 +23,8 @@ module Gitlab
file = find_file(@source_project, $~[:secret], $~[:file]) file = find_file(@source_project, $~[:secret], $~[:file])
break markdown unless file.try(:exists?) break markdown unless file.try(:exists?)
new_uploader = FileUploader.new(target_project) moved = FileUploader.copy_to(file, target_project)
with_link_in_tmp_dir(file.file) do |open_tmp_file| moved.markdown_link
new_uploader.store!(open_tmp_file)
end
new_uploader.markdown_link
end end
end end
...@@ -48,20 +45,7 @@ module Gitlab ...@@ -48,20 +45,7 @@ module Gitlab
def find_file(project, secret, file) def find_file(project, secret, file)
uploader = FileUploader.new(project, secret: secret) uploader = FileUploader.new(project, secret: secret)
uploader.retrieve_from_store!(file) uploader.retrieve_from_store!(file)
uploader.file uploader
end
# Because the uploaders use 'move_to_store' we must have a temporary
# file that is allowed to be (re)moved.
def with_link_in_tmp_dir(file)
dir = Dir.mktmpdir('UploadsRewriter', File.dirname(file))
# The filename matters to Carrierwave so we make sure to preserve it
tmp_file = File.join(dir, File.basename(file))
File.link(file, tmp_file)
# Open the file to placate Carrierwave
File.open(tmp_file) { |open_file| yield open_file }
ensure
FileUtils.rm_rf(dir)
end end
end end
end end
......
This diff is collapsed.
...@@ -368,7 +368,7 @@ module Gitlab ...@@ -368,7 +368,7 @@ module Gitlab
def call_commit_diff(request_params, options = {}) def call_commit_diff(request_params, options = {})
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false) request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
request_params[:enforce_limits] = options.fetch(:limits, true) request_params[:enforce_limits] = options.fetch(:limits, true)
request_params[:collapse_diffs] = request_params[:enforce_limits] || !options.fetch(:expanded, true) request_params[:collapse_diffs] = !options.fetch(:expanded, true)
request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h) request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h)
request = Gitaly::CommitDiffRequest.new(request_params) request = Gitaly::CommitDiffRequest.new(request_params)
......
...@@ -64,6 +64,8 @@ module Gitlab ...@@ -64,6 +64,8 @@ module Gitlab
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit) target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit) Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit)
rescue GRPC::FailedPrecondition => ex
raise Gitlab::Git::Repository::InvalidRef, ex
end end
def user_delete_branch(branch_name, user) def user_delete_branch(branch_name, user)
...@@ -133,6 +135,8 @@ module Gitlab ...@@ -133,6 +135,8 @@ module Gitlab
request request
).branch_update ).branch_update
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update) Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::CommitError, e
end end
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:) def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
......
...@@ -58,7 +58,7 @@ module QA ...@@ -58,7 +58,7 @@ module QA
def within_project_deploy_keys def within_project_deploy_keys
wait(reload: false) do wait(reload: false) do
find_element(:project_deploy_keys) has_css?(element_selector_css(:project_deploy_keys))
end end
within_element(:project_deploy_keys) do within_element(:project_deploy_keys) do
......
...@@ -93,6 +93,12 @@ describe SessionsController do ...@@ -93,6 +93,12 @@ describe SessionsController do
it 'displays an error when the reCAPTCHA is not solved' do it 'displays an error when the reCAPTCHA is not solved' do
# Without this, `verify_recaptcha` arbitraily returns true in test env # Without this, `verify_recaptcha` arbitraily returns true in test env
Recaptcha.configuration.skip_verify_env.delete('test') Recaptcha.configuration.skip_verify_env.delete('test')
counter = double(:counter)
expect(counter).to receive(:increment)
expect(Gitlab::Metrics).to receive(:counter)
.with(:failed_login_captcha_total, anything)
.and_return(counter)
post(:create, user: user_params) post(:create, user: user_params)
...@@ -104,6 +110,13 @@ describe SessionsController do ...@@ -104,6 +110,13 @@ describe SessionsController do
it 'successfully logs in a user when reCAPTCHA is solved' do it 'successfully logs in a user when reCAPTCHA is solved' do
# Avoid test ordering issue and ensure `verify_recaptcha` returns true # Avoid test ordering issue and ensure `verify_recaptcha` returns true
Recaptcha.configuration.skip_verify_env << 'test' Recaptcha.configuration.skip_verify_env << 'test'
counter = double(:counter)
expect(counter).to receive(:increment)
expect(Gitlab::Metrics).to receive(:counter)
.with(:successful_login_captcha_total, anything)
.and_return(counter)
expect(Gitlab::Metrics).to receive(:counter).and_call_original
post(:create, user: user_params) post(:create, user: user_params)
......
...@@ -165,7 +165,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -165,7 +165,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
it 'links to issues/new with the title and description filled in' do it 'links to issues/new with the title and description filled in' do
button_title = "Job Failed ##{job.id}" button_title = "Job Failed ##{job.id}"
job_url = project_job_path(project, job) job_url = project_job_url(project, job, host: page.server.host, port: page.server.port)
options = { issue: { title: button_title, description: "Job [##{job.id}](#{job_url}) failed for #{job.sha}:\n" } } options = { issue: { title: button_title, description: "Job [##{job.id}](#{job_url}) failed for #{job.sha}:\n" } }
href = new_project_issue_path(project, options) href = new_project_issue_path(project, options)
......
...@@ -5,6 +5,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -5,6 +5,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
# see spec/features/projects/files/project_owner_creates_license_file_spec.rb # see spec/features/projects/files/project_owner_creates_license_file_spec.rb
# see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb # see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
include FakeBlobHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
describe 'empty project' do describe 'empty project' do
...@@ -141,11 +143,57 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -141,11 +143,57 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false) allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false)
project.add_master(user) project.add_master(user)
sign_in(user) sign_in(user)
end
context 'Readme button' do
before do
allow(Project).to receive(:find_by_full_path)
.with(project.full_path, follow_redirects: true)
.and_return(project)
end
context 'when the project has a populated Readme' do
it 'show the "Readme" anchor' do
visit project_path(project)
expect(project.repository.readme).not_to be_nil
page.within('.project-stats') do
expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path)
expect(page).to have_link('Readme', href: presenter.readme_path)
end
end
context 'when the project has an empty Readme' do
it 'show the "Readme" anchor' do
allow(project.repository).to receive(:readme).and_return(fake_blob(path: 'README.md', data: '', size: 0))
visit project_path(project)
page.within('.project-stats') do
expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path)
expect(page).to have_link('Readme', href: presenter.readme_path)
end
end
end
end
context 'when the project does not have a Readme' do
it 'shows the "Add Readme" button' do
allow(project.repository).to receive(:readme).and_return(nil)
visit project_path(project) visit project_path(project)
page.within('.project-stats') do
expect(page).to have_link('Add Readme', href: presenter.add_readme_path)
end
end
end
end end
it 'no "Add Changelog" button if the project already has a changelog' do it 'no "Add Changelog" button if the project already has a changelog' do
visit project_path(project)
expect(project.repository.changelog).not_to be_nil expect(project.repository.changelog).not_to be_nil
page.within('.project-stats') do page.within('.project-stats') do
...@@ -154,6 +202,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -154,6 +202,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end end
it 'no "Add License" button if the project already has a license' do it 'no "Add License" button if the project already has a license' do
visit project_path(project)
expect(project.repository.license_blob).not_to be_nil expect(project.repository.license_blob).not_to be_nil
page.within('.project-stats') do page.within('.project-stats') do
...@@ -162,6 +212,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -162,6 +212,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end end
it 'no "Add Contribution guide" button if the project already has a contribution guide' do it 'no "Add Contribution guide" button if the project already has a contribution guide' do
visit project_path(project)
expect(project.repository.contribution_guide).not_to be_nil expect(project.repository.contribution_guide).not_to be_nil
page.within('.project-stats') do page.within('.project-stats') do
...@@ -171,6 +223,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -171,6 +223,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'GitLab CI configuration button' do describe 'GitLab CI configuration button' do
it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
visit project_path(project)
expect(project.repository.gitlab_ci_yml).to be_nil expect(project.repository.gitlab_ci_yml).to be_nil
page.within('.project-stats') do page.within('.project-stats') do
...@@ -211,6 +265,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -211,6 +265,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Auto DevOps button' do describe 'Auto DevOps button' do
it '"Enable Auto DevOps" button linked to settings page' do it '"Enable Auto DevOps" button linked to settings page' do
visit project_path(project)
page.within('.project-stats') do page.within('.project-stats') do
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end end
...@@ -263,6 +319,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -263,6 +319,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Kubernetes cluster button' do describe 'Kubernetes cluster button' do
it '"Add Kubernetes cluster" button linked to clusters page' do it '"Add Kubernetes cluster" button linked to clusters page' do
visit project_path(project)
page.within('.project-stats') do page.within('.project-stats') do
expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project)) expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
end end
......
...@@ -35,7 +35,6 @@ feature 'Master deletes tag' do ...@@ -35,7 +35,6 @@ feature 'Master deletes tag' do
end end
context 'when pre-receive hook fails', :js do context 'when pre-receive hook fails', :js do
context 'when Gitaly operation_user_delete_tag feature is enabled' do
before do before do
allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag) allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
.and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
...@@ -48,20 +47,6 @@ feature 'Master deletes tag' do ...@@ -48,20 +47,6 @@ feature 'Master deletes tag' do
end end
end end
context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
before do
allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
.and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
end
scenario 'shows the error message' do
delete_first_tag
expect(page).to have_content('Do not delete tags')
end
end
end
def delete_first_tag def delete_first_tag
page.within('.content') do page.within('.content') do
accept_confirm { first('.btn-remove').click } accept_confirm { first('.btn-remove').click }
......
...@@ -24,21 +24,6 @@ describe('DiffsStoreMutations', () => { ...@@ -24,21 +24,6 @@ describe('DiffsStoreMutations', () => {
}); });
}); });
describe('SET_DIFF_FILES', () => {
it('should set diff files to state', () => {
const filePath = '/first-diff-file-path';
const state = {};
const diffFiles = {
a_mode: 1,
highlighted_diff_lines: [{ file_path: filePath }],
};
mutations[types.SET_DIFF_FILES](state, diffFiles);
expect(state.diffFiles.aMode).toEqual(1);
expect(state.diffFiles.highlightedDiffLines[0].filePath).toEqual(filePath);
});
});
describe('SET_DIFF_VIEW_TYPE', () => { describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => { it('should set diff view type properly', () => {
const state = {}; const state = {};
......
...@@ -210,6 +210,13 @@ describe Banzai::Filter::MergeRequestReferenceFilter do ...@@ -210,6 +210,13 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
.to eq reference .to eq reference
end end
it 'commit ref tag is valid' do
doc = reference_filter("See #{reference}")
commit_ref_tag = doc.css('a').first.css('span.gfm.gfm-commit')
expect(commit_ref_tag.text).to eq(commit.short_id)
end
it 'has valid text' do it 'has valid text' do
doc = reference_filter("See #{reference}") doc = reference_filter("See #{reference}")
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180626125654 do
describe '#perform' do describe '#perform' do
context 'when diff files can be deleted' do context 'when diff files can be deleted' do
let(:merge_request) { create(:merge_request, :merged) } let(:merge_request) { create(:merge_request, :merged) }
......
...@@ -26,6 +26,21 @@ describe Gitlab::Diff::File do ...@@ -26,6 +26,21 @@ describe Gitlab::Diff::File do
end end
end end
describe '#diff_lines_for_serializer' do
it 'includes bottom match line if not in the end' do
expect(diff_file.diff_lines_for_serializer.last.type).to eq('match')
end
context 'when deleted' do
let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
it 'does not include bottom match line' do
expect(diff_file.diff_lines_for_serializer.last.type).not_to eq('match')
end
end
end
describe '#mode_changed?' do describe '#mode_changed?' do
it { expect(diff_file.mode_changed?).to be_falsey } it { expect(diff_file.mode_changed?).to be_falsey }
end end
......
...@@ -20,10 +20,11 @@ describe Gitlab::Gfm::UploadsRewriter do ...@@ -20,10 +20,11 @@ describe Gitlab::Gfm::UploadsRewriter do
"Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}" "Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}"
end end
shared_examples "files are accessible" do
describe '#rewrite' do describe '#rewrite' do
let!(:new_text) { rewriter.rewrite(new_project) } let!(:new_text) { rewriter.rewrite(new_project) }
let(:old_files) { [image_uploader, zip_uploader].map(&:file) } let(:old_files) { [image_uploader, zip_uploader] }
let(:new_files) do let(:new_files) do
described_class.new(new_text, new_project, user).files described_class.new(new_text, new_project, user).files
end end
...@@ -52,6 +53,23 @@ describe Gitlab::Gfm::UploadsRewriter do ...@@ -52,6 +53,23 @@ describe Gitlab::Gfm::UploadsRewriter do
expect(new_paths).not_to include zip_uploader.secret expect(new_paths).not_to include zip_uploader.secret
end end
end end
end
context "file are stored locally" do
include_examples "files are accessible"
end
context "files are stored remotely" do
before do
stub_uploads_object_storage(FileUploader)
old_files.each do |file|
file.migrate!(ObjectStorage::Store::REMOTE)
end
end
include_examples "files are accessible"
end
describe '#needs_rewrite?' do describe '#needs_rewrite?' do
subject { rewriter.needs_rewrite? } subject { rewriter.needs_rewrite? }
......
...@@ -1971,7 +1971,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1971,7 +1971,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
context 'with gitaly' do
it "calls Gitaly's OperationService" do it "calls Gitaly's OperationService" do
expect_any_instance_of(Gitlab::GitalyClient::OperationService) expect_any_instance_of(Gitlab::GitalyClient::OperationService)
.to receive(:user_ff_branch).with(user, source_sha, target_branch) .to receive(:user_ff_branch).with(user, source_sha, target_branch)
...@@ -1983,11 +1982,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1983,11 +1982,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
it_behaves_like '#ff_merge' it_behaves_like '#ff_merge'
end end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like '#ff_merge'
end
end
describe '#delete_all_refs_except' do describe '#delete_all_refs_except' do
let(:repository) do let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
...@@ -2308,6 +2302,7 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2308,6 +2302,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
end end
end end
end
describe '#squash' do describe '#squash' do
let(:squash_id) { '1' } let(:squash_id) { '1' }
...@@ -2327,7 +2322,8 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2327,7 +2322,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
repository.squash(user, squash_id, opts) repository.squash(user, squash_id, opts)
end end
context 'sparse checkout', :skip_gitaly_mock do # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'sparse checkout' do
let(:expected_files) { %w(files files/js files/js/application.js) } let(:expected_files) { %w(files files/js files/js/application.js) }
it 'checks out only the files in the diff' do it 'checks out only the files in the diff' do
...@@ -2372,7 +2368,8 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2372,7 +2368,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
context 'with an ASCII-8BIT diff', :skip_gitaly_mock do # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'with an ASCII-8BIT diff' do
let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" } let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
it 'applies a ASCII-8BIT diff' do it 'applies a ASCII-8BIT diff' do
...@@ -2383,7 +2380,8 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2383,7 +2380,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'with trailing whitespace in an invalid patch' do
let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" } let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
it 'does not include whitespace warnings in the error' do it 'does not include whitespace warnings in the error' do
...@@ -2397,7 +2395,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2397,7 +2395,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
end end
end
def create_remote_branch(repository, remote_name, branch_name, source_branch_name) def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
source_branch = repository.branches.find { |branch| branch.name == source_branch_name } source_branch = repository.branches.find { |branch| branch.name == source_branch_name }
......
...@@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do ...@@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message, repository: repository_message,
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660', left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
right_commit_id: commit.id, right_commit_id: commit.id,
collapse_diffs: true, collapse_diffs: false,
enforce_limits: true, enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h **Gitlab::Git::DiffCollection.collection_limits.to_h
) )
...@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do ...@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message, repository: repository_message,
left_commit_id: Gitlab::Git::EMPTY_TREE_ID, left_commit_id: Gitlab::Git::EMPTY_TREE_ID,
right_commit_id: initial_commit.id, right_commit_id: initial_commit.id,
collapse_diffs: true, collapse_diffs: false,
enforce_limits: true, enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h **Gitlab::Git::DiffCollection.collection_limits.to_h
) )
......
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20180619121030_enqueue_delete_diff_files_workers.rb')
describe EnqueueDeleteDiffFilesWorkers, :migration, :sidekiq do
let(:merge_request_diffs) { table(:merge_request_diffs) }
let(:merge_requests) { table(:merge_requests) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
before do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab')
projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab')
merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged')
merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected')
merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'without_files')
merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'collected')
merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected')
merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'empty')
merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected')
merge_requests.update(1, latest_merge_request_diff_id: 6)
end
it 'correctly schedules diff file deletion workers' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
# 1st batch
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(8.minutes, 1)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(9.minutes, 3)
# 2nd batch
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(16.minutes, 4)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(17.minutes, 6)
expect(BackgroundMigrationWorker.jobs.size).to eq(4)
end
end
end
it 'migrates the data' do
expect { migrate! }.to change { merge_request_diffs.where(state: 'without_files').count }
.from(1).to(4)
end
end
...@@ -9,6 +9,24 @@ describe WebHookLog do ...@@ -9,6 +9,24 @@ describe WebHookLog do
it { is_expected.to validate_presence_of(:web_hook) } it { is_expected.to validate_presence_of(:web_hook) }
describe '.recent' do
let(:hook) { create(:project_hook) }
it 'does not return web hook logs that are too old' do
create(:web_hook_log, web_hook: hook, created_at: 91.days.ago)
expect(described_class.recent.size).to be_zero
end
it 'returns the web hook logs in descending order' do
hook1 = create(:web_hook_log, web_hook: hook, created_at: 2.hours.ago)
hook2 = create(:web_hook_log, web_hook: hook, created_at: 1.hour.ago)
hooks = described_class.recent.to_a
expect(hooks).to eq([hook2, hook1])
end
end
describe '#success?' do describe '#success?' do
let(:web_hook_log) { build(:web_hook_log, response_status: status) } let(:web_hook_log) { build(:web_hook_log, response_status: status) }
......
...@@ -153,6 +153,13 @@ describe MergeRequestDiff do ...@@ -153,6 +153,13 @@ describe MergeRequestDiff do
expect(mr_diff.empty?).to be_truthy expect(mr_diff.empty?).to be_truthy
end end
it 'expands collapsed diffs before saving' do
mr_diff = create(:merge_request, source_branch: 'expand-collapse-lines', target_branch: 'master').merge_request_diff
diff_file = mr_diff.merge_request_diff_files.find_by(new_path: 'expand-collapse/file-5.txt')
expect(diff_file.diff).not_to be_empty
end
it 'saves binary diffs correctly' do it 'saves binary diffs correctly' do
path = 'files/images/icn-time-tracking.pdf' path = 'files/images/icn-time-tracking.pdf'
mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff
......
...@@ -120,6 +120,14 @@ describe BambooService, :use_clean_rails_memory_store_caching do ...@@ -120,6 +120,14 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end end
end end
describe '#execute' do
it 'runs update and build action' do
stub_update_and_build_request
subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
end
end
describe '#build_page' do describe '#build_page' do
it 'returns the contents of the reactive cache' do it 'returns the contents of the reactive cache' do
stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref') stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref')
...@@ -216,10 +224,20 @@ describe BambooService, :use_clean_rails_memory_store_caching do ...@@ -216,10 +224,20 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end end
end end
def stub_update_and_build_request(status: 200, body: nil)
bamboo_full_url = 'http://gitlab.com/bamboo/updateAndBuild.action?buildKey=foo&os_authType=basic'
stub_bamboo_request(bamboo_full_url, status, body)
end
def stub_request(status: 200, body: nil) def stub_request(status: 200, body: nil)
bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic' bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result/byChangeset/123?os_authType=basic'
stub_bamboo_request(bamboo_full_url, status, body)
end
WebMock.stub_request(:get, bamboo_full_url).to_return( def stub_bamboo_request(url, status, body)
WebMock.stub_request(:get, url).to_return(
status: status, status: status,
headers: { 'Content-Type' => 'application/json' }, headers: { 'Content-Type' => 'application/json' },
body: body body: body
......
...@@ -1861,7 +1861,6 @@ describe Repository do ...@@ -1861,7 +1861,6 @@ describe Repository do
describe '#add_tag' do describe '#add_tag' do
let(:user) { build_stubbed(:user) } let(:user) { build_stubbed(:user) }
shared_examples 'adding tag' do
context 'with a valid target' do context 'with a valid target' do
it 'creates the tag' do it 'creates the tag' do
repository.add_tag(user, '8.5', 'master', 'foo') repository.add_tag(user, '8.5', 'master', 'foo')
...@@ -1886,52 +1885,13 @@ describe Repository do ...@@ -1886,52 +1885,13 @@ describe Repository do
end end
end end
context 'when Gitaly operation_user_add_tag feature is enabled' do
it_behaves_like 'adding tag'
end
context 'when Gitaly operation_user_add_tag feature is disabled', :disable_gitaly do
it_behaves_like 'adding tag'
it 'passes commit SHA to pre-receive and update hooks and tag SHA to post-receive hook' do
pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project)
update_hook = Gitlab::Git::Hook.new('update', project)
post_receive_hook = Gitlab::Git::Hook.new('post-receive', project)
allow(Gitlab::Git::Hook).to receive(:new)
.and_return(pre_receive_hook, update_hook, post_receive_hook)
allow(pre_receive_hook).to receive(:trigger).and_call_original
allow(update_hook).to receive(:trigger).and_call_original
allow(post_receive_hook).to receive(:trigger).and_call_original
tag = repository.add_tag(user, '8.5', 'master', 'foo')
commit_sha = repository.commit('master').id
tag_sha = tag.target
expect(pre_receive_hook).to have_received(:trigger)
.with(anything, anything, anything, commit_sha, anything)
expect(update_hook).to have_received(:trigger)
.with(anything, anything, anything, commit_sha, anything)
expect(post_receive_hook).to have_received(:trigger)
.with(anything, anything, anything, tag_sha, anything)
end
end
end
describe '#rm_branch' do describe '#rm_branch' do
shared_examples "user deleting a branch" do
it 'removes a branch' do it 'removes a branch' do
expect(repository).to receive(:before_remove_branch) expect(repository).to receive(:before_remove_branch)
expect(repository).to receive(:after_remove_branch) expect(repository).to receive(:after_remove_branch)
repository.rm_branch(user, 'feature') repository.rm_branch(user, 'feature')
end end
end
context 'with gitaly enabled' do
it_behaves_like "user deleting a branch"
context 'when pre hooks failed' do context 'when pre hooks failed' do
before do before do
...@@ -1949,52 +1909,7 @@ describe Repository do ...@@ -1949,52 +1909,7 @@ describe Repository do
end end
end end
context 'with gitaly disabled', :disable_gitaly do
it_behaves_like "user deleting a branch"
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
let(:blank_sha) { '0000000000000000000000000000000000000000' }
context 'when pre hooks were successful' do
it 'runs without errors' do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
.with(git_user, repository.raw_repository, old_rev, blank_sha, 'refs/heads/feature')
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
end
it 'deletes the branch' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
expect(repository.find_branch('feature')).to be_nil
end
end
context 'when pre hooks failed' do
it 'gets an error' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
expect do
repository.rm_branch(user, 'feature')
end.to raise_error(Gitlab::Git::PreReceiveError)
end
it 'does not delete the branch' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
expect do
repository.rm_branch(user, 'feature')
end.to raise_error(Gitlab::Git::PreReceiveError)
expect(repository.find_branch('feature')).not_to be_nil
end
end
end
end
describe '#rm_tag' do describe '#rm_tag' do
shared_examples 'removing tag' do
it 'removes a tag' do it 'removes a tag' do
expect(repository).to receive(:before_remove_tag) expect(repository).to receive(:before_remove_tag)
...@@ -2004,15 +1919,6 @@ describe Repository do ...@@ -2004,15 +1919,6 @@ describe Repository do
end end
end end
context 'when Gitaly operation_user_delete_tag feature is enabled' do
it_behaves_like 'removing tag'
end
context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
it_behaves_like 'removing tag'
end
end
describe '#avatar' do describe '#avatar' do
it 'returns nil if repo does not exist' do it 'returns nil if repo does not exist' do
allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository) allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository)
......
...@@ -71,17 +71,5 @@ describe Files::UpdateService do ...@@ -71,17 +71,5 @@ describe Files::UpdateService do
expect(results.data).to eq(new_contents) expect(results.data).to eq(new_contents)
end end
end end
context 'with gitaly disabled', :skip_gitaly_mock do
context 'when target branch is different than source branch' do
let(:branch_name) { "#{project.default_branch}-new" }
it 'fires hooks only once' do
expect(Gitlab::Git::HooksService).to receive(:new).once.and_call_original
subject.execute
end
end
end
end end
end end
...@@ -36,9 +36,9 @@ describe MergeRequests::RebaseService do ...@@ -36,9 +36,9 @@ describe MergeRequests::RebaseService do
end end
end end
context 'when unexpected error occurs', :disable_gitaly do context 'when unexpected error occurs' do
before do before do
allow(repository).to receive(:run_git!).and_raise('Something went wrong') allow(repository).to receive(:gitaly_operation_client).and_raise('Something went wrong')
end end
it 'saves a generic error message' do it 'saves a generic error message' do
...@@ -53,9 +53,9 @@ describe MergeRequests::RebaseService do ...@@ -53,9 +53,9 @@ describe MergeRequests::RebaseService do
end end
end end
context 'with git command failure', :disable_gitaly do context 'with git command failure' do
before do before do
allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong') allow(repository).to receive(:gitaly_operation_client).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong')
end end
it 'saves a generic error message' do it 'saves a generic error message' do
...@@ -71,7 +71,7 @@ describe MergeRequests::RebaseService do ...@@ -71,7 +71,7 @@ describe MergeRequests::RebaseService do
end end
context 'valid params' do context 'valid params' do
shared_examples 'successful rebase' do describe 'successful rebase' do
before do before do
service.execute(merge_request) service.execute(merge_request)
end end
...@@ -97,26 +97,8 @@ describe MergeRequests::RebaseService do ...@@ -97,26 +97,8 @@ describe MergeRequests::RebaseService do
end end
end end
context 'when Gitaly rebase feature is enabled' do
it_behaves_like 'successful rebase'
end
context 'when Gitaly rebase feature is disabled', :disable_gitaly do
it_behaves_like 'successful rebase'
end
context 'git commands', :disable_gitaly do
it 'sets GL_REPOSITORY env variable when calling git commands' do
expect(repository).to receive(:popen).exactly(3)
.with(anything, anything, hash_including('GL_REPOSITORY'), anything)
.and_return(['', 0])
service.execute(merge_request)
end
end
context 'fork' do context 'fork' do
shared_examples 'successful fork rebase' do describe 'successful fork rebase' do
let(:forked_project) do let(:forked_project) do
fork_project(project, user, repository: true) fork_project(project, user, repository: true)
end end
...@@ -140,14 +122,6 @@ describe MergeRequests::RebaseService do ...@@ -140,14 +122,6 @@ describe MergeRequests::RebaseService do
expect(parent_sha).to eq(target_branch_sha) expect(parent_sha).to eq(target_branch_sha)
end end
end end
context 'when Gitaly rebase feature is enabled' do
it_behaves_like 'successful fork rebase'
end
context 'when Gitaly rebase feature is disabled', :disable_gitaly do
it_behaves_like 'successful fork rebase'
end
end end
end end
end end
......
...@@ -124,51 +124,6 @@ describe MergeRequests::SquashService do ...@@ -124,51 +124,6 @@ describe MergeRequests::SquashService do
message: a_string_including('squash')) message: a_string_including('squash'))
end end
end end
context 'with Gitaly disabled', :skip_gitaly_mock do
stages = {
'add worktree for squash' => 'worktree',
'configure sparse checkout' => 'config',
'get files in diff' => 'diff --name-only',
'check out target branch' => 'checkout',
'apply patch' => 'diff --binary',
'commit squashed changes' => 'commit',
'get SHA of squashed commit' => 'rev-parse'
}
stages.each do |stage, command|
context "when the #{stage} stage fails" do
before do
git_command = a_collection_containing_exactly(
a_string_starting_with("#{Gitlab.config.git.bin_path} #{command}")
).or(
a_collection_starting_with([Gitlab.config.git.bin_path] + command.split)
)
allow(repository).to receive(:popen).and_return(['', 0])
allow(repository).to receive(:popen).with(git_command, anything, anything, anything).and_return([error, 1])
end
it 'logs the stage and output' do
expect(service).to receive(:log_error).with(log_error)
expect(service).to receive(:log_error).with(error)
service.execute(merge_request)
end
it 'returns an error' do
expect(service.execute(merge_request)).to match(status: :error,
message: a_string_including('squash'))
end
it 'cleans up the temporary directory' do
expect(File.exist?(squash_dir_path)).to be(false)
service.execute(merge_request)
end
end
end
end
end end
context 'when any other exception is thrown' do context 'when any other exception is thrown' do
......
...@@ -80,6 +80,50 @@ describe FileUploader do ...@@ -80,6 +80,50 @@ describe FileUploader do
end end
end end
describe 'copy_to' do
shared_examples 'returns a valid uploader' do
describe 'returned uploader' do
let(:new_project) { create(:project) }
let(:moved) { described_class.copy_to(subject, new_project) }
it 'generates a new secret' do
expect(subject).to be
expect(described_class).to receive(:generate_secret).once.and_call_original
expect(moved).to be
end
it 'create new upload' do
expect(moved.upload).not_to eq(subject.upload)
end
it 'copies the file' do
expect(subject.file).to exist
expect(moved.file).to exist
expect(subject.file).not_to eq(moved.file)
expect(subject.object_store).to eq(moved.object_store)
end
end
end
context 'files are stored locally' do
before do
subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
end
include_examples 'returns a valid uploader'
end
context 'files are stored remotely' do
before do
stub_uploads_object_storage
subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
subject.migrate!(ObjectStorage::Store::REMOTE)
end
include_examples 'returns a valid uploader'
end
end
describe '#secret' do describe '#secret' do
it 'generates a secret if none is provided' do it 'generates a secret if none is provided' do
expect(described_class).to receive(:generate_secret).and_return('secret') expect(described_class).to receive(:generate_secret).and_return('secret')
......
require 'spec_helper'
describe PruneWebHookLogsWorker do
describe '#perform' do
before do
hook = create(:project_hook)
5.times do
create(:web_hook_log, web_hook: hook, created_at: 5.months.ago)
end
create(:web_hook_log, web_hook: hook, response_status: '404')
end
it 'removes all web hook logs older than one month' do
described_class.new.perform
expect(WebHookLog.count).to eq(1)
expect(WebHookLog.first.response_status).to eq('404')
end
end
end
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