diff options
70 files changed, 404 insertions, 411 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/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/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/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/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/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/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/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/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/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/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 |