diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2018-03-13 09:19:41 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2018-03-13 09:19:41 +0000 |
commit | 939c87233e2b825d1573f7f50ac536fe994fbc2f (patch) | |
tree | 0a7d2b2fbd51b2a8b76248dab549acb9923ef6e3 | |
parent | e84c943fa0609d05184cb95c845fa35cc1fcd432 (diff) | |
parent | 51f91537640d71262c2fa5070dfb7f8178d1decd (diff) | |
download | gitlab-ce-939c87233e2b825d1573f7f50ac536fe994fbc2f.tar.gz |
Merge branch 'master' into 44149-issue-comment-buttons
* master: (29 commits)
Fix provider server URL used when listing repos to import
Fix inconsistent punctuation on MR form
Update dependency for svgs
Fix timestamp to include %M instead of %I for post-deploy migrations.
Use Gitaly 0.89.0
Resolve "Hover style for sidebar dropdowns is wrong"
fixed spec
Respect the protocol in `expose_url`
Fix removes source branch text being rendered in merged state
Fix code and wiki search results when filename is non-ASCII
Include the ee/ directory in backtraces
Use GitLab fork of zaproxy
Updates file extensions on Vue docs
fixed note polling not sending updated last fetched at date added spec for polling
Add changelog entry
Use `list.id` for `:key`
added mutation spec
Bump parser and unparser gems to remove warnings
fix polling not working correctly
Fixed issue notes being duplicated
...
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 Binary files differindex 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 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" |