summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile.lock10
-rw-r--r--app/assets/javascripts/notes/services/notes_service.js5
-rw-r--r--app/assets/javascripts/notes/stores/actions.js8
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge.js44
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue47
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js1
-rw-r--r--app/assets/stylesheets/pages/issuable.scss40
-rw-r--r--app/helpers/import_helper.rb13
-rw-r--r--app/helpers/notes_helper.rb2
-rw-r--r--app/models/compare.rb39
-rw-r--r--app/services/compare_service.rb9
-rw-r--r--app/views/shared/boards/_show.html.haml2
-rw-r--r--app/views/shared/issuable/form/_contribution.html.haml2
-rw-r--r--changelogs/unreleased/34604-fix-generated-url-for-external-repository.yml5
-rw-r--r--changelogs/unreleased/42579-fix-sidebar-dropdown-hover-style.yml5
-rw-r--r--changelogs/unreleased/42814-fix-remove-source-branch-when-mwps.yml6
-rw-r--r--changelogs/unreleased/44139-fix-issue-boards-dup-keys.yml6
-rw-r--r--changelogs/unreleased/add-indexes-to-todos-for-heavy-users-like-sean.yml5
-rw-r--r--changelogs/unreleased/fix-code-search-500-with-non-ascii-filename.yml5
-rw-r--r--changelogs/unreleased/osw-stop-recalculating-merge-base-on-mr-loading.yml5
-rw-r--r--changelogs/unreleased/refactor-move-mr-widget-nothing-to-merge-vue-component.yml5
-rw-r--r--changelogs/unreleased/tc-api-fix-expose_url.yml5
-rw-r--r--config/initializers/backtrace_silencers.rb9
-rw-r--r--db/migrate/20180309160427_add_partial_indexes_on_todos.rb28
-rw-r--r--db/schema.rb4
-rw-r--r--doc/development/fe_guide/style_guide_js.md51
-rw-r--r--doc/development/fe_guide/vue.md12
-rw-r--r--doc/user/project/merge_requests/img/allow_maintainer_push.pngbin99079 -> 49216 bytes
-rw-r--r--lib/api/helpers/related_resources_helpers.rb2
-rw-r--r--lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb2
-rw-r--r--lib/gitlab.rb1
-rw-r--r--lib/gitlab/auth/saml/config.rb2
-rw-r--r--lib/gitlab/diff/diff_refs.rb6
-rw-r--r--lib/gitlab/github_import/client.rb5
-rw-r--r--lib/gitlab/gitlab_import/client.rb2
-rw-r--r--lib/gitlab/legacy_github_import/client.rb2
-rw-r--r--lib/gitlab/search_results.rb4
-rw-r--r--lib/google_api/auth.rb2
-rw-r--r--locale/gitlab.pot108
-rw-r--r--package.json2
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb6
-rw-r--r--spec/helpers/import_helper_spec.rb33
-rw-r--r--spec/javascripts/notes/mock_data.js2
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js65
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js15
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js20
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js34
-rw-r--r--spec/lib/api/helpers/related_resources_helpers_spec.rb41
-rw-r--r--spec/lib/gitlab/profiler_spec.rb4
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb16
-rw-r--r--spec/models/compare_spec.rb40
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml2
-rw-r--r--yarn.lock6
60 files changed, 630 insertions, 198 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index fe6d01c1a45..5aee1345c52 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.88.0
+0.89.0
diff --git a/Gemfile.lock b/Gemfile.lock
index fa99ec3febe..b85c7085d07 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -63,7 +63,7 @@ GEM
fog-core
mime-types (>= 2.99)
unf
- ast (2.3.0)
+ ast (2.4.0)
atomic (1.1.99)
attr_encrypted (3.0.3)
encryptor (~> 3.0.0)
@@ -586,8 +586,8 @@ GEM
orm_adapter (0.5.0)
os (0.9.6)
parallel (1.12.1)
- parser (2.4.0.2)
- ast (~> 2.3)
+ parser (2.5.0.3)
+ ast (~> 2.4.0)
parslet (1.5.0)
blankslate (~> 2.0)
path_expander (1.0.2)
@@ -951,13 +951,13 @@ GEM
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uniform_notifier (1.10.0)
- unparser (0.2.6)
+ unparser (0.2.7)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
concord (~> 0.1.5)
diff-lcs (~> 1.3)
equalizer (~> 0.0.9)
- parser (>= 2.3.1.2, < 2.5)
+ parser (>= 2.3.1.2, < 2.6)
procto (~> 0.0.2)
url_safe_base64 (0.2.2)
validates_hostname (1.0.6)
diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js
index 4766351dfc5..b4c19a9ec22 100644
--- a/app/assets/javascripts/notes/services/notes_service.js
+++ b/app/assets/javascripts/notes/services/notes_service.js
@@ -27,10 +27,11 @@ export default {
return Vue.http[method](endpoint);
},
poll(data = {}) {
- const { endpoint, lastFetchedAt } = data;
+ const endpoint = data.notesData.notesPath;
+ const lastFetchedAt = data.lastFetchedAt;
const options = {
headers: {
- 'X-Last-Fetched-At': lastFetchedAt,
+ 'X-Last-Fetched-At': lastFetchedAt ? `${lastFetchedAt}` : undefined,
},
};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 70fc368a471..ebbacb576d6 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -209,18 +209,16 @@ const pollSuccessCallBack = (resp, commit, state, getters) => {
});
}
- commit(types.SET_LAST_FETCHED_AT, resp.lastFetchedAt);
+ commit(types.SET_LAST_FETCHED_AT, resp.last_fetched_at);
return resp;
};
export const poll = ({ commit, state, getters }) => {
- const requestData = { endpoint: state.notesData.notesPath, lastFetchedAt: state.lastFetchedAt };
-
eTagPoll = new Poll({
resource: service,
method: 'poll',
- data: requestData,
+ data: state,
successCallback: resp => resp.json()
.then(data => pollSuccessCallBack(data, commit, state, getters)),
errorCallback: () => Flash('Something went wrong while fetching latest comments.'),
@@ -229,7 +227,7 @@ export const poll = ({ commit, state, getters }) => {
if (!Visibility.hidden()) {
eTagPoll.makeRequest();
} else {
- service.poll(requestData);
+ service.poll(state);
}
Visibility.change(() => {
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index f07afbe4303..9308daa36f1 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -90,19 +90,21 @@ export default {
const notes = [];
notesData.forEach((note) => {
- const nn = Object.assign({}, note);
-
// To support legacy notes, should be very rare case.
if (note.individual_note && note.notes.length > 1) {
note.notes.forEach((n) => {
- nn.notes = [n]; // override notes array to only have one item to mimick individual_note
- notes.push(nn);
+ notes.push({
+ ...note,
+ notes: [n], // override notes array to only have one item to mimick individual_note
+ });
});
} else {
const oldNote = utils.findNoteObjectById(state.notes, note.id);
- nn.expanded = oldNote ? oldNote.expanded : note.expanded;
- notes.push(nn);
+ notes.push({
+ ...note,
+ expanded: (oldNote ? oldNote.expanded : note.expanded),
+ });
}
});
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
index de98a77be6f..7ff7fc7988a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
@@ -63,7 +63,7 @@
};
this.isRemovingSourceBranch = true;
- this.service.mergeResource.save(options)
+ this.service.merge(options)
.then(res => res.data)
.then((data) => {
if (data.status === 'merge_when_pipeline_succeeds') {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge.js
deleted file mode 100644
index ebfd6765934..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import emptyStateSVG from 'icons/_mr_widget_empty_state.svg';
-
-export default {
- name: 'MRWidgetNothingToMerge',
- props: {
- mr: {
- type: Object,
- required: true,
- },
- },
- data() {
- return { emptyStateSVG };
- },
- template: `
- <div class="mr-widget-body mr-widget-empty-state">
- <div class="row">
- <div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
- <span v-html="emptyStateSVG"></span>
- </div>
- <div class="text col-sm-7 col-sm-pull-5 col-xs-12">
- <span>
- Merge requests are a place to propose changes you have made to a project
- and discuss those changes with others.
- </span>
- <p>
- Interested parties can even contribute by pushing commits if they want to.
- </p>
- <p>
- Currently there are no changes in this merge request's source branch.
- Please push new commits or use a different branch.
- </p>
- <div>
- <a
- v-if="mr.newBlobPath"
- :href="mr.newBlobPath"
- class="btn btn-inverted btn-save">
- Create file
- </a>
- </div>
- </div>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
new file mode 100644
index 00000000000..3d9161f6926
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
@@ -0,0 +1,47 @@
+<script>
+import emptyStateSVG from 'icons/_mr_widget_empty_state.svg';
+
+export default {
+ name: 'MRWidgetNothingToMerge',
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return { emptyStateSVG };
+ },
+};
+</script>
+
+<template>
+ <div class="mr-widget-body mr-widget-empty-state">
+ <div class="row">
+ <div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
+ <span v-html="emptyStateSVG"></span>
+ </div>
+ <div class="text col-sm-7 col-sm-pull-5 col-xs-12">
+ <span>
+ Merge requests are a place to propose changes you have made to a project
+ and discuss those changes with others.
+ </span>
+ <p>
+ Interested parties can even contribute by pushing commits if they want to.
+ </p>
+ <p>
+ Currently there are no changes in this merge request's source branch.
+ Please push new commits or use a different branch.
+ </p>
+ <div>
+ <a
+ v-if="mr.newBlobPath"
+ :href="mr.newBlobPath"
+ class="btn btn-inverted btn-save">
+ Create file
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index b867dd90a41..20624aad0ad 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -24,7 +24,7 @@ export { default as MergingState } from './components/states/mr_widget_merging.v
export { default as WipState } from './components/states/mr_widget_wip';
export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
-export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
+export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
export { default as ReadyToMergeState } from './components/states/mr_widget_ready_to_merge';
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index 01365b70897..cc8bc6af1e1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -71,7 +71,8 @@ export default {
return this.mr.deployments.length;
},
shouldRenderSourceBranchRemovalStatus() {
- return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch;
+ return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch &&
+ (!this.mr.isNothingToMergeState && !this.mr.isMergedState);
},
},
methods: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 5d07bcf1bb9..a47ca9fae86 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -125,6 +125,10 @@ export default class MergeRequestStore {
return this.state === stateKey.nothingToMerge;
}
+ get isMergedState() {
+ return this.state === stateKey.merged;
+ }
+
initRebase(data) {
this.canPushToSourceBranch = data.can_push_to_source_branch;
this.rebaseInProgress = data.rebase_in_progress;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
index 29d5bd4a1da..483ad52b8cc 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
@@ -49,6 +49,7 @@ export const stateKey = {
notAllowedToMerge: 'notAllowedToMerge',
readyToMerge: 'readyToMerge',
rebase: 'rebase',
+ merged: 'merged',
};
export default {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 4c9732c26d9..e21a9f0afc9 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -137,12 +137,22 @@
z-index: 200;
overflow: hidden;
- a:not(.btn-retry),
- .btn-link {
+ a:not(.btn) {
color: inherit;
+
+ &:hover {
+ color: $gl-link-hover-color;
+
+ .avatar {
+ border-color: rgba($avatar-border, .2);
+ }
+
+ }
+
}
.btn-link {
+ color: inherit;
outline: none;
}
@@ -214,7 +224,7 @@
&:hover {
text-decoration: underline;
- color: $md-link-color;
+ color: $gl-link-hover-color;
}
}
}
@@ -486,16 +496,6 @@
}
}
- a:not(.btn-retry) {
- &:hover {
- color: $md-link-color;
-
- .avatar {
- border-color: rgba($avatar-border, .2);
- }
- }
- }
-
.dropdown-menu-toggle {
width: 100%;
padding-top: 6px;
@@ -503,6 +503,20 @@
.dropdown-menu {
width: 100%;
+
+ /*
+ * Overwrite hover style for dropdown items, so that they are not blue
+ * This should be removed during dev of https://gitlab.com/gitlab-org/gitlab-ce/issues/44040
+ */
+ li a {
+ &:hover,
+ &:active,
+ &:focus,
+ &.is-focused {
+ @include dropdown-item-hover;
+ }
+ }
+
}
}
diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb
index 9149d79ecb8..4664b1728c4 100644
--- a/app/helpers/import_helper.rb
+++ b/app/helpers/import_helper.rb
@@ -1,4 +1,6 @@
module ImportHelper
+ include ::Gitlab::Utils::StrongMemoize
+
def has_ci_cd_only_params?
false
end
@@ -75,17 +77,18 @@ module ImportHelper
private
def github_project_url(full_path)
- "#{github_root_url}/#{full_path}"
+ URI.join(github_root_url, full_path).to_s
end
def github_root_url
- return @github_url if defined?(@github_url)
+ strong_memoize(:github_url) do
+ provider = Gitlab::Auth::OAuth::Provider.config_for('github')
- provider = Gitlab.config.omniauth.providers.find { |p| p.name == 'github' }
- @github_url = provider.fetch('url', 'https://github.com') if provider
+ provider&.dig('url').presence || 'https://github.com'
+ end
end
def gitea_project_url(full_path)
- "#{@gitea_host_url.sub(%r{/+\z}, '')}/#{full_path}"
+ URI.join(@gitea_host_url, full_path).to_s
end
end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index a70e73a6da9..20aed60cb7a 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -169,7 +169,7 @@ module NotesHelper
reopenPath: reopen_issuable_path(issuable),
notesPath: notes_url,
totalNotes: issuable.discussions.length,
- lastFetchedAt: Time.now
+ lastFetchedAt: Time.now.to_i
}.to_json
end
diff --git a/app/models/compare.rb b/app/models/compare.rb
index 3a8bbcb1acd..feb4b89c781 100644
--- a/app/models/compare.rb
+++ b/app/models/compare.rb
@@ -1,4 +1,6 @@
class Compare
+ include Gitlab::Utils::StrongMemoize
+
delegate :same, :head, :base, to: :@compare
attr_reader :project
@@ -11,9 +13,10 @@ class Compare
end
end
- def initialize(compare, project, straight: false)
+ def initialize(compare, project, base_sha: nil, straight: false)
@compare = compare
@project = project
+ @base_sha = base_sha
@straight = straight
end
@@ -22,40 +25,36 @@ class Compare
end
def start_commit
- return @start_commit if defined?(@start_commit)
+ strong_memoize(:start_commit) do
+ commit = @compare.base
- commit = @compare.base
- @start_commit = commit ? ::Commit.new(commit, project) : nil
+ ::Commit.new(commit, project) if commit
+ end
end
def head_commit
- return @head_commit if defined?(@head_commit)
+ strong_memoize(:head_commit) do
+ commit = @compare.head
- commit = @compare.head
- @head_commit = commit ? ::Commit.new(commit, project) : nil
+ ::Commit.new(commit, project) if commit
+ end
end
alias_method :commit, :head_commit
- def base_commit
- return @base_commit if defined?(@base_commit)
-
- @base_commit = if start_commit && head_commit
- project.merge_base_commit(start_commit.id, head_commit.id)
- else
- nil
- end
- end
-
def start_commit_sha
- start_commit.try(:sha)
+ start_commit&.sha
end
def base_commit_sha
- base_commit.try(:sha)
+ strong_memoize(:base_commit) do
+ next unless start_commit && head_commit
+
+ @base_sha || project.merge_base_commit(start_commit.id, head_commit.id)&.sha
+ end
end
def head_commit_sha
- commit.try(:sha)
+ commit&.sha
end
def raw_diffs(*args)
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index 1db91c3c90c..2a69a205629 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -10,9 +10,14 @@ class CompareService
@start_ref_name = new_start_ref_name
end
- def execute(target_project, target_ref, straight: false)
+ def execute(target_project, target_ref, base_sha: nil, straight: false)
raw_compare = target_project.repository.compare_source_branch(target_ref, start_project.repository, start_ref_name, straight: straight)
- Compare.new(raw_compare, target_project, straight: straight) if raw_compare
+ return unless raw_compare
+
+ Compare.new(raw_compare,
+ target_project,
+ base_sha: base_sha,
+ straight: straight)
end
end
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index 44b09545a61..dac60094686 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -27,7 +27,7 @@
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
":board-id" => "boardId",
- ":key" => "_uid" }
+ ":key" => "list.id" }
= render "shared/boards/components/sidebar", group: group
- if @project
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
diff --git a/app/views/shared/issuable/form/_contribution.html.haml b/app/views/shared/issuable/form/_contribution.html.haml
index 0f2d313a5cc..de508278d7c 100644
--- a/app/views/shared/issuable/form/_contribution.html.haml
+++ b/app/views/shared/issuable/form/_contribution.html.haml
@@ -14,7 +14,7 @@
.checkbox
= form.label :allow_maintainer_to_push do
= form.check_box :allow_maintainer_to_push, disabled: !issuable.can_allow_maintainer_to_push?(current_user)
- = _('Allow edits from maintainers')
+ = _('Allow edits from maintainers.')
= link_to 'About this feature', help_page_path('user/project/merge_requests/maintainer_access')
.help-block
= allow_maintainer_push_unavailable_reason(issuable)
diff --git a/changelogs/unreleased/34604-fix-generated-url-for-external-repository.yml b/changelogs/unreleased/34604-fix-generated-url-for-external-repository.yml
new file mode 100644
index 00000000000..c4b5f59b724
--- /dev/null
+++ b/changelogs/unreleased/34604-fix-generated-url-for-external-repository.yml
@@ -0,0 +1,5 @@
+---
+title: Fix generated URL when listing repoitories for import
+merge_request: 17692
+author:
+type: fixed
diff --git a/changelogs/unreleased/42579-fix-sidebar-dropdown-hover-style.yml b/changelogs/unreleased/42579-fix-sidebar-dropdown-hover-style.yml
new file mode 100644
index 00000000000..c0a247dc895
--- /dev/null
+++ b/changelogs/unreleased/42579-fix-sidebar-dropdown-hover-style.yml
@@ -0,0 +1,5 @@
+---
+title: Fix hover style of dropdown items in the right sidebar
+merge_request: 17519
+author:
+type: fixed
diff --git a/changelogs/unreleased/42814-fix-remove-source-branch-when-mwps.yml b/changelogs/unreleased/42814-fix-remove-source-branch-when-mwps.yml
new file mode 100644
index 00000000000..08e77ee7c3b
--- /dev/null
+++ b/changelogs/unreleased/42814-fix-remove-source-branch-when-mwps.yml
@@ -0,0 +1,6 @@
+---
+title: Fix "Remove source branch" button in Merge request widget during merge when pipeline
+ succeeds state
+merge_request: 17192
+author:
+type: fixed
diff --git a/changelogs/unreleased/44139-fix-issue-boards-dup-keys.yml b/changelogs/unreleased/44139-fix-issue-boards-dup-keys.yml
new file mode 100644
index 00000000000..dd5f2f06d6c
--- /dev/null
+++ b/changelogs/unreleased/44139-fix-issue-boards-dup-keys.yml
@@ -0,0 +1,6 @@
+---
+title: Use object ID to prevent duplicate keys Vue warning on Issue Boards page during
+ development
+merge_request: 17682
+author:
+type: other
diff --git a/changelogs/unreleased/add-indexes-to-todos-for-heavy-users-like-sean.yml b/changelogs/unreleased/add-indexes-to-todos-for-heavy-users-like-sean.yml
new file mode 100644
index 00000000000..f0e5103a9d9
--- /dev/null
+++ b/changelogs/unreleased/add-indexes-to-todos-for-heavy-users-like-sean.yml
@@ -0,0 +1,5 @@
+---
+title: Add partial indexes on todos to handle users with many todos
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/fix-code-search-500-with-non-ascii-filename.yml b/changelogs/unreleased/fix-code-search-500-with-non-ascii-filename.yml
new file mode 100644
index 00000000000..29e3b7be985
--- /dev/null
+++ b/changelogs/unreleased/fix-code-search-500-with-non-ascii-filename.yml
@@ -0,0 +1,5 @@
+---
+title: Fix code and wiki search results when filename is non-ASCII
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-stop-recalculating-merge-base-on-mr-loading.yml b/changelogs/unreleased/osw-stop-recalculating-merge-base-on-mr-loading.yml
new file mode 100644
index 00000000000..1673e1d3658
--- /dev/null
+++ b/changelogs/unreleased/osw-stop-recalculating-merge-base-on-mr-loading.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid re-fetching merge-base SHA from Gitaly unnecessarily
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/refactor-move-mr-widget-nothing-to-merge-vue-component.yml b/changelogs/unreleased/refactor-move-mr-widget-nothing-to-merge-vue-component.yml
new file mode 100644
index 00000000000..dc8ff95dc27
--- /dev/null
+++ b/changelogs/unreleased/refactor-move-mr-widget-nothing-to-merge-vue-component.yml
@@ -0,0 +1,5 @@
+---
+title: Move NothingToMerge vue component
+merge_request: 17544
+author: George Tsiolis
+type: performance
diff --git a/changelogs/unreleased/tc-api-fix-expose_url.yml b/changelogs/unreleased/tc-api-fix-expose_url.yml
new file mode 100644
index 00000000000..c701f64d6bf
--- /dev/null
+++ b/changelogs/unreleased/tc-api-fix-expose_url.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure the API returns https links when https is configured
+merge_request: 17681
+author:
+type: fixed
diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb
index 59385cdf379..58941aae1b0 100644
--- a/config/initializers/backtrace_silencers.rb
+++ b/config/initializers/backtrace_silencers.rb
@@ -1,7 +1,2 @@
-# Be sure to restart your server when you modify this file.
-
-# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
-# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
-
-# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
-# Rails.backtrace_cleaner.remove_silencers!
+Rails.backtrace_cleaner.remove_silencers!
+Rails.backtrace_cleaner.add_silencer { |line| line !~ Gitlab::APP_DIRS_PATTERN }
diff --git a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
new file mode 100644
index 00000000000..18a5c69df1b
--- /dev/null
+++ b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
@@ -0,0 +1,28 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPartialIndexesOnTodos < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME_PENDING="index_todos_on_user_id_and_id_pending"
+ INDEX_NAME_DONE="index_todos_on_user_id_and_id_done"
+
+ def up
+ unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_PENDING)
+ add_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
+ end
+ unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_DONE)
+ add_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
+ end
+ end
+
+ def down
+ remove_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
+ remove_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 970b1ad9948..ab4370e2754 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180309121820) do
+ActiveRecord::Schema.define(version: 20180309160427) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1778,6 +1778,8 @@ ActiveRecord::Schema.define(version: 20180309121820) do
add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree
add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree
add_index "todos", ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
+ add_index "todos", ["user_id", "id"], name: "index_todos_on_user_id_and_id_done", where: "((state)::text = 'done'::text)", using: :btree
+ add_index "todos", ["user_id", "id"], name: "index_todos_on_user_id_and_id_pending", where: "((state)::text = 'pending'::text)", using: :btree
add_index "todos", ["user_id"], name: "index_todos_on_user_id", using: :btree
create_table "trending_projects", force: :cascade do |t|
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index 917d28b48ee..7b5fa6ca42f 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -548,6 +548,57 @@ On those a default key should not be provided.
1. Properties in a Vue Component:
Check [order of properties in components rule][vue-order].
+#### `:key`
+When using `v-for` you need to provide a *unique* `:key` attribute for each item.
+
+1. If the elements of the array being iterated have an unique `id` it is advised to use it:
+ ```html
+ <div
+ v-for="item in items"
+ :key="item.id"
+ >
+ <!-- content -->
+ </div>
+ ```
+
+1. When the elements being iterated don't have a unique id, you can use the array index as the `:key` attribute
+ ```html
+ <div
+ v-for="(item, index) in items"
+ :key="index"
+ >
+ <!-- content -->
+ </div>
+ ```
+
+
+1. When using `v-for` with `template` and there is more than one child element, the `:key` values must be unique. It's advised to use `kebab-case` namespaces.
+ ```html
+ <template v-for="(item, index) in items">
+ <span :key="`span-${index}`"></span>
+ <button :key="`button-${index}`"></button>
+ </template>
+ ```
+
+1. When dealing with nested `v-for` use the same guidelines as above.
+ ```html
+ <div
+ v-for="item in items"
+ :key="item.id"
+ >
+ <span
+ v-for="element in array"
+ :key="element.id"
+ >
+ <!-- content -->
+ </span>
+ </div>
+ ```
+
+
+Useful links:
+1. [`key`](https://vuejs.org/v2/guide/list.html#key)
+1. [Vue Style Guide: Keyed v-for](https://vuejs.org/v2/style-guide/#Keyed-v-for-essential )
#### Vue and Bootstrap
1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 093a3ca4407..c1170fa3b13 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -53,13 +53,13 @@ you can find a clear separation of concerns:
```
new_feature
├── components
-│ └── component.js.es6
+│ └── component.vue
│ └── ...
-├── store
-│ └── new_feature_store.js.es6
-├── service
-│ └── new_feature_service.js.es6
-├── new_feature_bundle.js.es6
+├── stores
+│ └── new_feature_store.js
+├── services
+│ └── new_feature_service.js
+├── new_feature_bundle.js
```
_For consistency purposes, we recommend you to follow the same structure._
diff --git a/doc/user/project/merge_requests/img/allow_maintainer_push.png b/doc/user/project/merge_requests/img/allow_maintainer_push.png
index 1631527071b..91cc399f4ff 100644
--- a/doc/user/project/merge_requests/img/allow_maintainer_push.png
+++ b/doc/user/project/merge_requests/img/allow_maintainer_push.png
Binary files differ
diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb
index 1f677529b07..7f4d6e58b34 100644
--- a/lib/api/helpers/related_resources_helpers.rb
+++ b/lib/api/helpers/related_resources_helpers.rb
@@ -15,7 +15,7 @@ module API
url_options = Gitlab::Application.routes.default_url_options
protocol, host, port = url_options.slice(:protocol, :host, :port).values
- URI::HTTP.build(scheme: protocol, host: host, port: port, path: path).to_s
+ URI::Generic.build(scheme: protocol, host: host, port: port, path: path).to_s
end
private
diff --git a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
index 7cb4bccb23c..91175b49c79 100644
--- a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
+++ b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
@@ -3,7 +3,7 @@ require 'rails/generators'
module Rails
class PostDeploymentMigrationGenerator < Rails::Generators::NamedBase
def create_migration_file
- timestamp = Time.now.strftime('%Y%m%d%H%I%S')
+ timestamp = Time.now.strftime('%Y%m%d%H%M%S')
template "migration.rb", "db/post_migrate/#{timestamp}_#{file_name}.rb"
end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 11f7c8b9510..aa9fd36d9ff 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -2,6 +2,7 @@ require_dependency 'gitlab/git'
module Gitlab
COM_URL = 'https://gitlab.com'.freeze
+ APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}
def self.com?
# Check `staging?` as well to keep parity with gitlab.com
diff --git a/lib/gitlab/auth/saml/config.rb b/lib/gitlab/auth/saml/config.rb
index e654e7fe438..2760b1a3247 100644
--- a/lib/gitlab/auth/saml/config.rb
+++ b/lib/gitlab/auth/saml/config.rb
@@ -4,7 +4,7 @@ module Gitlab
class Config
class << self
def options
- Gitlab.config.omniauth.providers.find { |provider| provider.name == 'saml' }
+ Gitlab::Auth::OAuth::Provider.config_for('saml')
end
def groups
diff --git a/lib/gitlab/diff/diff_refs.rb b/lib/gitlab/diff/diff_refs.rb
index 88e0db830f6..81df47964be 100644
--- a/lib/gitlab/diff/diff_refs.rb
+++ b/lib/gitlab/diff/diff_refs.rb
@@ -44,7 +44,11 @@ module Gitlab
project.commit(head_sha)
else
straight = start_sha == base_sha
- CompareService.new(project, head_sha).execute(project, start_sha, straight: straight)
+
+ CompareService.new(project, head_sha).execute(project,
+ start_sha,
+ base_sha: base_sha,
+ straight: straight)
end
end
end
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 4f160e4a447..a61beafae0d 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -197,10 +197,7 @@ module Gitlab
end
def github_omniauth_provider
- @github_omniauth_provider ||=
- Gitlab.config.omniauth.providers
- .find { |provider| provider.name == 'github' }
- .to_h
+ @github_omniauth_provider ||= Gitlab::Auth::OAuth::Provider.config_for('github').to_h
end
def rate_limit_counter
diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb
index 075b3982608..5482504e72e 100644
--- a/lib/gitlab/gitlab_import/client.rb
+++ b/lib/gitlab/gitlab_import/client.rb
@@ -72,7 +72,7 @@ module Gitlab
end
def config
- Gitlab.config.omniauth.providers.find {|provider| provider.name == "gitlab"}
+ Gitlab::Auth::OAuth::Provider.config_for('gitlab')
end
def gitlab_options
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index 53c910d44bd..d8ed0ebca9d 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -83,7 +83,7 @@ module Gitlab
end
def config
- Gitlab.config.omniauth.providers.find { |provider| provider.name == "github" }
+ Gitlab::Auth::OAuth::Provider.config_for('github')
end
def github_options
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 757ef71b95a..1e45d074e0a 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -7,8 +7,8 @@ module Gitlab
def initialize(opts = {})
@id = opts.fetch(:id, nil)
- @filename = opts.fetch(:filename, nil)
- @basename = opts.fetch(:basename, nil)
+ @filename = encode_utf8(opts.fetch(:filename, nil))
+ @basename = encode_utf8(opts.fetch(:basename, nil))
@ref = opts.fetch(:ref, nil)
@startline = opts.fetch(:startline, nil)
@data = encode_utf8(opts.fetch(:data, nil))
diff --git a/lib/google_api/auth.rb b/lib/google_api/auth.rb
index 99a82c849e0..1aeaa387a49 100644
--- a/lib/google_api/auth.rb
+++ b/lib/google_api/auth.rb
@@ -32,7 +32,7 @@ module GoogleApi
private
def config
- Gitlab.config.omniauth.providers.find { |provider| provider.name == "google_oauth2" }
+ Gitlab::Auth::OAuth::Provider.config_for('google_oauth2')
end
def client
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3f05e878cc8..a04f869f2bb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-06 17:36+0100\n"
-"PO-Revision-Date: 2018-03-06 17:36+0100\n"
+"POT-Creation-Date: 2018-03-12 19:50+0100\n"
+"PO-Revision-Date: 2018-03-12 19:50+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -28,6 +28,11 @@ msgid_plural "%d commits behind"
msgstr[0] ""
msgstr[1] ""
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -43,6 +48,11 @@ msgid_plural "%d merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
@@ -102,6 +112,9 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -111,6 +124,9 @@ msgstr ""
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
msgid "About auto deploy"
msgstr ""
@@ -213,7 +229,7 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers"
+msgid "Allow edits from maintainers."
msgstr ""
msgid "Allows you to add and manage Kubernetes clusters."
@@ -857,6 +873,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about environments"
msgstr ""
+msgid "ClusterIntegration|Learn more about security configuration"
+msgstr ""
+
msgid "ClusterIntegration|Machine type"
msgstr ""
@@ -914,6 +933,9 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Security"
+msgstr ""
+
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
@@ -941,6 +963,9 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
+msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
@@ -1182,6 +1207,9 @@ msgstr ""
msgid "Create empty bare repository"
msgstr ""
+msgid "Create group label"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1197,6 +1225,9 @@ msgstr ""
msgid "Create new..."
msgstr ""
+msgid "Create project label"
+msgstr ""
+
msgid "CreateNewFork|Fork"
msgstr ""
@@ -1776,9 +1807,18 @@ msgstr ""
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Labels|Promote label %{labelTitle} to Group Label?"
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -1850,9 +1890,15 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Manage group labels"
+msgstr ""
+
msgid "Manage labels"
msgstr ""
+msgid "Manage project labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
@@ -1907,6 +1953,12 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -2002,6 +2054,9 @@ msgstr ""
msgid "No file chosen"
msgstr ""
+msgid "No labels created yet."
+msgstr ""
+
msgid "No repository"
msgstr ""
@@ -2251,9 +2306,15 @@ msgstr ""
msgid "Pipelines|Loading Pipelines"
msgstr ""
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
msgid "Pipelines|Run Pipeline"
msgstr ""
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
msgid "Pipelines|There are currently no %{scope} pipelines."
msgstr ""
@@ -2380,9 +2441,6 @@ msgstr ""
msgid "Project avatar in repository: %{link}"
msgstr ""
-msgid "Project cache successfully reset."
-msgstr ""
-
msgid "Project details"
msgstr ""
@@ -2446,6 +2504,12 @@ msgstr ""
msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr ""
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
msgid "PrometheusService|Active"
msgstr ""
@@ -2458,6 +2522,9 @@ msgstr ""
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
msgstr ""
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
msgid "PrometheusService|Finding and configuring metrics..."
msgstr ""
@@ -2479,15 +2546,9 @@ msgstr ""
msgid "PrometheusService|Missing environment variable"
msgstr ""
-msgid "PrometheusService|Monitored"
-msgstr ""
-
msgid "PrometheusService|More information"
msgstr ""
-msgid "PrometheusService|No metrics are being monitored. To start monitoring, deploy to an environment."
-msgstr ""
-
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr ""
@@ -2503,7 +2564,16 @@ msgstr ""
msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
msgstr ""
-msgid "PrometheusService|View environments"
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote to Group Label"
+msgstr ""
+
+msgid "Promote to Group Milestone"
msgstr ""
msgid "Protip:"
@@ -2515,9 +2585,6 @@ msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
-msgid "Push access to this project is necessary in order to enable this option"
-msgstr ""
-
msgid "Push events"
msgstr ""
@@ -3338,9 +3405,6 @@ msgstr ""
msgid "Trigger this manual action"
msgstr ""
-msgid "Unable to reset project cache."
-msgstr ""
-
msgid "Unlock"
msgstr ""
@@ -3383,12 +3447,18 @@ msgstr ""
msgid "View file @ "
msgstr ""
+msgid "View group labels"
+msgstr ""
+
msgid "View labels"
msgstr ""
msgid "View open merge request"
msgstr ""
+msgid "View project labels"
+msgstr ""
+
msgid "View replaced file @ "
msgstr ""
diff --git a/package.json b/package.json
index 6549da99f97..472bdbebda8 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
"dependencies": {
- "@gitlab-org/gitlab-svgs": "^1.13.0",
+ "@gitlab-org/gitlab-svgs": "^1.14.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.0",
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index 890774922aa..db92a3504f3 100644
--- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -125,6 +125,12 @@ describe 'Merge request > User merges when pipeline succeeds', :js do
expect(page).to have_content "canceled the automatic merge"
end
+ it 'allows to remove source branch' do
+ click_link "Remove source branch"
+
+ expect(page).to have_content "The source branch will be removed"
+ end
+
context 'when pipeline succeeds' do
before do
build.success
diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb
index 9afff47f4e9..57d843c1be2 100644
--- a/spec/helpers/import_helper_spec.rb
+++ b/spec/helpers/import_helper_spec.rb
@@ -27,25 +27,48 @@ describe ImportHelper do
describe '#provider_project_link' do
context 'when provider is "github"' do
+ let(:github_server_url) { nil }
+
+ before do
+ setting = Settingslogic.new('name' => 'github')
+ setting['url'] = github_server_url if github_server_url
+
+ allow(Gitlab.config.omniauth).to receive(:providers).and_return([setting])
+ end
+
context 'when provider does not specify a custom URL' do
it 'uses default GitHub URL' do
- allow(Gitlab.config.omniauth).to receive(:providers)
- .and_return([Settingslogic.new('name' => 'github')])
-
expect(helper.provider_project_link('github', 'octocat/Hello-World'))
.to include('href="https://github.com/octocat/Hello-World"')
end
end
context 'when provider specify a custom URL' do
+ let(:github_server_url) { 'https://github.company.com' }
+
it 'uses custom URL' do
- allow(Gitlab.config.omniauth).to receive(:providers)
- .and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
+ expect(helper.provider_project_link('github', 'octocat/Hello-World'))
+ .to include('href="https://github.company.com/octocat/Hello-World"')
+ end
+ end
+
+ context "when custom URL contains a '/' char at the end" do
+ let(:github_server_url) { 'https://github.company.com/' }
+ it "doesn't render double slash" do
expect(helper.provider_project_link('github', 'octocat/Hello-World'))
.to include('href="https://github.company.com/octocat/Hello-World"')
end
end
+
+ context 'when provider is missing' do
+ it 'uses the default URL' do
+ allow(Gitlab.config.omniauth).to receive(:providers).and_return([])
+
+ expect(helper.provider_project_link('github', 'octocat/Hello-World'))
+ .to include('href="https://github.com/octocat/Hello-World"')
+ end
+ end
end
context 'when provider is "gitea"' do
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index bf60cb12f52..5be13ed0dfe 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -1,7 +1,7 @@
/* eslint-disable */
export const notesDataMock = {
discussionsPath: '/gitlab-org/gitlab-ce/issues/26/discussions.json',
- lastFetchedAt: '1501862675',
+ lastFetchedAt: 1501862675,
markdownDocsPath: '/help/user/markdown',
newSessionPath: '/users/sign_in?redirect_to_referer=yes',
notesPath: '/gitlab-org/gitlab-ce/noteable/issue/98/notes',
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 0f092810574..91249b2c79e 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import _ from 'underscore';
+import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
import * as actions from '~/notes/stores/actions';
import store from '~/notes/stores';
import testAction from '../../helpers/vuex_action_helper';
@@ -145,4 +146,68 @@ describe('Actions Notes Store', () => {
], done);
});
});
+
+ describe('poll', () => {
+ beforeEach((done) => {
+ jasmine.clock().install();
+
+ spyOn(Vue.http, 'get').and.callThrough();
+
+ store.dispatch('setNotesData', notesDataMock)
+ .then(done)
+ .catch(done.fail);
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('calls service with last fetched state', (done) => {
+ const interceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify({
+ notes: [],
+ last_fetched_at: '123456',
+ }), {
+ status: 200,
+ headers: {
+ 'poll-interval': '1000',
+ },
+ }));
+ };
+
+ Vue.http.interceptors.push(interceptor);
+ Vue.http.interceptors.push(headersInterceptor);
+
+ store.dispatch('poll')
+ .then(() => new Promise(resolve => requestAnimationFrame(resolve)))
+ .then(() => {
+ expect(Vue.http.get).toHaveBeenCalledWith(jasmine.anything(), {
+ url: jasmine.anything(),
+ method: 'get',
+ headers: {
+ 'X-Last-Fetched-At': undefined,
+ },
+ });
+ expect(store.state.lastFetchedAt).toBe('123456');
+
+ jasmine.clock().tick(1500);
+ })
+ .then(() => new Promise((resolve) => {
+ requestAnimationFrame(resolve);
+ }))
+ .then(() => {
+ expect(Vue.http.get.calls.count()).toBe(2);
+ expect(Vue.http.get.calls.mostRecent().args[1].headers).toEqual({
+ 'X-Last-Fetched-At': '123456',
+ });
+ })
+ .then(() => store.dispatch('stopPolling'))
+ .then(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index 2627f721d9d..98f101d6bc5 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -101,10 +101,21 @@ describe('Notes Store mutations', () => {
const state = {
notes: [],
};
+ const legacyNote = {
+ id: 2,
+ individual_note: true,
+ notes: [{
+ note: '1',
+ }, {
+ note: '2',
+ }],
+ };
- mutations.SET_INITIAL_NOTES(state, [note]);
+ mutations.SET_INITIAL_NOTES(state, [note, legacyNote]);
expect(state.notes[0].id).toEqual(note.id);
- expect(state.notes.length).toEqual(1);
+ expect(state.notes[1].notes[0].note).toBe(legacyNote.notes[0].note);
+ expect(state.notes[2].notes[0].note).toBe(legacyNote.notes[1].note);
+ expect(state.notes.length).toEqual(3);
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
index dd907ad9015..d47815a5b5a 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue';
+import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import eventHub from '~/vue_merge_request_widget/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
@@ -25,12 +26,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
targetBranchPath,
targetBranch,
},
- service: {
- cancelAutomaticMerge() {},
- mergeResource: {
- save() {},
- },
- },
+ service: new MRWidgetService({}),
});
});
@@ -90,18 +86,16 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
describe('removeSourceBranch', () => {
it('should set flag and call service then request main component to update the widget', (done) => {
- spyOn(vm.service.mergeResource, 'save').and.returnValue(new Promise((resolve) => {
- resolve({
- data: {
- status: 'merge_when_pipeline_succeeds',
- },
- });
+ spyOn(vm.service, 'merge').and.returnValue(Promise.resolve({
+ data: {
+ status: 'merge_when_pipeline_succeeds',
+ },
}));
vm.removeSourceBranch();
setTimeout(() => {
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(vm.service.mergeResource.save).toHaveBeenCalledWith({
+ expect(vm.service.merge).toHaveBeenCalledWith({
sha,
merge_when_pipeline_succeeds: true,
should_remove_source_branch: true,
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
index a8a02fa6b66..2a762c9336e 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
-import nothingToMergeComponent from '~/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge';
+import NothingToMerge from '~/vue_merge_request_widget/components/states/nothing_to_merge.vue';
-describe('MRWidgetNothingToMerge', () => {
+describe('NothingToMerge', () => {
describe('template', () => {
- const Component = Vue.extend(nothingToMergeComponent);
+ const Component = Vue.extend(NothingToMerge);
const newBlobPath = '/foo';
const vm = new Component({
el: document.createElement('div'),
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 3e310980ffa..32876ca66ac 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -82,6 +82,10 @@ describe('mrWidgetOptions', () => {
});
describe('shouldRenderSourceBranchRemovalStatus', () => {
+ beforeEach(() => {
+ vm.mr.state = 'readyToMerge';
+ });
+
it('should return true when cannot remove source branch and branch will be removed', () => {
vm.mr.canRemoveSourceBranch = false;
vm.mr.shouldRemoveSourceBranch = true;
@@ -102,6 +106,22 @@ describe('mrWidgetOptions', () => {
expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
});
+
+ it('should return false when in merged state', () => {
+ vm.mr.canRemoveSourceBranch = false;
+ vm.mr.shouldRemoveSourceBranch = true;
+ vm.mr.state = 'merged';
+
+ expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
+ });
+
+ it('should return false when in nothing to merge state', () => {
+ vm.mr.canRemoveSourceBranch = false;
+ vm.mr.shouldRemoveSourceBranch = true;
+ vm.mr.state = 'nothingToMerge';
+
+ expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
+ });
});
describe('shouldRenderDeployments', () => {
@@ -407,6 +427,7 @@ describe('mrWidgetOptions', () => {
it('renders when user cannot remove branch and branch should be removed', (done) => {
vm.mr.canRemoveSourceBranch = false;
vm.mr.shouldRemoveSourceBranch = true;
+ vm.mr.state = 'readyToMerge';
vm.$nextTick(() => {
const tooltip = vm.$el.querySelector('.fa-question-circle');
@@ -419,5 +440,18 @@ describe('mrWidgetOptions', () => {
done();
});
});
+
+ it('does not render in merged state', (done) => {
+ vm.mr.canRemoveSourceBranch = false;
+ vm.mr.shouldRemoveSourceBranch = true;
+ vm.mr.state = 'merged';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).toContain('The source branch has been removed');
+ expect(vm.$el.textContent).not.toContain('Removes source branch');
+
+ done();
+ });
+ });
});
});
diff --git a/spec/lib/api/helpers/related_resources_helpers_spec.rb b/spec/lib/api/helpers/related_resources_helpers_spec.rb
new file mode 100644
index 00000000000..b918301f1cb
--- /dev/null
+++ b/spec/lib/api/helpers/related_resources_helpers_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe API::Helpers::RelatedResourcesHelpers do
+ subject(:helpers) do
+ Class.new.include(described_class).new
+ end
+
+ describe '#expose_url' do
+ let(:path) { '/api/v4/awesome_endpoint' }
+ subject(:url) { helpers.expose_url(path) }
+
+ def stub_default_url_options(protocol: 'http', host: 'example.com', port: nil)
+ expect(Gitlab::Application.routes).to receive(:default_url_options)
+ .and_return(protocol: protocol, host: host, port: port)
+ end
+
+ it 'respects the protocol if it is HTTP' do
+ stub_default_url_options(protocol: 'http')
+
+ is_expected.to start_with('http://')
+ end
+
+ it 'respects the protocol if it is HTTPS' do
+ stub_default_url_options(protocol: 'https')
+
+ is_expected.to start_with('https://')
+ end
+
+ it 'accepts port to be nil' do
+ stub_default_url_options(port: nil)
+
+ is_expected.to start_with('http://example.com/')
+ end
+
+ it 'includes port if provided' do
+ stub_default_url_options(port: 8080)
+
+ is_expected.to start_with('http://example.com:8080/')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb
index f02b1cf55fb..3d5b56cd5b8 100644
--- a/spec/lib/gitlab/profiler_spec.rb
+++ b/spec/lib/gitlab/profiler_spec.rb
@@ -94,10 +94,12 @@ describe Gitlab::Profiler do
it 'strips out the private token' do
expect(custom_logger).to receive(:add) do |severity, _progname, message|
+ next if message.include?('spec/')
+
expect(severity).to eq(Logger::DEBUG)
expect(message).to include('public').and include(described_class::FILTERED_STRING)
expect(message).not_to include(private_token)
- end
+ end.twice
custom_logger.debug("public #{private_token}")
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index c46bb8edebf..57905a74e92 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -108,14 +108,26 @@ describe Gitlab::ProjectSearchResults do
context 'when the search returns non-ASCII data' do
context 'with UTF-8' do
- let(:results) { project.repository.search_files_by_content("файл", 'master') }
+ let(:results) { project.repository.search_files_by_content('файл', 'master') }
it 'returns results as UTF-8' do
expect(subject.filename).to eq('encoding/russian.rb')
expect(subject.basename).to eq('encoding/russian')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
- expect(subject.data).to eq("Хороший файл")
+ expect(subject.data).to eq('Хороший файл')
+ end
+ end
+
+ context 'with UTF-8 in the filename' do
+ let(:results) { project.repository.search_files_by_content('webhook', 'master') }
+
+ it 'returns results as UTF-8' do
+ expect(subject.filename).to eq('encoding/テスト.txt')
+ expect(subject.basename).to eq('encoding/テスト')
+ expect(subject.ref).to eq('master')
+ expect(subject.startline).to eq(3)
+ expect(subject.data).to include('WebHookの確認')
end
end
diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb
index 04f3cecae00..8e88bb81162 100644
--- a/spec/models/compare_spec.rb
+++ b/spec/models/compare_spec.rb
@@ -37,33 +37,51 @@ describe Compare do
end
end
- describe '#base_commit' do
- let(:base_commit) { Commit.new(another_sample_commit, project) }
+ describe '#base_commit_sha' do
+ it 'returns @base_sha if it is present' do
+ expect(project).not_to receive(:merge_base_commit)
- it 'returns project merge base commit' do
- expect(project).to receive(:merge_base_commit).with(start_commit.id, head_commit.id).and_return(base_commit)
+ sha = double
+ service = described_class.new(raw_compare, project, base_sha: sha)
- expect(subject.base_commit).to eq(base_commit)
+ expect(service.base_commit_sha).to eq(sha)
+ end
+
+ it 'fetches merge base SHA from repo when @base_sha is nil' do
+ expect(project).to receive(:merge_base_commit)
+ .with(start_commit.id, head_commit.id)
+ .once
+ .and_call_original
+
+ expect(subject.base_commit_sha)
+ .to eq(project.repository.merge_base(start_commit.id, head_commit.id))
+ end
+
+ it 'is memoized on first call' do
+ expect(project).to receive(:merge_base_commit)
+ .with(start_commit.id, head_commit.id)
+ .once
+ .and_call_original
+
+ 3.times { subject.base_commit_sha }
end
it 'returns nil if there is no start_commit' do
expect(subject).to receive(:start_commit).and_return(nil)
- expect(subject.base_commit).to eq(nil)
+ expect(subject.base_commit_sha).to eq(nil)
end
it 'returns nil if there is no head commit' do
expect(subject).to receive(:head_commit).and_return(nil)
- expect(subject.base_commit).to eq(nil)
+ expect(subject.base_commit_sha).to eq(nil)
end
end
describe '#diff_refs' do
- it 'uses base_commit sha as base_sha' do
- expect(subject).to receive(:base_commit).at_least(:once).and_call_original
-
- expect(subject.diff_refs.base_sha).to eq(subject.base_commit.id)
+ it 'uses base_commit_sha sha as base_sha' do
+ expect(subject.diff_refs.base_sha).to eq(subject.base_commit_sha)
end
it 'uses start_commit sha as start_sha' do
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index c233e255fe0..33f9efc1490 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -137,7 +137,7 @@ sast:container:
dast:
stage: dast
allow_failure: true
- image: owasp/zap2docker-stable
+ image: registry.gitlab.com/gitlab-org/security-products/zaproxy
variables:
POSTGRES_DB: "false"
script:
diff --git a/yarn.lock b/yarn.lock
index a2eee3a547d..adbb37bea72 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -54,9 +54,9 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.13.0":
- version "1.13.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.13.0.tgz#9e856ef9fa7bbe49b2dce9789187a89e11311215"
+"@gitlab-org/gitlab-svgs@^1.14.0":
+ version "1.14.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.14.0.tgz#b4a5cca3106f33224c5486cf674ba3b70cee727e"
"@types/jquery@^2.0.40":
version "2.0.48"