diff options
89 files changed, 560 insertions, 542 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5ab3648d9f2..733710bb005 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -163,64 +163,7 @@ spinach 7 10: *spinach-knapsack spinach 8 10: *spinach-knapsack spinach 9 10: *spinach-knapsack -# Execute all testing suites against Ruby 2.1 -.ruby-21: &ruby-21 - image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1" - <<: *use-db - only: - - master@gitlab-org/gitlab-ce - - master@gitlab-org/gitlab-ee - - master@gitlab/gitlabhq - - master@gitlab/gitlab-ee - cache: - key: "ruby21" - paths: - - vendor/ruby - -.rspec-knapsack-ruby21: &rspec-knapsack-ruby21 - <<: *rspec-knapsack - <<: *dedicated-runner - <<: *ruby-21 - -.spinach-knapsack-ruby21: &spinach-knapsack-ruby21 - <<: *spinach-knapsack - <<: *dedicated-runner - <<: *ruby-21 - -rspec 0 20 ruby21: *rspec-knapsack-ruby21 -rspec 1 20 ruby21: *rspec-knapsack-ruby21 -rspec 2 20 ruby21: *rspec-knapsack-ruby21 -rspec 3 20 ruby21: *rspec-knapsack-ruby21 -rspec 4 20 ruby21: *rspec-knapsack-ruby21 -rspec 5 20 ruby21: *rspec-knapsack-ruby21 -rspec 6 20 ruby21: *rspec-knapsack-ruby21 -rspec 7 20 ruby21: *rspec-knapsack-ruby21 -rspec 8 20 ruby21: *rspec-knapsack-ruby21 -rspec 9 20 ruby21: *rspec-knapsack-ruby21 -rspec 10 20 ruby21: *rspec-knapsack-ruby21 -rspec 11 20 ruby21: *rspec-knapsack-ruby21 -rspec 12 20 ruby21: *rspec-knapsack-ruby21 -rspec 13 20 ruby21: *rspec-knapsack-ruby21 -rspec 14 20 ruby21: *rspec-knapsack-ruby21 -rspec 15 20 ruby21: *rspec-knapsack-ruby21 -rspec 16 20 ruby21: *rspec-knapsack-ruby21 -rspec 17 20 ruby21: *rspec-knapsack-ruby21 -rspec 18 20 ruby21: *rspec-knapsack-ruby21 -rspec 19 20 ruby21: *rspec-knapsack-ruby21 - -spinach 0 10 ruby21: *spinach-knapsack-ruby21 -spinach 1 10 ruby21: *spinach-knapsack-ruby21 -spinach 2 10 ruby21: *spinach-knapsack-ruby21 -spinach 3 10 ruby21: *spinach-knapsack-ruby21 -spinach 4 10 ruby21: *spinach-knapsack-ruby21 -spinach 5 10 ruby21: *spinach-knapsack-ruby21 -spinach 6 10 ruby21: *spinach-knapsack-ruby21 -spinach 7 10 ruby21: *spinach-knapsack-ruby21 -spinach 8 10 ruby21: *spinach-knapsack-ruby21 -spinach 9 10 ruby21: *spinach-knapsack-ruby21 - # Other generic tests - .ruby-static-analysis: &ruby-static-analysis variables: SIMPLECOV: "false" diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 6d7d88c6791..34c2e097ba8 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -6,13 +6,13 @@ (How one can reproduce the issue - this is very important) -### Expected behavior +### What is the current *bug* behavior? -(What you should see instead) +(What actually happens) -### Actual behavior +### What is the expected *correct* behavior? -(What actually happens) +(What you should see instead) ### Relevant logs and/or screenshots @@ -23,23 +23,23 @@ logs, and code as it's very hard to read otherwise.) (If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com) -#### Results of GitLab application Check +#### Results of GitLab environment info (For installations with omnibus-gitlab package run and paste the output of: -`sudo gitlab-rake gitlab:check SANITIZE=true`) +`sudo gitlab-rake gitlab:env:info`) (For installations from source run and paste the output of: -`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`) - -(we will only investigate if the tests are passing) +`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) -#### Results of GitLab environment info +#### Results of GitLab application Check (For installations with omnibus-gitlab package run and paste the output of: -`sudo gitlab-rake gitlab:env:info`) +`sudo gitlab-rake gitlab:check SANITIZE=true`) (For installations from source run and paste the output of: -`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) +`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`) + +(we will only investigate if the tests are passing) ### Possible fixes diff --git a/.rubocop.yml b/.rubocop.yml index cfff42e5c99..88345373a5b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,8 +31,7 @@ AllCops: - 'lib/gitlab/seeder.rb' - 'generator_templates/**/*' - -##################### Style ################################## +# Style ####################################################################### # Check indentation of private/protected visibility modifiers. Style/AccessModifierIndentation: @@ -471,7 +470,7 @@ Style/WhileUntilModifier: Style/WordArray: Enabled: false -#################### Metrics ################################ +# Metrics ##################################################################### # A calculated magnitude based on number of assignments, # branches, and conditions. @@ -516,8 +515,7 @@ Metrics/PerceivedComplexity: Enabled: true Max: 18 - -#################### Lint ################################ +# Lint ######################################################################## # Checks for useless access modifiers. Lint/UselessAccessModifier: @@ -679,8 +677,7 @@ Lint/UselessSetterCall: Lint/Void: Enabled: true - -##################### Performance ############################ +# Performance ################################################################# # Use `casecmp` rather than `downcase ==`. Performance/Casecmp: @@ -718,8 +715,7 @@ Performance/StringReplacement: Performance/TimesMap: Enabled: true - -##################### Rails ################################## +# Rails ####################################################################### # Enables Rails cops. Rails: @@ -767,7 +763,7 @@ Rails/ReadWriteAttribute: Rails/ScopeArgs: Enabled: true -##################### RSpec ################################## +# RSpec ####################################################################### # Check that instances are not being stubbed globally. RSpec/AnyInstance: @@ -828,3 +824,9 @@ RSpec/NotToNot: # Prefer using verifying doubles over normal doubles. RSpec/VerifiedDoubles: Enabled: false + +# Custom ###################################################################### + +# Disallow the `git` and `github` arguments in the Gemfile. +GemFetcher: + Enabled: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c08c29d8a3..72cd57ad7ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ - [Issue weight](#issue-weight) - [Regression issues](#regression-issues) - [Technical debt](#technical-debt) - - [Stewardship][#stewardship] + - [Stewardship](#stewardship) - [Merge requests](#merge-requests) - [Merge request guidelines](#merge-request-guidelines) - [Contribution acceptance criteria](#contribution-acceptance-criteria) @@ -284,7 +284,7 @@ group :development, :test do gem 'rspec-retry', '~> 0.4.5' gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rerun-reporter', '~> 0.0.2' - gem 'rspec_profiling' + gem 'rspec_profiling', '~> 0.0.5' # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) gem 'minitest', '~> 5.7.0' diff --git a/Gemfile.lock b/Gemfile.lock index c5dd6ab8d22..235426afa49 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -638,7 +638,7 @@ GEM rspec-retry (0.4.5) rspec-core rspec-support (3.5.0) - rspec_profiling (0.0.4) + rspec_profiling (0.0.5) activerecord pg rails @@ -738,7 +738,7 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sqlite3 (1.3.11) + sqlite3 (1.3.13) stackprof (0.2.10) state_machines (0.4.0) state_machines-activemodel (0.4.0) @@ -962,7 +962,7 @@ DEPENDENCIES rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.5.0) rspec-retry (~> 0.4.5) - rspec_profiling + rspec_profiling (~> 0.0.5) rubocop (~> 0.46.0) rubocop-rspec (~> 1.9.1) ruby-fogbugz (~> 0.2.1) @@ -1011,4 +1011,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.14.2 + 1.14.3 @@ -1,4 +1,4 @@ -Copyright (c) 2011-2016 GitLab B.V. +Copyright (c) 2011-2017 GitLab B.V. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PROCESS.md b/PROCESS.md index f257c1d5358..fead93bd4cf 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -59,7 +59,7 @@ star, smile, etc.). Some good tips about code reviews can be found in our ## Feature Freeze -On the 7th of each month, RC1 of the upcoming release is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it. +After the 7th (Pacific Standard Time Zone) of each month, RC1 of the upcoming release is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it. Merge requests may still be merged into master during this period, but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch. By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things. diff --git a/app/assets/javascripts/copy_as_gfm.js.es6 b/app/assets/javascripts/copy_as_gfm.js.es6 index 2bfe57b4100..4bd537a6f28 100644 --- a/app/assets/javascripts/copy_as_gfm.js.es6 +++ b/app/assets/javascripts/copy_as_gfm.js.es6 @@ -91,6 +91,9 @@ require('./lib/utils/common_utils'); }, }, SanitizationFilter: { + 'a[name]:not([href]):empty'(el, text) { + return el.outerHTML; + }, 'dl'(el, text) { let lines = text.trim().split('\n'); // Add two spaces to the front of subsequent list items lines, diff --git a/app/assets/javascripts/diff.js.es6 b/app/assets/javascripts/diff.js.es6 index c39e30fb7e0..ccccd0a36ff 100644 --- a/app/assets/javascripts/diff.js.es6 +++ b/app/assets/javascripts/diff.js.es6 @@ -76,7 +76,7 @@ require('./lib/utils/url_utility'); const diffFile = diffTitle.closest('.diff-file'); const nothingHereBlock = $('.nothing-here-block:visible', diffFile); if (nothingHereBlock.length) { - const clickTarget = $('.file-title, .click-to-expand', diffFile); + const clickTarget = $('.js-file-title, .click-to-expand', diffFile); diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => { this.highlighSelectedLine(); if (cb) cb(); diff --git a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6 index 2514459e65e..d948dff58ec 100644 --- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6 +++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6 @@ -1,6 +1,6 @@ /* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */ -/* global Vue */ /* global CommentsStore */ +const Vue = require('vue'); (() => { const CommentAndResolveBtn = Vue.extend({ @@ -9,13 +9,11 @@ }, data() { return { - textareaIsEmpty: true + textareaIsEmpty: true, + discussion: {}, }; }, computed: { - discussion: function () { - return CommentsStore.state[this.discussionId]; - }, showButton: function () { if (this.discussion) { return this.discussion.isResolvable(); @@ -42,6 +40,9 @@ } } }, + created() { + this.discussion = CommentsStore.state[this.discussionId]; + }, mounted: function () { const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`); this.textareaIsEmpty = $textarea.val() === ''; diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6 b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6 index c3898873eaa..57cb0d0ae6e 100644 --- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6 +++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6 @@ -1,7 +1,7 @@ /* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, space-before-function-paren, no-lonely-if, no-continue, brace-style, max-len, quotes */ -/* global Vue */ /* global DiscussionMixins */ /* global CommentsStore */ +const Vue = require('vue'); (() => { const JumpToDiscussion = Vue.extend({ @@ -12,12 +12,10 @@ data: function () { return { discussions: CommentsStore.state, + discussion: {}, }; }, computed: { - discussion: function () { - return this.discussions[this.discussionId]; - }, allResolved: function () { return this.unresolvedDiscussionCount === 0; }, @@ -186,7 +184,10 @@ offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()) }); } - } + }, + created() { + this.discussion = this.discussions[this.discussionId]; + }, }); Vue.component('jump-to-discussion', JumpToDiscussion); diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 index 5852b8bbdb7..d1873d6c7a2 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 +++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 @@ -1,8 +1,8 @@ /* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */ -/* global Vue */ /* global CommentsStore */ /* global ResolveService */ /* global Flash */ +const Vue = require('vue'); (() => { const ResolveBtn = Vue.extend({ @@ -10,14 +10,14 @@ noteId: Number, discussionId: String, resolved: Boolean, - projectPath: String, canResolve: Boolean, resolvedBy: String }, data: function () { return { discussions: CommentsStore.state, - loading: false + loading: false, + note: {}, }; }, watch: { @@ -30,13 +30,6 @@ discussion: function () { return this.discussions[this.discussionId]; }, - note: function () { - if (this.discussion) { - return this.discussion.getNote(this.noteId); - } else { - return undefined; - } - }, buttonText: function () { if (this.isResolved) { return `Resolved by ${this.resolvedByName}`; @@ -73,10 +66,10 @@ if (this.isResolved) { promise = ResolveService - .unresolve(this.projectPath, this.noteId); + .unresolve(this.noteId); } else { promise = ResolveService - .resolve(this.projectPath, this.noteId); + .resolve(this.noteId); } promise.then((response) => { @@ -106,6 +99,8 @@ }, created: function () { CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy); + + this.note = this.discussion.getNote(this.noteId); } }); diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6 index 72cdae812bc..de9367f2136 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6 +++ b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6 @@ -1,7 +1,7 @@ /* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */ -/* global Vue */ /* global DiscussionMixins */ /* global CommentsStore */ +const Vue = require('vue'); ((w) => { w.ResolveCount = Vue.extend({ diff --git a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 index ee5f62b2d9e..7c5fcd04d2d 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 +++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 @@ -1,25 +1,22 @@ /* eslint-disable object-shorthand, func-names, space-before-function-paren, comma-dangle, no-else-return, quotes, max-len */ -/* global Vue */ /* global CommentsStore */ /* global ResolveService */ +const Vue = require('vue'); + (() => { const ResolveDiscussionBtn = Vue.extend({ props: { discussionId: String, mergeRequestId: Number, - projectPath: String, canResolve: Boolean, }, data: function() { return { - discussions: CommentsStore.state + discussion: {}, }; }, computed: { - discussion: function () { - return this.discussions[this.discussionId]; - }, showButton: function () { if (this.discussion) { return this.discussion.isResolvable(); @@ -51,11 +48,13 @@ }, methods: { resolve: function () { - ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId); + ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId); } }, created: function () { CommentsStore.createDiscussion(this.discussionId, this.canResolve); + + this.discussion = CommentsStore.state[this.discussionId]; } }); diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 index f0edfb8aaf1..190461451d5 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 @@ -3,6 +3,7 @@ /* global ResolveCount */ function requireAll(context) { return context.keys().map(context); } +const Vue = require('vue'); requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/)); requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/)); requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/)); @@ -10,11 +11,14 @@ requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/)); requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/)); $(() => { + const projectPath = document.querySelector('.merge-request').dataset.projectPath; const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn'; window.gl = window.gl || {}; window.gl.diffNoteApps = {}; + window.ResolveService = new gl.DiffNotesResolveServiceClass(projectPath); + gl.diffNotesCompileComponents = () => { const $components = $(COMPONENT_SELECTOR).filter(function () { return $(this).closest('resolve-count').length !== 1; diff --git a/app/assets/javascripts/diff_notes/services/resolve.js.es6 b/app/assets/javascripts/diff_notes/services/resolve.js.es6 index a52c476352d..090c454e9e4 100644 --- a/app/assets/javascripts/diff_notes/services/resolve.js.es6 +++ b/app/assets/javascripts/diff_notes/services/resolve.js.es6 @@ -1,45 +1,37 @@ /* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */ -/* global Vue */ /* global Flash */ /* global CommentsStore */ -((w) => { - class ResolveServiceClass { - constructor() { - this.noteResource = Vue.resource('notes{/noteId}/resolve'); - this.discussionResource = Vue.resource('merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve'); - } +const Vue = window.Vue = require('vue'); +window.Vue.use(require('vue-resource')); +require('../../vue_shared/vue_resource_interceptor'); - setCSRF() { - Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken(); - } +(() => { + window.gl = window.gl || {}; - prepareRequest(root) { - this.setCSRF(); - Vue.http.options.root = root; + class ResolveServiceClass { + constructor(root) { + this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`); + this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`); } - resolve(projectPath, noteId) { - this.prepareRequest(projectPath); - + resolve(noteId) { return this.noteResource.save({ noteId }, {}); } - unresolve(projectPath, noteId) { - this.prepareRequest(projectPath); - + unresolve(noteId) { return this.noteResource.delete({ noteId }, {}); } - toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) { + toggleResolveForDiscussion(mergeRequestId, discussionId) { const discussion = CommentsStore.state[discussionId]; const isResolved = discussion.isResolved(); let promise; if (isResolved) { - promise = this.unResolveAll(projectPath, mergeRequestId, discussionId); + promise = this.unResolveAll(mergeRequestId, discussionId); } else { - promise = this.resolveAll(projectPath, mergeRequestId, discussionId); + promise = this.resolveAll(mergeRequestId, discussionId); } promise.then((response) => { @@ -62,11 +54,9 @@ }); } - resolveAll(projectPath, mergeRequestId, discussionId) { + resolveAll(mergeRequestId, discussionId) { const discussion = CommentsStore.state[discussionId]; - this.prepareRequest(projectPath); - discussion.loading = true; return this.discussionResource.save({ @@ -75,11 +65,9 @@ }, {}); } - unResolveAll(projectPath, mergeRequestId, discussionId) { + unResolveAll(mergeRequestId, discussionId) { const discussion = CommentsStore.state[discussionId]; - this.prepareRequest(projectPath); - discussion.loading = true; return this.discussionResource.delete({ @@ -89,5 +77,5 @@ } } - w.ResolveService = new ResolveServiceClass(); -})(window); + gl.DiffNotesResolveServiceClass = ResolveServiceClass; +})(); diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 index 859d6515531..e8c2df03a46 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 @@ -4,7 +4,7 @@ class FilteredSearchDropdown { constructor(droplab, dropdown, input, filter) { this.droplab = droplab; - this.hookId = input.getAttribute('data-id'); + this.hookId = input && input.getAttribute('data-id'); this.input = input; this.filter = filter; this.dropdown = dropdown; diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6 index e5d2d706fc7..69aed77c83d 100644 --- a/app/assets/javascripts/merge_request_widget.js.es6 +++ b/app/assets/javascripts/merge_request_widget.js.es6 @@ -154,7 +154,7 @@ require('./smart_interval'); return $.getJSON(this.opts.ci_status_url, (function(_this) { return function(data) { var message, status, title; - if (data.status === '') { + if (!data.status) { return; } if (data.environments && data.environments.length) _this.renderEnvironments(data.environments); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index d108da29af7..3579843baed 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -455,7 +455,7 @@ require('vendor/task_list'); var mergeRequestId = $form.data('noteable-iid'); if (ResolveService != null) { - ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId); + ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId); } } diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 5b20c63384c..3ee0c73a8d2 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -33,13 +33,13 @@ this.$toggleIcon.addClass('fa-caret-down'); } - $('.file-title, .click-to-expand', this.file).on('click', (function (e) { + $('.js-file-title, .click-to-expand', this.file).on('click', (function (e) { this.toggleDiff($(e.target)); }).bind(this)); } SingleFileDiff.prototype.toggleDiff = function($target, cb) { - if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; + if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; this.isOpen = !this.isOpen; if (!this.isOpen && !this.hasError) { this.content.hide(); diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index d485e75a434..fb8ea18d122 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -9,6 +9,8 @@ } .user-calendar-activities { + direction: ltr; + .str-truncated { max-width: 70%; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index facfb7f9920..ff31e7f7b3d 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -125,7 +125,6 @@ top: 100%; left: 0; z-index: 9; - max-width: 280px; min-width: 240px; margin-top: 2px; margin-bottom: 0; @@ -137,6 +136,10 @@ border-radius: $border-radius-base; box-shadow: 0 2px 4px $dropdown-shadow-color; + .filtered-search-input-container & { + max-width: 280px; + } + &.is-loading { .dropdown-content { display: none; diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index c51912b4ac4..30f242a35db 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -231,3 +231,46 @@ span.idiff { } } } + +.file-title-flex-parent { + display: flex; + align-items: center; + justify-content: space-between; + background-color: $gray-light; + border-bottom: 1px solid $border-color; + padding: 5px $gl-padding; + margin: 0; + border-radius: 3px 3px 0 0; + + .file-header-content { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 30px; + position: relative; + } + + .btn-clipboard { + position: absolute; + right: 0; + } + + a { + color: $gl-text-color; + } + + small { + margin: 0 10px 0 0; + } + + .file-actions { + white-space: nowrap; + + .btn { + padding: 0 10px; + font-size: 13px; + line-height: 28px; + display: inline-block; + } + } +} diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 96ba7c40634..92d7772da57 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -34,9 +34,14 @@ } } - .file-title { + .file-title, + .file-title-flex-parent { cursor: pointer; + a:hover { + text-decoration: none; + } + &:hover { background-color: $gray-normal; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9174976c4c6..1a53730bed5 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -461,8 +461,19 @@ .issuable-list { li { + + .issue-box { + display: -webkit-flex; + display: flex; + } + + .issue-info-container { + -webkit-flex: 1; + flex: 1; + padding-right: $gl-padding; + } + .issue-check { - float: left; padding-right: $gl-padding; margin-bottom: 10px; min-width: 15px; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 1e605337f09..80b0c9493d8 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -1,6 +1,6 @@ .issues-list { .issue { - padding: 10px $gl-padding; + padding: 10px 0 10px $gl-padding; position: relative; .title { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 8541fe75e8d..692142c5887 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -272,8 +272,15 @@ .mr-list { .merge-request { - padding: 10px 15px; + padding: 10px 0 10px 15px; position: relative; + display: -webkit-flex; + display: flex; + + .issue-info-container { + -webkit-flex: 1; + flex: 1; + } .merge-request-title { margin-bottom: 2px; diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb index 9a438d5512c..2f422d352ed 100644 --- a/app/controllers/projects/protected_branches_controller.rb +++ b/app/controllers/projects/protected_branches_controller.rb @@ -68,8 +68,12 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController def access_levels_options { - push_access_levels: ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } }, - merge_access_levels: ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } } + push_access_levels: { + "Roles" => ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } }, + }, + merge_access_levels: { + "Roles" => ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } } + } } end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 80d002f9c32..eef403dba92 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -250,21 +250,11 @@ class JiraService < IssueTrackerService end end - # Build remote link on JIRA properties - # Icons here must be available on WEB so JIRA can read the URL - # We are using a open word graphics icon which have LGPL license def build_remote_link_props(url:, title:, resolved: false) status = { resolved: resolved } - if resolved - status[:icon] = { - title: 'Closed', - url16x16: 'http://www.openwebgraphics.com/resources/data/1768/16x16_apply.png' - } - end - { GlobalID: 'GitLab', object: { diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 0a954c20fcd..13d00dd1fcb 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -18,7 +18,7 @@ .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''), id: klass::file_name_noext } .file-holder#README - .file-title + .js-file-title.file-title %i.fa.fa-file = klass::file_name .pull-right diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml index b0bee1c6204..dfbc7772698 100644 --- a/app/views/ci/lints/show.html.haml +++ b/app/views/ci/lints/show.html.haml @@ -11,7 +11,7 @@ .form-group .col-sm-12 .file-holder - .file-title.clearfix + .js-file-title.file-title.clearfix Content of .gitlab-ci.yml #ci-editor.ci-editor= @content = text_area_tag(:content, @content, class: 'hidden form-control span1', rows: 7, require: true) diff --git a/app/views/discussions/_diff_with_notes.html.haml b/app/views/discussions/_diff_with_notes.html.haml index 3a95a652810..94408b92374 100644 --- a/app/views/discussions/_diff_with_notes.html.haml +++ b/app/views/discussions/_diff_with_notes.html.haml @@ -2,7 +2,7 @@ - blob = discussion.blob .diff-file.file-holder - .file-title + .js-file-title.file-title = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_file.content_commit, project: discussion.project, url: discussion_diff_path(discussion) .diff-content.code.js-syntax-highlight diff --git a/app/views/discussions/_resolve_all.html.haml b/app/views/discussions/_resolve_all.html.haml index f0b61e0f7de..e30ee1b0e05 100644 --- a/app/views/discussions/_resolve_all.html.haml +++ b/app/views/discussions/_resolve_all.html.haml @@ -1,6 +1,5 @@ - if discussion.for_merge_request? - %resolve-discussion-btn{ ":project-path" => "'#{project_path(discussion.project)}'", - ":discussion-id" => "'#{discussion.id}'", + %resolve-discussion-btn{ ":discussion-id" => "'#{discussion.id}'", ":merge-request-id" => discussion.noteable.iid, ":can-resolve" => discussion.can_resolve?(current_user), "inline-template" => true } diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index dd1df46792b..87f9b503989 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -528,7 +528,7 @@ - blob = Snippet.new(content: "Wow\nSuch\nFile") .example .file-holder - .file-title + .js-file-title.file-title Awesome file .file-actions .btn-group diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index 1c3bccccb5c..a08436715d2 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -10,6 +10,7 @@ - if @project && event.project != @project %span at %strong= link_to_project event.project + = clipboard_button(clipboard_text: event.ref_name, class: 'btn-clipboard btn-transparent', title: 'Copy branch to clipboard') #{time_ago_with_tooltip(event.created_at)} .pull-right diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 23f54553014..8a40281e28c 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -7,7 +7,7 @@ #blob-content-holder.tree-holder .file-holder - .file-title + .js-file-title.file-title = blob_icon @blob.mode, @blob.name %strong = @path diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index f75f438ee4f..19fa4c78501 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -24,7 +24,7 @@ #blob-content-holder.blob-content-holder %article.file-holder - .file-title + .js-file-title.file-title = blob_icon blob.mode, blob.name %strong = blob.name diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 228ac61fc8c..e7adef5558a 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -1,5 +1,5 @@ .file-holder.file.append-bottom-default - .file-title.clearfix + .js-file-title.file-title.clearfix .editor-ref = icon('code-fork') = ref diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 75885badac9..0232a09b4a8 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,7 +1,8 @@ - environment = local_assigns.fetch(:environment, nil) .diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) } - .file-title - = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" + .js-file-title.file-title-flex-parent + .file-header-content + = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" - unless diff_file.submodule? .file-actions.hidden-xs diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml index ddec775b789..5b09b6907ab 100644 --- a/app/views/projects/diffs/_file_header.html.haml +++ b/app/views/projects/diffs/_file_header.html.haml @@ -10,13 +10,13 @@ - if diff_file.renamed_file - old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path) - %strong + %strong.file-title-name.has-tooltip{ data: { title: old_path } } = old_path → - %strong + %strong.file-title-name.has-tooltip{ data: { title: new_path } } = new_path - else - %strong + %strong.file-title-name.has-tooltip{ data: { title: diff_file.new_path } } = diff_file.new_path - if diff_file.deleted_file deleted diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 085b2fc2814..5c9839cb330 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -1,60 +1,61 @@ %li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id } } - - if @bulk_edit - .issue-check - = check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected_issue" + .issue-box + - if @bulk_edit + .issue-check + = check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected_issue" + .issue-info-container + .issue-title.title + %span.issue-title-text + = confidential_icon(issue) + = link_to issue.title, issue_path(issue) + %ul.controls + - if issue.closed? + %li + CLOSED - .issue-title.title - %span.issue-title-text - = confidential_icon(issue) - = link_to issue.title, issue_path(issue) - %ul.controls - - if issue.closed? - %li - CLOSED + - if issue.assignee + %li + = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name") - - if issue.assignee - %li - = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name") + - upvotes, downvotes = issue.upvotes, issue.downvotes + - if upvotes > 0 + %li + = icon('thumbs-up') + = upvotes - - upvotes, downvotes = issue.upvotes, issue.downvotes - - if upvotes > 0 - %li - = icon('thumbs-up') - = upvotes + - if downvotes > 0 + %li + = icon('thumbs-down') + = downvotes - - if downvotes > 0 - %li - = icon('thumbs-down') - = downvotes + - note_count = issue.notes.user.count + %li + = link_to issue_path(issue, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do + = icon('comments') + = note_count - - note_count = issue.notes.user.count - %li - = link_to issue_path(issue, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do - = icon('comments') - = note_count + .issue-info + #{issuable_reference(issue)} · + opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} + by #{link_to_member(@project, issue.author, avatar: false)} + - if issue.milestone + + = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do + = icon('clock-o') + = issue.milestone.title + - if issue.due_date + %span{ class: "#{'cred' if issue.overdue?}" } + + = icon('calendar') + = issue.due_date.to_s(:medium) + - if issue.labels.any? + + - issue.labels.each do |label| + = link_to_label(label, subject: issue.project, css_class: 'label-link') + - if issue.tasks? + + %span.task-status + = issue.task_status - .issue-info - #{issuable_reference(issue)} · - opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} - by #{link_to_member(@project, issue.author, avatar: false)} - - if issue.milestone - - = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do - = icon('clock-o') - = issue.milestone.title - - if issue.due_date - %span{ class: "#{'cred' if issue.overdue?}" } - - = icon('calendar') - = issue.due_date.to_s(:medium) - - if issue.labels.any? - - - issue.labels.each do |label| - = link_to_label(label, subject: issue.project, css_class: 'label-link') - - if issue.tasks? - - %span.task-status - = issue.task_status - - .pull-right.issue-updated-at - %span updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')} + .pull-right.issue-updated-at + %span updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')} diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 4dbb97b3228..a5fbe9d6128 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -3,73 +3,74 @@ .issue-check = check_box_tag dom_id(merge_request, "selected"), nil, false, 'data-id' => merge_request.id, class: "selected_issue" - .merge-request-title.title - %span.merge-request-title-text - = link_to merge_request.title, merge_request_path(merge_request) - %ul.controls - - if merge_request.merged? - %li - MERGED - - elsif merge_request.closed? - %li - = icon('ban') - CLOSED + .issue-info-container + .merge-request-title.title + %span.merge-request-title-text + = link_to merge_request.title, merge_request_path(merge_request) + %ul.controls + - if merge_request.merged? + %li + MERGED + - elsif merge_request.closed? + %li + = icon('ban') + CLOSED - - if merge_request.head_pipeline - %li - = render_pipeline_status(merge_request.head_pipeline) + - if merge_request.head_pipeline + %li + = render_pipeline_status(merge_request.head_pipeline) - - if merge_request.open? && merge_request.broken? - %li - = link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do - = icon('exclamation-triangle') + - if merge_request.open? && merge_request.broken? + %li + = link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do + = icon('exclamation-triangle') - - if merge_request.assignee - %li - = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") + - if merge_request.assignee + %li + = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") - - upvotes, downvotes = merge_request.upvotes, merge_request.downvotes - - if upvotes > 0 - %li - = icon('thumbs-up') - = upvotes + - upvotes, downvotes = merge_request.upvotes, merge_request.downvotes + - if upvotes > 0 + %li + = icon('thumbs-up') + = upvotes - - if downvotes > 0 - %li - = icon('thumbs-down') - = downvotes + - if downvotes > 0 + %li + = icon('thumbs-down') + = downvotes - - note_count = merge_request.related_notes.user.count - %li - = link_to merge_request_path(merge_request, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do - = icon('comments') - = note_count + - note_count = merge_request.related_notes.user.count + %li + = link_to merge_request_path(merge_request, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do + = icon('comments') + = note_count - .merge-request-info - #{issuable_reference(merge_request)} · - opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} - by #{link_to_member(@project, merge_request.author, avatar: false)} - - if merge_request.target_project.default_branch != merge_request.target_branch - - = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do - = icon('code-fork') - = merge_request.target_branch + .merge-request-info + #{issuable_reference(merge_request)} · + opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} + by #{link_to_member(@project, merge_request.author, avatar: false)} + - if merge_request.target_project.default_branch != merge_request.target_branch + + = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do + = icon('code-fork') + = merge_request.target_branch - - if merge_request.milestone - - = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do - = icon('clock-o') - = merge_request.milestone.title + - if merge_request.milestone + + = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do + = icon('clock-o') + = merge_request.milestone.title - - if merge_request.labels.any? - - - merge_request.labels.each do |label| - = link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link') + - if merge_request.labels.any? + + - merge_request.labels.each do |label| + = link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link') - - if merge_request.tasks? - - %span.task-status - = merge_request.task_status + - if merge_request.tasks? + + %span.task-status + = merge_request.task_status - .pull-right.hidden-xs - %span updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')} + .pull-right.hidden-xs + %span updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')} diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 83250443bea..dd615d3036c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -3,10 +3,9 @@ - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('lib_vue') = page_specific_javascript_bundle_tag('diff_notes') -.merge-request{ 'data-url' => merge_request_path(@merge_request) } +.merge-request{ 'data-url' => merge_request_path(@merge_request), 'data-project-path' => project_path(@merge_request.project) } = render "projects/merge_requests/show/mr_title" .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } } diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml index dcf578b85f9..1ecd9924d88 100644 --- a/app/views/projects/merge_requests/conflicts.html.haml +++ b/app/views/projects/merge_requests/conflicts.html.haml @@ -23,7 +23,7 @@ .files-wrapper{ "v-if" => "!isLoading && !hasError" } .files .diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" } - .file-title + .js-file-title.file-title %i.fa.fa-fw{ ":class" => "file.iconClass" } %strong {{file.filePath}} = render partial: 'projects/merge_requests/conflicts/file_actions' diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 4b1da9c73e5..e58de9f0e18 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -30,8 +30,7 @@ - if note.resolvable? - can_resolve = can?(current_user, :resolve_note, note) - %resolve-btn{ "project-path" => "#{project_path(note.project)}", - "discussion-id" => "#{note.discussion_id}", + %resolve-btn{ "discussion-id" => "#{note.discussion_id}", ":note-id" => note.id, ":resolved" => note.resolved?, ":can-resolve" => can_resolve, diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 485b23815bc..6b3d7d4008b 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -4,7 +4,7 @@ .project-snippets %article.file-holder.snippet-file-content - .file-title + .js-file-title.file-title = blob_icon 0, @snippet.file_name = @snippet.file_name .file-actions diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml index a1f4e3e8ed6..bdcc160a067 100644 --- a/app/views/projects/tree/_readme.html.haml +++ b/app/views/projects/tree/_readme.html.haml @@ -1,5 +1,5 @@ %article.file-holder.readme-holder - .file-title + .js-file-title.file-title = blob_icon readme.mode, readme.name = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, @path, readme.name)) do %strong diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 9e8adc82583..7f1f807e2e7 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -1,7 +1,7 @@ - file_name, blob = blob .blob-result .file-holder - .file-title + .js-file-title.file-title - ref = @search_results.repository_ref - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(ref, file_name)) = link_to blob_link do diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index 23ca6479414..f7808ea6aff 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -14,7 +14,7 @@ - snippet_path = reliable_snippet_path(snippet) = link_to snippet_path do .file-holder - .file-title + .js-file-title.file-title %i.fa.fa-file %strong= snippet.file_name - if markup?(snippet.file_name) diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml index 648d0bd76cb..d87f9df2677 100644 --- a/app/views/search/results/_wiki_blob.html.haml +++ b/app/views/search/results/_wiki_blob.html.haml @@ -1,7 +1,7 @@ - wiki_blob = parse_search_result(wiki_blob) .blob-result .file-holder - .file-title + .js-file-title.file-title = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.basename) do %i.fa.fa-file %strong diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 56c0f7390a5..e7f7db73223 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -18,7 +18,7 @@ = f.label :file_name, "File", class: 'control-label' .col-sm-10 .file-holder.snippet - .file-title + .js-file-title.file-title = f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name' .file-content.code %pre#editor= @snippet.content diff --git a/app/views/sherlock/file_samples/show.html.haml b/app/views/sherlock/file_samples/show.html.haml index 92151176fce..1a6e2542dc1 100644 --- a/app/views/sherlock/file_samples/show.html.haml +++ b/app/views/sherlock/file_samples/show.html.haml @@ -26,7 +26,7 @@ = @file_sample.events %article.file-holder - .file-title + .js-file-title.file-title %i.fa.fa-file-text-o.fa-fw %strong = @file_sample.file diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 837a1a0cc8c..970afbe6b64 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -3,7 +3,7 @@ = render 'shared/snippets/header' %article.file-holder.snippet-file-content - .file-title + .js-file-title.file-title = blob_icon 0, @snippet.file_name = @snippet.file_name .file-actions diff --git a/changelogs/unreleased/25709-diff-file-overflow.yml b/changelogs/unreleased/25709-diff-file-overflow.yml new file mode 100644 index 00000000000..7d1b2b36ab8 --- /dev/null +++ b/changelogs/unreleased/25709-diff-file-overflow.yml @@ -0,0 +1,4 @@ +--- +title: Responsive title in diffs inline, side by side, with and without sidebar +merge_request: 8475 +author: diff --git a/changelogs/unreleased/27610-issue-number-alignment.yml b/changelogs/unreleased/27610-issue-number-alignment.yml new file mode 100644 index 00000000000..19ab8872c62 --- /dev/null +++ b/changelogs/unreleased/27610-issue-number-alignment.yml @@ -0,0 +1,4 @@ +--- +title: fixes issue number alignment problem in MR and issue list +merge_request: 9020 +author: diff --git a/changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml b/changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml new file mode 100644 index 00000000000..6c98b46d8cb --- /dev/null +++ b/changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml @@ -0,0 +1,4 @@ +--- +title: Fixes dropdown width in admin project page +merge_request: 9002 +author: diff --git a/changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml b/changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml new file mode 100644 index 00000000000..fcbd48b0357 --- /dev/null +++ b/changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml @@ -0,0 +1,4 @@ +--- +title: Fix contribution activity alignment +merge_request: +author: diff --git a/changelogs/unreleased/copy-branch-to-clipboard.yml b/changelogs/unreleased/copy-branch-to-clipboard.yml new file mode 100644 index 00000000000..c12e324ed3c --- /dev/null +++ b/changelogs/unreleased/copy-branch-to-clipboard.yml @@ -0,0 +1,4 @@ +--- +title: Added the ability to copy a branch name to the clipboard +merge_request: 9103 +author: Glenn Sayers diff --git a/changelogs/unreleased/fixes-namespace-api-documentation.yml b/changelogs/unreleased/fixes-namespace-api-documentation.yml new file mode 100644 index 00000000000..6b578bb1602 --- /dev/null +++ b/changelogs/unreleased/fixes-namespace-api-documentation.yml @@ -0,0 +1,4 @@ +--- +title: Update API docs for new namespace format +merge_request: 9073 +author: Markus Koller diff --git a/changelogs/unreleased/issue_26701.yml b/changelogs/unreleased/issue_26701.yml new file mode 100644 index 00000000000..6834351bf43 --- /dev/null +++ b/changelogs/unreleased/issue_26701.yml @@ -0,0 +1,4 @@ +--- +title: Remove JIRA closed status icon +merge_request: +author: diff --git a/changelogs/unreleased/protected-branch-dropdown-titles.yml b/changelogs/unreleased/protected-branch-dropdown-titles.yml new file mode 100644 index 00000000000..df82cc00fc9 --- /dev/null +++ b/changelogs/unreleased/protected-branch-dropdown-titles.yml @@ -0,0 +1,4 @@ +--- +title: Added headers to protected branch access dropdowns +merge_request: +author: diff --git a/changelogs/unreleased/upgrade-babel-v6.yml b/changelogs/unreleased/upgrade-babel-v6.yml new file mode 100644 index 00000000000..55f9b3e407c --- /dev/null +++ b/changelogs/unreleased/upgrade-babel-v6.yml @@ -0,0 +1,4 @@ +--- +title: upgrade babel 5.8.x to babel 6.22.x +merge_request: 9072 +author: diff --git a/changelogs/unreleased/upgrade-webpack-v2-2.yml b/changelogs/unreleased/upgrade-webpack-v2-2.yml new file mode 100644 index 00000000000..6a49859d68c --- /dev/null +++ b/changelogs/unreleased/upgrade-webpack-v2-2.yml @@ -0,0 +1,4 @@ +--- +title: upgrade to webpack v2.2 +merge_request: 9078 +author: diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index aabe859730a..7336d7c842a 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -302,3 +302,21 @@ :why: https://github.com/dchest/tweetnacl-js/blob/master/LICENSE :versions: [] :when: 2017-01-14 20:10:57.812077000 Z +- - :approve + - wordwrap + - :who: Mike Greiling + :why: https://github.com/substack/node-wordwrap/blob/0.0.3/LICENSE + :versions: [] + :when: 2017-02-08 20:17:13.084968000 Z +- - :approve + - spdx-expression-parse + - :who: Mike Greiling + :why: https://github.com/kemitchell/spdx-expression-parse.js/blob/v1.0.4/LICENSE + :versions: [] + :when: 2017-02-08 22:33:01.806977000 Z +- - :approve + - spdx-license-ids + - :who: Mike Greiling + :why: https://github.com/shinnn/spdx-license-ids/blob/v1.2.2/LICENSE + :versions: [] + :when: 2017-02-08 22:35:00.225232000 Z diff --git a/config/webpack.config.js b/config/webpack.config.js index 968c0076eaf..00f448c1fbb 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -48,26 +48,23 @@ var config = { devtool: 'inline-source-map', module: { - loaders: [ + rules: [ { test: /\.(js|es6)$/, exclude: /(node_modules|vendor\/assets)/, loader: 'babel-loader', - query: { - // 'use strict' was broken in sprockets-es6 due to sprockets concatination method. - // many es5 strict errors which were never caught ended up in our es6 assets as a result. - // this hack is necessary until they can be fixed. - blacklist: ['useStrict'] + options: { + presets: [ + ["es2015", {"modules": false}], + 'stage-2' + ] } }, { test: /\.(js|es6)$/, + exclude: /node_modules/, loader: 'imports-loader', - query: 'this=>window' - }, - { - test: /\.json$/, - loader: 'json-loader' + options: 'this=>window' } ] }, @@ -88,7 +85,7 @@ var config = { ], resolve: { - extensions: ['', '.js', '.es6', '.js.es6'], + extensions: ['.js', '.es6', '.js.es6'], alias: { '~': path.join(ROOT_PATH, 'app/assets/javascripts'), 'bootstrap/js': 'bootstrap-sass/assets/javascripts/bootstrap', @@ -104,14 +101,16 @@ if (IS_PRODUCTION) { config.devtool = 'source-map'; config.plugins.push( new webpack.NoErrorsPlugin(), + new webpack.LoaderOptionsPlugin({ + minimize: true, + debug: false + }), new webpack.optimize.UglifyJsPlugin({ - compress: { warnings: false } + sourceMap: true }), new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } - }), - new webpack.optimize.DedupePlugin(), - new webpack.optimize.OccurrenceOrderPlugin() + }) ); } diff --git a/doc/api/groups.md b/doc/api/groups.md index 84ab01c292b..a3a43ca7f1c 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -106,15 +106,7 @@ Example response: "id": 5, "name": "Experimental", "path": "h5bp", - "owner_id": null, - "created_at": "2016-04-05T21:40:49.152Z", - "updated_at": "2016-04-07T08:07:48.466Z", - "description": "foo", - "avatar": { - "url": null - }, - "share_with_group_lock": false, - "visibility_level": 10 + "kind": "group" }, "avatar_url": null, "star_count": 1, @@ -190,15 +182,7 @@ Example response: "id": 4, "name": "Twitter", "path": "twitter", - "owner_id": null, - "created_at": "2016-06-17T07:47:24.216Z", - "updated_at": "2016-06-17T07:47:24.216Z", - "description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.", - "avatar": { - "url": null - }, - "share_with_group_lock": false, - "visibility_level": 20 + "kind": "group" }, "avatar_url": null, "star_count": 0, @@ -237,15 +221,7 @@ Example response: "id": 4, "name": "Twitter", "path": "twitter", - "owner_id": null, - "created_at": "2016-06-17T07:47:24.216Z", - "updated_at": "2016-06-17T07:47:24.216Z", - "description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.", - "avatar": { - "url": null - }, - "share_with_group_lock": false, - "visibility_level": 20 + "kind": "group" }, "avatar_url": null, "star_count": 0, @@ -286,15 +262,7 @@ Example response: "id": 5, "name": "H5bp", "path": "h5bp", - "owner_id": null, - "created_at": "2016-06-17T07:47:26.621Z", - "updated_at": "2016-06-17T07:47:26.621Z", - "description": "Id consequatur rem vel qui doloremque saepe.", - "avatar": { - "url": null - }, - "share_with_group_lock": false, - "visibility_level": 20 + "kind": "group" }, "avatar_url": null, "star_count": 0, @@ -416,15 +384,7 @@ Example response: "id": 5, "name": "Experimental", "path": "h5bp", - "owner_id": null, - "created_at": "2016-04-05T21:40:49.152Z", - "updated_at": "2016-04-07T08:07:48.466Z", - "description": "foo", - "avatar": { - "url": null - }, - "share_with_group_lock": false, - "visibility_level": 10 + "kind": "group" }, "avatar_url": null, "star_count": 1, diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index 5ef5e3f5744..eab532af594 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -57,7 +57,7 @@ Once you have the authorization code you can request an `access_token` using the ``` parameters = 'client_id=APP_ID&client_secret=APP_SECRET&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI' -RestClient.post 'http://localhost:3000/oauth/token', parameters +RestClient.post 'http://gitlab.example.com/oauth/token', parameters # The response will be { @@ -77,13 +77,13 @@ You can now make requests to the API with the access token returned. The access token allows you to make requests to the API on a behalf of a user. ``` -GET https://localhost:3000/api/v3/user?access_token=OAUTH-TOKEN +GET https://gitlab.example.com/api/v3/user?access_token=OAUTH-TOKEN ``` Or you can put the token to the Authorization header: ``` -curl --header "Authorization: Bearer OAUTH-TOKEN" https://localhost:3000/api/v3/user +curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v3/user ``` ## Resource Owner Password Credentials diff --git a/doc/api/projects.md b/doc/api/projects.md index 040153ac880..bad238f57d7 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -72,13 +72,10 @@ Parameters: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "archived": false, "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", @@ -125,13 +122,10 @@ Parameters: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 4, "name": "Brightbox", - "owner_id": 1, "path": "brightbox", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "permissions": { "project_access": { @@ -210,13 +204,10 @@ Parameters: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "archived": false, "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", @@ -260,13 +251,10 @@ Parameters: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 4, "name": "Brightbox", - "owner_id": 1, "path": "brightbox", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "permissions": { "project_access": { @@ -398,13 +386,10 @@ Parameters: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "permissions": { "project_access": { @@ -779,13 +764,10 @@ Example response: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "archived": true, "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", @@ -847,13 +829,10 @@ Example response: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "archived": true, "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", @@ -921,13 +900,10 @@ Example response: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "permissions": { "project_access": { @@ -1006,13 +982,10 @@ Example response: "last_activity_at": "2013-09-30T13:46:02Z", "creator_id": 3, "namespace": { - "created_at": "2013-09-30T13:46:02Z", - "description": "", "id": 3, "name": "Diaspora", - "owner_id": 1, "path": "diaspora", - "updated_at": "2013-09-30T13:46:02Z" + "kind": "group" }, "permissions": { "project_access": { diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md index 3b075ff5fc0..2a531193adf 100644 --- a/doc/gitlab-basics/command-line-commands.md +++ b/doc/gitlab-basics/command-line-commands.md @@ -25,6 +25,8 @@ git clone PASTE HTTPS OR SSH HERE A clone of the project will be created in your computer. +>**Note:** If you clone your project via an URL that contains special characters, make sure that they are URL-encoded. + ### Go into a project, directory or file to work in it ``` diff --git a/doc/gitlab-basics/img/profile_settings.png b/doc/gitlab-basics/img/profile_settings.png Binary files differindex 26df4c0a734..aaa1a39313d 100644 --- a/doc/gitlab-basics/img/profile_settings.png +++ b/doc/gitlab-basics/img/profile_settings.png diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png Binary files differindex 6a1430d9663..7ebb8973ef0 100644 --- a/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png +++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png diff --git a/features/snippets/user.feature b/features/snippets/user.feature deleted file mode 100644 index 5b5dadb7b39..00000000000 --- a/features/snippets/user.feature +++ /dev/null @@ -1,34 +0,0 @@ -@snippets -Feature: Snippets User - Background: - Given I sign in as a user - And I have public "Personal snippet one" snippet - And I have private "Personal snippet private" snippet - And I have internal "Personal snippet internal" snippet - - Scenario: I should see all my snippets - Given I visit my snippets page - Then I should see "Personal snippet one" in snippets - And I should see "Personal snippet private" in snippets - And I should see "Personal snippet internal" in snippets - - Scenario: I can see only my private snippets - Given I visit my snippets page - And I click "Private" filter - Then I should not see "Personal snippet one" in snippets - And I should not see "Personal snippet internal" in snippets - And I should see "Personal snippet private" in snippets - - Scenario: I can see only my public snippets - Given I visit my snippets page - And I click "Public" filter - Then I should see "Personal snippet one" in snippets - And I should not see "Personal snippet private" in snippets - And I should not see "Personal snippet internal" in snippets - - Scenario: I can see only my internal snippets - Given I visit my snippets page - And I click "Internal" filter - Then I should see "Personal snippet internal" in snippets - And I should not see "Personal snippet private" in snippets - And I should not see "Personal snippet one" in snippets diff --git a/features/steps/snippets/user.rb b/features/steps/snippets/user.rb deleted file mode 100644 index 997c605bce2..00000000000 --- a/features/steps/snippets/user.rb +++ /dev/null @@ -1,55 +0,0 @@ -class Spinach::Features::SnippetsUser < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedSnippet - - step 'I visit my snippets page' do - visit dashboard_snippets_path - end - - step 'I should see "Personal snippet one" in snippets' do - expect(page).to have_content "Personal snippet one" - end - - step 'I should see "Personal snippet private" in snippets' do - expect(page).to have_content "Personal snippet private" - end - - step 'I should see "Personal snippet internal" in snippets' do - expect(page).to have_content "Personal snippet internal" - end - - step 'I should not see "Personal snippet one" in snippets' do - expect(page).not_to have_content "Personal snippet one" - end - - step 'I should not see "Personal snippet private" in snippets' do - expect(page).not_to have_content "Personal snippet private" - end - - step 'I should not see "Personal snippet internal" in snippets' do - expect(page).not_to have_content "Personal snippet internal" - end - - step 'I click "Internal" filter' do - page.within('.snippet-scope-menu') do - click_link "Internal" - end - end - - step 'I click "Private" filter' do - page.within('.snippet-scope-menu') do - click_link "Private" - end - end - - step 'I click "Public" filter' do - page.within('.snippet-scope-menu') do - click_link "Public" - end - end - - def snippet - @snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one") - end -end diff --git a/lib/gitlab/chat_commands/presenters/issuable.rb b/lib/gitlab/chat_commands/presenters/issue_base.rb index dfb1c8f6616..a0058407fb2 100644 --- a/lib/gitlab/chat_commands/presenters/issuable.rb +++ b/lib/gitlab/chat_commands/presenters/issue_base.rb @@ -1,7 +1,7 @@ module Gitlab module ChatCommands module Presenters - module Issuable + module IssueBase def color(issuable) issuable.open? ? '#38ae67' : '#d22852' end diff --git a/lib/gitlab/chat_commands/presenters/issue_new.rb b/lib/gitlab/chat_commands/presenters/issue_new.rb index a1a3add56c9..0d31660039a 100644 --- a/lib/gitlab/chat_commands/presenters/issue_new.rb +++ b/lib/gitlab/chat_commands/presenters/issue_new.rb @@ -2,7 +2,7 @@ module Gitlab module ChatCommands module Presenters class IssueNew < Presenters::Base - include Presenters::Issuable + include Presenters::IssueBase def present in_channel_response(new_issue) diff --git a/lib/gitlab/chat_commands/presenters/issue_search.rb b/lib/gitlab/chat_commands/presenters/issue_search.rb index 3478359b91d..73788cf9662 100644 --- a/lib/gitlab/chat_commands/presenters/issue_search.rb +++ b/lib/gitlab/chat_commands/presenters/issue_search.rb @@ -2,7 +2,7 @@ module Gitlab module ChatCommands module Presenters class IssueSearch < Presenters::Base - include Presenters::Issuable + include Presenters::IssueBase def present text = if @resource.count >= 5 diff --git a/lib/gitlab/chat_commands/presenters/issue_show.rb b/lib/gitlab/chat_commands/presenters/issue_show.rb index fe5847ccd15..bd784ad241e 100644 --- a/lib/gitlab/chat_commands/presenters/issue_show.rb +++ b/lib/gitlab/chat_commands/presenters/issue_show.rb @@ -2,7 +2,7 @@ module Gitlab module ChatCommands module Presenters class IssueShow < Presenters::Base - include Presenters::Issuable + include Presenters::IssueBase def present if @resource.confidential? diff --git a/package.json b/package.json index a25e09e4cf2..249c69f586a 100644 --- a/package.json +++ b/package.json @@ -11,28 +11,27 @@ "webpack-prod": "NODE_ENV=production npm run webpack" }, "dependencies": { - "babel": "^5.8.38", - "babel-core": "^5.8.38", - "babel-loader": "^5.4.2", + "babel-core": "^6.22.1", + "babel-loader": "^6.2.10", + "babel-preset-es2015": "^6.22.0", + "babel-preset-stage-2": "^6.22.0", "bootstrap-sass": "3.3.6", "compression-webpack-plugin": "^0.3.2", "d3": "3.5.11", "dropzone": "4.2.0", - "exports-loader": "^0.6.3", "imports-loader": "^0.6.5", "jquery": "2.2.1", "jquery-ui": "github:jquery/jquery-ui#1.11.4", "jquery-ujs": "1.2.1", - "json-loader": "^0.5.4", "mousetrap": "1.4.6", "pikaday": "^1.5.1", "select2": "3.5.2-browserify", - "stats-webpack-plugin": "^0.4.2", + "stats-webpack-plugin": "^0.4.3", "underscore": "1.8.3", "vue": "2.0.3", "vue-resource": "0.9.3", - "webpack": "^1.14.0", - "webpack-dev-server": "^1.16.2" + "webpack": "^2.2.1", + "webpack-dev-server": "^2.3.0" }, "devDependencies": { "eslint": "^3.10.1", @@ -43,10 +42,10 @@ "istanbul": "^0.4.5", "jasmine-core": "^2.5.2", "jasmine-jquery": "^2.1.1", - "karma": "^1.3.0", + "karma": "^1.4.1", "karma-jasmine": "^1.1.0", "karma-phantomjs-launcher": "^1.0.2", "karma-sourcemap-loader": "^0.3.7", - "karma-webpack": "^1.8.0" + "karma-webpack": "^2.0.2" } } diff --git a/rubocop/cop/gem_fetcher.rb b/rubocop/cop/gem_fetcher.rb index 4a63c760744..c199f6acab2 100644 --- a/rubocop/cop/gem_fetcher.rb +++ b/rubocop/cop/gem_fetcher.rb @@ -1,17 +1,15 @@ module RuboCop module Cop - # Cop that checks for all gems specified in the Gemfile, and will - # alert if any gem is to be fetched not from the RubyGems index. - # This enforcement is done so as to minimize external build - # dependencies and build times. + # This cop prevents usage of the `git` and `github` arguments to `gem` in a + # `Gemfile` in order to avoid additional points of failure beyond + # rubygems.org. class GemFetcher < RuboCop::Cop::Cop MSG = 'Do not use gems from git repositories, only use gems from RubyGems.' GIT_KEYS = [:git, :github] def on_send(node) - file_path = node.location.expression.source_buffer.name - return unless file_path.end_with?("Gemfile") + return unless gemfile?(node) func_name = node.children[1] return unless func_name == :gem @@ -19,10 +17,21 @@ module RuboCop node.children.last.each_node(:pair) do |pair| key_name = pair.children[0].children[0].to_sym if GIT_KEYS.include?(key_name) - add_offense(node, :selector) + add_offense(node, pair.source_range, MSG) end end end + + private + + def gemfile?(node) + node + .location + .expression + .source_buffer + .name + .end_with?("Gemfile") + end end end end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 3e292a4527c..aa35fb1701c 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -1,4 +1,4 @@ -require_relative 'cop/migration/add_index' +require_relative 'cop/gem_fetcher' require_relative 'cop/migration/add_column' require_relative 'cop/migration/add_column_with_default' -require_relative 'cop/gem_fetcher' +require_relative 'cop/migration/add_index' diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb index f3a5b565122..fec86128d03 100644 --- a/spec/features/copy_as_gfm_spec.rb +++ b/spec/features/copy_as_gfm_spec.rb @@ -251,6 +251,8 @@ describe 'Copy as GFM', feature: true, js: true do 'SanitizationFilter', <<-GFM.strip_heredoc + <a name="named-anchor"></a> + <sub>sub</sub> <dl> diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 8b3e2fa93a2..8c64b050e19 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -72,8 +72,8 @@ feature 'Expand and collapse diffs', js: true, feature: true do it 'collapses large diffs for renamed files by default' do expect(large_diff_renamed).not_to have_selector('.code') expect(large_diff_renamed).to have_selector('.nothing-here-block') - expect(large_diff_renamed).to have_selector('.file-title .deletion') - expect(large_diff_renamed).to have_selector('.file-title .addition') + expect(large_diff_renamed).to have_selector('.js-file-title .deletion') + expect(large_diff_renamed).to have_selector('.js-file-title .addition') end it 'shows non-renderable diffs as such immediately, regardless of their size' do @@ -115,9 +115,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do context 'expanding a large diff' do before do # Wait for diffs - find('.file-title', match: :first) + find('.js-file-title', match: :first) # Click `large_diff.md` title - all('.file-title')[1].click + all('.diff-toggle-caret')[1].click wait_for_ajax end @@ -159,9 +159,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do context 'expanding the diff' do before do # Wait for diffs - find('.file-title', match: :first) + find('.js-file-title', match: :first) # Click `large_diff.md` title - all('.file-title')[1].click + all('.diff-toggle-caret')[1].click wait_for_ajax end @@ -181,9 +181,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do context 'collapsing an expanded diff' do before do # Wait for diffs - find('.file-title', match: :first) + find('.js-file-title', match: :first) # Click `small_diff.md` title - all('.file-title')[3].click + all('.diff-toggle-caret')[3].click end it 'hides the diff content' do @@ -194,9 +194,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do context 're-expanding the same diff' do before do # Wait for diffs - find('.file-title', match: :first) + find('.js-file-title', match: :first) # Click `small_diff.md` title - all('.file-title')[3].click + all('.diff-toggle-caret')[3].click end it 'shows the diff content' do @@ -290,9 +290,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do context 'collapsing an expanded diff' do before do # Wait for diffs - find('.file-title', match: :first) + find('.js-file-title', match: :first) # Click `small_diff.md` title - all('.file-title')[3].click + all('.diff-toggle-caret')[3].click end it 'hides the diff content' do @@ -303,9 +303,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do context 're-expanding the same diff' do before do # Wait for diffs - find('.file-title', match: :first) + find('.js-file-title', match: :first) # Click `small_diff.md` title - all('.file-title')[3].click + all('.diff-toggle-caret')[3].click end it 'shows the diff content' do diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb index fc88fd74af8..582349d8d5b 100644 --- a/spec/features/projects/files/find_file_keyboard_spec.rb +++ b/spec/features/projects/files/find_file_keyboard_spec.rb @@ -22,7 +22,7 @@ feature 'Find file keyboard shortcuts', feature: true, js: true do expect(page).to have_selector('.blob-content-holder') - page.within('.file-title') do + page.within('.js-file-title') do expect(page).to have_content('CHANGELOG') end end @@ -35,7 +35,7 @@ feature 'Find file keyboard shortcuts', feature: true, js: true do expect(page).to have_selector('.blob-content-holder') - page.within('.file-title') do + page.within('.js-file-title') do expect(page).to have_content('application.js') end end diff --git a/spec/features/protected_branches/access_control_ce_spec.rb b/spec/features/protected_branches/access_control_ce_spec.rb index 395c61a4743..e4aca25a339 100644 --- a/spec/features/protected_branches/access_control_ce_spec.rb +++ b/spec/features/protected_branches/access_control_ce_spec.rb @@ -26,7 +26,11 @@ RSpec.shared_examples "protected branches > access control > CE" do within(".protected-branches-list") do find(".js-allowed-to-push").click - within('.js-allowed-to-push-container') { click_on access_type_name } + + within('.js-allowed-to-push-container') do + expect(first("li")).to have_content("Roles") + click_on access_type_name + end end wait_for_ajax @@ -61,7 +65,11 @@ RSpec.shared_examples "protected branches > access control > CE" do within(".protected-branches-list") do find(".js-allowed-to-merge").click - within('.js-allowed-to-merge-container') { click_on access_type_name } + + within('.js-allowed-to-merge-container') do + expect(first("li")).to have_content("Roles") + click_on access_type_name + end end wait_for_ajax diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb new file mode 100644 index 00000000000..191c2fb9a22 --- /dev/null +++ b/spec/features/snippets/user_snippets_spec.rb @@ -0,0 +1,49 @@ +require 'rails_helper' + +feature 'User Snippets', feature: true do + let(:author) { create(:user) } + let!(:public_snippet) { create(:personal_snippet, :public, author: author, title: "This is a public snippet") } + let!(:internal_snippet) { create(:personal_snippet, :internal, author: author, title: "This is an internal snippet") } + let!(:private_snippet) { create(:personal_snippet, :private, author: author, title: "This is a private snippet") } + + background do + login_as author + visit dashboard_snippets_path + end + + scenario 'View all of my snippets' do + expect(page).to have_content(public_snippet.title) + expect(page).to have_content(internal_snippet.title) + expect(page).to have_content(private_snippet.title) + end + + scenario 'View my public snippets' do + page.within('.snippet-scope-menu') do + click_link "Public" + end + + expect(page).to have_content(public_snippet.title) + expect(page).not_to have_content(internal_snippet.title) + expect(page).not_to have_content(private_snippet.title) + end + + scenario 'View my internal snippets' do + page.within('.snippet-scope-menu') do + click_link "Internal" + end + + expect(page).not_to have_content(public_snippet.title) + expect(page).to have_content(internal_snippet.title) + expect(page).not_to have_content(private_snippet.title) + end + + scenario 'View my private snippets' do + page.within('.snippet-scope-menu') do + click_link "Private" + end + + expect(page).not_to have_content(public_snippet.title) + expect(page).not_to have_content(internal_snippet.title) + expect(page).to have_content(private_snippet.title) + end +end diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js.es6 b/spec/javascripts/filtered_search/dropdown_user_spec.js.es6 index f4b0d60db34..fa9d03c8a9a 100644 --- a/spec/javascripts/filtered_search/dropdown_user_spec.js.es6 +++ b/spec/javascripts/filtered_search/dropdown_user_spec.js.es6 @@ -9,7 +9,7 @@ require('~/filtered_search/dropdown_user'); let dropdownUser; beforeEach(() => { - spyOn(gl.FilteredSearchDropdown.prototype, 'constructor').and.callFake(() => {}); + spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {}); @@ -39,7 +39,7 @@ require('~/filtered_search/dropdown_user'); describe('config droplabAjaxFilter\'s endpoint', () => { beforeEach(() => { - spyOn(gl.FilteredSearchDropdown.prototype, 'constructor').and.callFake(() => {}); + spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); }); diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 2f6b159d76e..4bca0229e7a 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -135,7 +135,7 @@ describe JiraService, models: true do url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{merge_request.diff_head_sha}", title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.", icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, - status: { resolved: true, icon: { url16x16: "http://www.openwebgraphics.com/resources/data/1768/16x16_apply.png", title: "Closed" } } + status: { resolved: true } } ) ).once diff --git a/spec/rubocop/cop/gem_fetcher_spec.rb b/spec/rubocop/cop/gem_fetcher_spec.rb new file mode 100644 index 00000000000..c07f6a831dc --- /dev/null +++ b/spec/rubocop/cop/gem_fetcher_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../rubocop/cop/gem_fetcher' + +describe RuboCop::Cop::GemFetcher do + include CopHelper + + subject(:cop) { described_class.new } + + context 'in Gemfile' do + before do + allow(cop).to receive(:gemfile?).and_return(true) + end + + it 'registers an offense when a gem uses `git`' do + inspect_source(cop, 'gem "foo", git: "https://gitlab.com/foo/bar.git"') + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['git: "https://gitlab.com/foo/bar.git"']) + end + end + + it 'registers an offense when a gem uses `github`' do + inspect_source(cop, 'gem "foo", github: "foo/bar.git"') + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['github: "foo/bar.git"']) + end + end + end + + context 'outside of Gemfile' do + it 'registers no offense' do + inspect_source(cop, 'gem "foo", git: "https://gitlab.com/foo/bar.git"') + + expect(cop.offenses.size).to eq(0) + end + end +end |