From e35a56c5ae807af87736bd9a8bc64c3369194056 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 18 Jul 2017 14:18:11 +0100 Subject: [PATCH 001/108] Fly-out dropdown menu in new sidebar Closes #34026 [ci skip] --- app/assets/javascripts/main.js | 17 +++++++ app/assets/stylesheets/new_sidebar.scss | 45 ++++++++++++++++++- .../nav/_new_project_sidebar.html.haml | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 26c67fb721c..9364d19def1 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -347,4 +347,21 @@ $(function () { gl.utils.renderTimeago(); $(document).trigger('init.scrolling-tabs'); + + $('.sidebar-top-level-items > li:not(.active)').on('mouseover', (e) => { + const windowHeight = window.innerHeight; + const $this = e.currentTarget; + const $subitems = $('.sidebar-sub-level-items', $this).show(); + + if ($subitems.length) { + const boundingRect = $this.getBoundingClientRect(); + + const bottomOverflow = windowHeight - (boundingRect.top + $subitems.outerHeight()); + const top = bottomOverflow < 0 ? boundingRect.top - Math.abs(bottomOverflow) : boundingRect.top; + + $subitems.css({ + top, + }); + } + }).on('mouseout', e => $('.sidebar-sub-level-items', e.currentTarget).hide()); }); diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index bd9a5d7392d..527d47d5353 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -98,6 +98,7 @@ $new-sidebar-width: 220px; } li { + position: relative; white-space: nowrap; a { @@ -126,8 +127,21 @@ $new-sidebar-width: 220px; padding-bottom: 8px; > li { + &:not(.active) { + @media (min-width: $screen-sm-min) { + a { + padding: 11px 16px 11px 24px; + + &:hover, + &:focus { + background: transparent; + font-weight: 600; + } + } + } + } + a { - font-size: 12px; padding: 8px 16px 8px 24px; &:hover, @@ -152,6 +166,34 @@ $new-sidebar-width: 220px; .sidebar-top-level-items { > li { + &:not(.active) { + .sidebar-sub-level-items { + @media (min-width: $screen-sm-min) { + position: fixed; + left: 220px; + width: 150px; + margin-left: -1px; + padding-bottom: 0; + background-color: #fff; + box-shadow: 2px 1px 3px rgba(0,0,0,.1); + border: 1px solid #e5e5e5; + border-left: 0; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 0; + height: 0; + border-style: solid; + border-width: 21px 12px; + border-color: transparent transparent transparent $hover-background; + } + } + } + } + .badge { float: right; background-color: $inactive-badge-background; @@ -171,6 +213,7 @@ $new-sidebar-width: 220px; } } + &:not(.active):hover > a, > a:hover { background-color: $hover-background; color: $hover-color; diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index 882123c0b0a..da8f9ce1908 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -173,7 +173,7 @@ %ul.sidebar-sub-level-items - can_edit = can?(current_user, :admin_project, @project) - if can_edit - = nav_link(controller: :projects) do + = nav_link(path: %w[projects#edit]) do = link_to edit_project_path(@project), title: 'General' do %span General From ad633afdec35b4f972706b71e50fad652f65d112 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Jul 2017 08:38:20 +0100 Subject: [PATCH 002/108] transform the position correctly position the arrow fix scss lint --- app/assets/javascripts/main.js | 16 +++++++-- app/assets/stylesheets/new_sidebar.scss | 43 ++++++++++++++----------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 9364d19def1..95c0082e8dd 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -348,6 +348,9 @@ $(function () { $(document).trigger('init.scrolling-tabs'); + const style = document.createElement("style"); + document.head.appendChild(style); + $('.sidebar-top-level-items > li:not(.active)').on('mouseover', (e) => { const windowHeight = window.innerHeight; const $this = e.currentTarget; @@ -355,13 +358,20 @@ $(function () { if ($subitems.length) { const boundingRect = $this.getBoundingClientRect(); - const bottomOverflow = windowHeight - (boundingRect.top + $subitems.outerHeight()); const top = bottomOverflow < 0 ? boundingRect.top - Math.abs(bottomOverflow) : boundingRect.top; $subitems.css({ - top, + transform: `translate3d(0, ${top}px, 0)`, }); + + style.sheet.insertRule(`.sidebar-sub-level-items::before { transform: translate3d(0, ${boundingRect.top - top}px, 0); }`, 0); } - }).on('mouseout', e => $('.sidebar-sub-level-items', e.currentTarget).hide()); + }).on('mouseout', (e) => { + $('.sidebar-sub-level-items', e.currentTarget).hide(); + + if (style.sheet.rules.length) { + style.sheet.deleteRule(0); + } + }); }); diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 527d47d5353..5326ce627f5 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -98,7 +98,6 @@ $new-sidebar-width: 220px; } li { - position: relative; white-space: nowrap; a { @@ -127,20 +126,6 @@ $new-sidebar-width: 220px; padding-bottom: 8px; > li { - &:not(.active) { - @media (min-width: $screen-sm-min) { - a { - padding: 11px 16px 11px 24px; - - &:hover, - &:focus { - background: transparent; - font-weight: 600; - } - } - } - } - a { padding: 8px 16px 8px 24px; @@ -170,13 +155,14 @@ $new-sidebar-width: 220px; .sidebar-sub-level-items { @media (min-width: $screen-sm-min) { position: fixed; + top: 0; left: 220px; width: 150px; margin-left: -1px; padding-bottom: 0; - background-color: #fff; - box-shadow: 2px 1px 3px rgba(0,0,0,.1); - border: 1px solid #e5e5e5; + background-color: $white-light; + box-shadow: 2px 1px 3px $dropdown-shadow-color; + border: 1px solid $dropdown-border-color; border-left: 0; &::before { @@ -189,6 +175,27 @@ $new-sidebar-width: 220px; border-style: solid; border-width: 21px 12px; border-color: transparent transparent transparent $hover-background; + pointer-events: none; + } + + &::after { + content: ""; + position: absolute; + top: 44px; + left: -20px; + right: 0; + bottom: 0; + z-index: -1; + } + + a { + padding: 11px 16px 11px 24px; + + &:hover, + &:focus { + background: transparent; + font-weight: 600; + } } } } From 7505417478011e489e011c3cabf71c11a75e5da2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Jul 2017 10:36:08 +0100 Subject: [PATCH 003/108] moved JS & only run when new_nav cookie exists --- app/assets/javascripts/fly_out_nav.js | 35 +++++++++++++++++++++++++ app/assets/javascripts/layout_nav.js | 6 +++++ app/assets/javascripts/main.js | 27 ------------------- spec/javascripts/fly_out_nav_spec.js | 37 +++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 app/assets/javascripts/fly_out_nav.js create mode 100644 spec/javascripts/fly_out_nav_spec.js diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js new file mode 100644 index 00000000000..202a8c18c22 --- /dev/null +++ b/app/assets/javascripts/fly_out_nav.js @@ -0,0 +1,35 @@ +export const calculateTop = (boundingRect, outerHeight) => { + const windowHeight = window.innerHeight; + const bottomOverflow = windowHeight - (boundingRect.top + outerHeight); + + return bottomOverflow < 0 ? boundingRect.top - Math.abs(bottomOverflow) : boundingRect.top; +}; + +export const createArrowStyles = (boundingRect, top) => `.sidebar-sub-level-items::before { transform: translate3d(0, ${boundingRect.top - top}px, 0); }`; + +export default () => { + const style = document.createElement('style'); + document.head.appendChild(style); + + $('.sidebar-top-level-items > li:not(.active)').on('mouseover', (e) => { + const $this = e.currentTarget; + const $subitems = $('.sidebar-sub-level-items', $this).show(); + + if ($subitems.length) { + const boundingRect = $this.getBoundingClientRect(); + const top = calculateTop(boundingRect, $subitems.outerHeight()); + + $subitems.css({ + transform: `translate3d(0, ${top}px, 0)`, + }); + + style.sheet.insertRule(createArrowStyles(boundingRect, top), 0); + } + }).on('mouseout', (e) => { + $('.sidebar-sub-level-items', e.currentTarget).hide(); + + if (style.sheet.rules.length) { + style.sheet.deleteRule(0); + } + }); +}; diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 71064ccc539..e0363e091a7 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,5 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */ +import Cookies from 'js-cookie'; import _ from 'underscore'; +import initFlyOutNav from './fly_out_nav'; (function() { var hideEndFade; @@ -54,5 +56,9 @@ import _ from 'underscore'; $(() => { $(window).on('scroll', _.throttle(applyScrollNavClass, 100)); + + if (Cookies.get('new_nav') === 'true') { + initFlyOutNav(); + } }); }).call(window); diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 95c0082e8dd..26c67fb721c 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -347,31 +347,4 @@ $(function () { gl.utils.renderTimeago(); $(document).trigger('init.scrolling-tabs'); - - const style = document.createElement("style"); - document.head.appendChild(style); - - $('.sidebar-top-level-items > li:not(.active)').on('mouseover', (e) => { - const windowHeight = window.innerHeight; - const $this = e.currentTarget; - const $subitems = $('.sidebar-sub-level-items', $this).show(); - - if ($subitems.length) { - const boundingRect = $this.getBoundingClientRect(); - const bottomOverflow = windowHeight - (boundingRect.top + $subitems.outerHeight()); - const top = bottomOverflow < 0 ? boundingRect.top - Math.abs(bottomOverflow) : boundingRect.top; - - $subitems.css({ - transform: `translate3d(0, ${top}px, 0)`, - }); - - style.sheet.insertRule(`.sidebar-sub-level-items::before { transform: translate3d(0, ${boundingRect.top - top}px, 0); }`, 0); - } - }).on('mouseout', (e) => { - $('.sidebar-sub-level-items', e.currentTarget).hide(); - - if (style.sheet.rules.length) { - style.sheet.deleteRule(0); - } - }); }); diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js new file mode 100644 index 00000000000..bbf3eb6f582 --- /dev/null +++ b/spec/javascripts/fly_out_nav_spec.js @@ -0,0 +1,37 @@ +import { calculateTop, createArrowStyles } from '~/fly_out_nav'; + +describe('Fly out sidebar navigation', () => { + describe('calculateTop', () => { + it('returns boundingRect top', () => { + const boundingRect = { + top: 100, + }; + + expect( + calculateTop(boundingRect, 100), + ).toBe(100); + }); + + it('returns boundingRect - bottomOverflow', () => { + const boundingRect = { + top: window.innerHeight, + }; + + expect( + calculateTop(boundingRect, 100), + ).toBe(window.innerHeight - 100); + }); + }); + + describe('createArrowStyles', () => { + it('returns translate3d styles', () => { + const boundingRect = { + top: 100, + }; + + expect( + createArrowStyles(boundingRect, 50), + ).toContain('translate3d(0, 50px, 0)'); + }); + }); +}); From b5399517687edbe854f3bb51a89c30c13a3f12b4 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 18 Jul 2017 21:40:35 +0900 Subject: [PATCH 004/108] Ini --- lib/api/api.rb | 1 + lib/api/group_variables.rb | 95 ++++++++++ lib/api/helpers.rb | 4 + spec/requests/api/group_variables_spec.rb | 221 ++++++++++++++++++++++ 4 files changed, 321 insertions(+) create mode 100644 lib/api/group_variables.rb create mode 100644 spec/requests/api/group_variables_spec.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index efcf0976a81..f6a310841e4 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -134,6 +134,7 @@ module API mount ::API::Triggers mount ::API::Users mount ::API::Variables + mount ::API::GroupVariables mount ::API::Version route :any, '*path' do diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb new file mode 100644 index 00000000000..0dd418887e0 --- /dev/null +++ b/lib/api/group_variables.rb @@ -0,0 +1,95 @@ +module API + class GroupVariables < Grape::API + include PaginationParams + + before { authenticate! } + before { authorize! :admin_build, user_group } + + params do + requires :id, type: String, desc: 'The ID of a group' + end + + resource :groups, requirements: { id: %r{[^/]+} } do + desc 'Get group-level variables' do + success Entities::Variable + end + params do + use :pagination + end + get ':id/variables' do + variables = user_group.variables + present paginate(variables), with: Entities::Variable + end + + desc 'Get a specific variable from a group' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end + get ':id/variables/:key' do + key = params[:key] + variable = user_group.variables.find_by(key: key) + + return not_found!('GroupVariable') unless variable + + present variable, with: Entities::Variable + end + + desc 'Create a new variable in a group' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + requires :value, type: String, desc: 'The value of the variable' + optional :protected, type: String, desc: 'Whether the variable is protected' + end + post ':id/variables' do + variable_params = declared_params(include_missing: false) + + variable = user_group.variables.create(variable_params) + + if variable.valid? + present variable, with: Entities::Variable + else + render_validation_error!(variable) + end + end + + desc 'Update an existing variable from a group' do + success Entities::Variable + end + params do + optional :key, type: String, desc: 'The key of the variable' + optional :value, type: String, desc: 'The value of the variable' + optional :protected, type: String, desc: 'Whether the variable is protected' + end + put ':id/variables/:key' do + variable = user_group.variables.find_by(key: params[:key]) + + return not_found!('GroupVariable') unless variable + + variable_params = declared_params(include_missing: false).except(:key) + + if variable.update(variable_params) + present variable, with: Entities::Variable + else + render_validation_error!(variable) + end + end + + desc 'Delete an existing variable from a group' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end + delete ':id/variables/:key' do + variable = user_group.variables.find_by(key: params[:key]) + not_found!('GroupVariable') unless variable + + variable.destroy + end + end + end +end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 0f4791841d2..56cd1f3df5a 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -29,6 +29,10 @@ module API @project ||= find_project!(params[:id]) end + def user_group + @group ||= find_group!(params[:id]) + end + def available_labels @available_labels ||= LabelsFinder.new(current_user, project_id: user_project.id).execute end diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb new file mode 100644 index 00000000000..402ea057cc5 --- /dev/null +++ b/spec/requests/api/group_variables_spec.rb @@ -0,0 +1,221 @@ +require 'spec_helper' + +describe API::GroupVariables do + let(:group) { create(:group) } + let(:user) { create(:user) } + + describe 'GET /groups/:id/variables' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'returns group variables' do + get api("/groups/#{group.id}/variables", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_a(Array) + end + end + + context 'authorized user with invalid permissions' do + it 'does not return group variables' do + get api("/groups/#{group.id}/variables", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not return group variables' do + get api("/groups/#{group.id}/variables") + + expect(response).to have_http_status(401) + end + end + end + + describe 'GET /groups/:id/variables/:key' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'returns group variable details' do + get api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(200) + expect(json_response['value']).to eq(variable.value) + expect(json_response['protected']).to eq(variable.protected?) + end + + it 'responds with 404 Not Found if requesting non-existing variable' do + get api("/groups/#{group.id}/variables/non_existing_variable", user) + + expect(response).to have_http_status(404) + end + end + + context 'authorized user with invalid permissions' do + it 'does not return group variable details' do + get api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not return group variable details' do + get api("/groups/#{group.id}/variables/#{variable.key}") + + expect(response).to have_http_status(401) + end + end + end + + describe 'POST /groups/:id/variables' do + context 'authorized user with proper permissions' do + let!(:variable) { create(:ci_group_variable, group: group) } + + before do + group.add_master(user) + end + + it 'creates variable' do + expect do + post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true + end.to change{group.variables.count}.by(1) + + expect(response).to have_http_status(201) + expect(json_response['key']).to eq('TEST_VARIABLE_2') + expect(json_response['value']).to eq('VALUE_2') + expect(json_response['protected']).to be_truthy + end + + it 'creates variable with optional attributes' do + expect do + post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2' + end.to change{group.variables.count}.by(1) + + expect(response).to have_http_status(201) + expect(json_response['key']).to eq('TEST_VARIABLE_2') + expect(json_response['value']).to eq('VALUE_2') + expect(json_response['protected']).to be_falsey + end + + it 'does not allow to duplicate variable key' do + expect do + post api("/groups/#{group.id}/variables", user), key: variable.key, value: 'VALUE_2' + end.to change{group.variables.count}.by(0) + + expect(response).to have_http_status(400) + end + end + + context 'authorized user with invalid permissions' do + it 'does not create variable' do + post api("/groups/#{group.id}/variables", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not create variable' do + post api("/groups/#{group.id}/variables") + + expect(response).to have_http_status(401) + end + end + end + + describe 'PUT /groups/:id/variables/:key' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'updates variable data' do + initial_variable = group.variables.first + value_before = initial_variable.value + + put api("/groups/#{group.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP', protected: true + + updated_variable = group.variables.first + + expect(response).to have_http_status(200) + expect(value_before).to eq(variable.value) + expect(updated_variable.value).to eq('VALUE_1_UP') + expect(updated_variable).to be_protected + end + + it 'responds with 404 Not Found if requesting non-existing variable' do + put api("/groups/#{group.id}/variables/non_existing_variable", user) + + expect(response).to have_http_status(404) + end + end + + context 'authorized user with invalid permissions' do + it 'does not update variable' do + put api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not update variable' do + put api("/groups/#{group.id}/variables/#{variable.key}") + + expect(response).to have_http_status(401) + end + end + end + + describe 'DELETE /groups/:id/variables/:key' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'deletes variable' do + expect do + delete api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(204) + end.to change{group.variables.count}.by(-1) + end + + it 'responds with 404 Not Found if requesting non-existing variable' do + delete api("/groups/#{group.id}/variables/non_existing_variable", user) + + expect(response).to have_http_status(404) + end + end + + context 'authorized user with invalid permissions' do + it 'does not delete variable' do + delete api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not delete variable' do + delete api("/groups/#{group.id}/variables/#{variable.key}") + + expect(response).to have_http_status(401) + end + end + end +end From 280cfc6152986debbd909261210d12be0a70c810 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 18 Jul 2017 21:53:16 +0900 Subject: [PATCH 005/108] Add changelog --- .../unreleased/34519-extend-api-group-secret-variable.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/34519-extend-api-group-secret-variable.yml diff --git a/changelogs/unreleased/34519-extend-api-group-secret-variable.yml b/changelogs/unreleased/34519-extend-api-group-secret-variable.yml new file mode 100644 index 00000000000..e0b625c392f --- /dev/null +++ b/changelogs/unreleased/34519-extend-api-group-secret-variable.yml @@ -0,0 +1,4 @@ +--- +title: Extend API for Group Secret Variable +merge_request: 12936 +author: From 862e2c80be2963009c74d01e502e7ac9777c3a86 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Wed, 19 Jul 2017 20:57:27 +0900 Subject: [PATCH 006/108] Document update --- doc/api/README.md | 3 +- doc/api/group_level_variables.md | 125 ++++++++++++++++++ ...ariables.md => project_level_variables.md} | 12 +- 3 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 doc/api/group_level_variables.md rename doc/api/{build_variables.md => project_level_variables.md} (94%) diff --git a/doc/api/README.md b/doc/api/README.md index 95e7a457848..9c308254ab2 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -11,7 +11,8 @@ following locations: - [Award Emoji](award_emoji.md) - [Branches](branches.md) - [Broadcast Messages](broadcast_messages.md) -- [Build Variables](build_variables.md) +- [Project-level Variables](project_level_variables.md) +- [Group-level Variables](group_level_variables.md) - [Commits](commits.md) - [Deployments](deployments.md) - [Deploy Keys](deploy_keys.md) diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md new file mode 100644 index 00000000000..e19be7b35c4 --- /dev/null +++ b/doc/api/group_level_variables.md @@ -0,0 +1,125 @@ +# Group-level Variables API + +## List group variables + +Get list of a group's variables. + +``` +GET /groups/:id/variables +``` + +| Attribute | Type | required | Description | +|-----------|---------|----------|---------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | + +``` +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables" +``` + +```json +[ + { + "key": "TEST_VARIABLE_1", + "value": "TEST_1" + }, + { + "key": "TEST_VARIABLE_2", + "value": "TEST_2" + } +] +``` + +## Show variable details + +Get the details of a group's specific variable. + +``` +GET /groups/:id/variables/:key +``` + +| Attribute | Type | required | Description | +|-----------|---------|----------|-----------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable | + +``` +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/TEST_VARIABLE_1" +``` + +```json +{ + "key": "TEST_VARIABLE_1", + "value": "TEST_1" +} +``` + +## Create variable + +Create a new variable. + +``` +POST /groups/:id/variables +``` + +| Attribute | Type | required | Description | +|-------------|---------|----------|-----------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed | +| `value` | string | yes | The `value` of a variable | +| `protected` | boolean | no | Whether the variable is protected | + +``` +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables" --form "key=NEW_VARIABLE" --form "value=new value" +``` + +```json +{ + "key": "NEW_VARIABLE", + "value": "new value", + "protected": false +} +``` + +## Update variable + +Update a group's variable. + +``` +PUT /groups/:id/variables/:key +``` + +| Attribute | Type | required | Description | +|-------------|---------|----------|-------------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable | +| `value` | string | yes | The `value` of a variable | +| `protected` | boolean | no | Whether the variable is protected | + +``` +curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/NEW_VARIABLE" --form "value=updated value" +``` + +```json +{ + "key": "NEW_VARIABLE", + "value": "updated value", + "protected": true +} +``` + +## Remove variable + +Remove a group's variable. + +``` +DELETE /groups/:id/variables/:key +``` + +| Attribute | Type | required | Description | +|-----------|---------|----------|-------------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable | + +``` +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/VARIABLE_1" +``` diff --git a/doc/api/build_variables.md b/doc/api/project_level_variables.md similarity index 94% rename from doc/api/build_variables.md rename to doc/api/project_level_variables.md index d4f00256ed3..82ac0b09027 100644 --- a/doc/api/build_variables.md +++ b/doc/api/project_level_variables.md @@ -1,8 +1,8 @@ -# Build Variables API +# Project-level Variables API ## List project variables -Get list of a project's build variables. +Get list of a project's variables. ``` GET /projects/:id/variables @@ -31,7 +31,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/ ## Show variable details -Get the details of a project's specific build variable. +Get the details of a project's specific variable. ``` GET /projects/:id/variables/:key @@ -55,7 +55,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/ ## Create variable -Create a new build variable. +Create a new variable. ``` POST /projects/:id/variables @@ -82,7 +82,7 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitl ## Update variable -Update a project's build variable. +Update a project's variable. ``` PUT /projects/:id/variables/:key @@ -109,7 +109,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitla ## Remove variable -Remove a project's build variable. +Remove a project's variable. ``` DELETE /projects/:id/variables/:key From 0925edb4ff4ae8c12648be7a59866340c81b9eff Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Jul 2017 16:57:00 +0100 Subject: [PATCH 007/108] updated styles --- app/assets/javascripts/fly_out_nav.js | 15 +---------- app/assets/stylesheets/new_sidebar.scss | 33 +++++++++---------------- spec/javascripts/fly_out_nav_spec.js | 14 +---------- 3 files changed, 13 insertions(+), 49 deletions(-) diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 202a8c18c22..1ae2e72410f 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -5,12 +5,7 @@ export const calculateTop = (boundingRect, outerHeight) => { return bottomOverflow < 0 ? boundingRect.top - Math.abs(bottomOverflow) : boundingRect.top; }; -export const createArrowStyles = (boundingRect, top) => `.sidebar-sub-level-items::before { transform: translate3d(0, ${boundingRect.top - top}px, 0); }`; - export default () => { - const style = document.createElement('style'); - document.head.appendChild(style); - $('.sidebar-top-level-items > li:not(.active)').on('mouseover', (e) => { const $this = e.currentTarget; const $subitems = $('.sidebar-sub-level-items', $this).show(); @@ -22,14 +17,6 @@ export default () => { $subitems.css({ transform: `translate3d(0, ${top}px, 0)`, }); - - style.sheet.insertRule(createArrowStyles(boundingRect, top), 0); } - }).on('mouseout', (e) => { - $('.sidebar-sub-level-items', e.currentTarget).hide(); - - if (style.sheet.rules.length) { - style.sheet.deleteRule(0); - } - }); + }).on('mouseout', e => $('.sidebar-sub-level-items', e.currentTarget).hide()); }; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 5326ce627f5..b74e5ad3272 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -151,6 +151,12 @@ $new-sidebar-width: 220px; .sidebar-top-level-items { > li { + > a { + @media (min-width: $screen-sm-min) { + margin-right: 2px; + } + } + &:not(.active) { .sidebar-sub-level-items { @media (min-width: $screen-sm-min) { @@ -158,43 +164,26 @@ $new-sidebar-width: 220px; top: 0; left: 220px; width: 150px; - margin-left: -1px; - padding-bottom: 0; - background-color: $white-light; + background-color: $hover-background; box-shadow: 2px 1px 3px $dropdown-shadow-color; - border: 1px solid $dropdown-border-color; - border-left: 0; - - &::before { - content: ""; - position: absolute; - left: 0; - top: 0; - width: 0; - height: 0; - border-style: solid; - border-width: 21px 12px; - border-color: transparent transparent transparent $hover-background; - pointer-events: none; - } + border-radius: 0 3px 3px 0; &::after { content: ""; position: absolute; top: 44px; - left: -20px; + left: -30px; right: 0; bottom: 0; z-index: -1; } a { - padding: 11px 16px 11px 24px; + color: $white-light; &:hover, &:focus { - background: transparent; - font-weight: 600; + background-color: darken($hover-background, 10%); } } } diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index bbf3eb6f582..0e71e2a87e5 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -1,4 +1,4 @@ -import { calculateTop, createArrowStyles } from '~/fly_out_nav'; +import { calculateTop } from '~/fly_out_nav'; describe('Fly out sidebar navigation', () => { describe('calculateTop', () => { @@ -22,16 +22,4 @@ describe('Fly out sidebar navigation', () => { ).toBe(window.innerHeight - 100); }); }); - - describe('createArrowStyles', () => { - it('returns translate3d styles', () => { - const boundingRect = { - top: 100, - }; - - expect( - createArrowStyles(boundingRect, 50), - ).toContain('translate3d(0, 50px, 0)'); - }); - }); }); From 030d0608f68364fbab38ee33e7d0b004219e594d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Jul 2017 16:59:22 +0100 Subject: [PATCH 008/108] changed hover color in dropdown --- app/assets/stylesheets/new_sidebar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index b74e5ad3272..e9cfb920f00 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -183,7 +183,7 @@ $new-sidebar-width: 220px; &:hover, &:focus { - background-color: darken($hover-background, 10%); + background-color: rgba($black, .12); } } } From 1a2d180e3dfd8bdad94766a2e8b1195288c2b146 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Jul 2017 17:01:00 +0100 Subject: [PATCH 009/108] changed text color for reseting & hover on links --- app/assets/stylesheets/new_sidebar.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index e9cfb920f00..43c4d52d1e2 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -179,10 +179,11 @@ $new-sidebar-width: 220px; } a { - color: $white-light; + color: rgba($white-light, .9); &:hover, &:focus { + color: $white-light; background-color: rgba($black, .12); } } From 72538b5f9f6f1be0ba421a76c2a24da4dd23db0e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 24 Jul 2017 16:14:29 +0100 Subject: [PATCH 010/108] improvements to positioning of the dropdown --- app/assets/javascripts/fly_out_nav.js | 10 ++++++++-- app/assets/stylesheets/new_sidebar.scss | 11 ++++++++++- spec/javascripts/fly_out_nav_spec.js | 6 ++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 1ae2e72410f..f2151396d43 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -2,7 +2,8 @@ export const calculateTop = (boundingRect, outerHeight) => { const windowHeight = window.innerHeight; const bottomOverflow = windowHeight - (boundingRect.top + outerHeight); - return bottomOverflow < 0 ? boundingRect.top - Math.abs(bottomOverflow) : boundingRect.top; + return bottomOverflow < 0 ? (boundingRect.top - outerHeight) + boundingRect.height : + boundingRect.top; }; export default () => { @@ -13,10 +14,15 @@ export default () => { if ($subitems.length) { const boundingRect = $this.getBoundingClientRect(); const top = calculateTop(boundingRect, $subitems.outerHeight()); + const isAbove = top < boundingRect.top; $subitems.css({ transform: `translate3d(0, ${top}px, 0)`, }); + + if (isAbove) { + $subitems.addClass('is-above'); + } } - }).on('mouseout', e => $('.sidebar-sub-level-items', e.currentTarget).hide()); + }).on('mouseout', e => $('.sidebar-sub-level-items', e.currentTarget).hide().removeClass('is-above')); }; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 78f278c1669..cd6c7914142 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -215,9 +215,18 @@ $new-sidebar-width: 220px; position: absolute; top: 44px; left: -30px; - right: 0; bottom: 0; + height: 100%; + max-height: 150px; + width: 300px; z-index: -1; + transform: skew(33deg); + } + + &.is-above::after { + top: auto; + bottom: 44px; + transform: skew(-30deg); } a { diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index 0e71e2a87e5..d3c6dafe460 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -5,6 +5,7 @@ describe('Fly out sidebar navigation', () => { it('returns boundingRect top', () => { const boundingRect = { top: 100, + height: 100, }; expect( @@ -14,12 +15,13 @@ describe('Fly out sidebar navigation', () => { it('returns boundingRect - bottomOverflow', () => { const boundingRect = { - top: window.innerHeight, + top: window.innerHeight - 50, + height: 100, }; expect( calculateTop(boundingRect, 100), - ).toBe(window.innerHeight - 100); + ).toBe(window.innerHeight - 50); }); }); }); From 80b55398a63d1536188ced206e47b2f2e623ca82 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Jul 2017 16:53:53 +0100 Subject: [PATCH 011/108] added extra padding around fly-out menu to delay hiding --- app/assets/stylesheets/new_sidebar.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index cd6c7914142..92d6e1152e6 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -210,15 +210,25 @@ $new-sidebar-width: 220px; box-shadow: 2px 1px 3px $dropdown-shadow-color; border-radius: 0 3px 3px 0; + &::before { + content: ""; + position: absolute; + top: -20px; + bottom: -20px; + left: 0; + right: -20px; + z-index: -1; + } + &::after { content: ""; position: absolute; top: 44px; left: -30px; + right: 35px; bottom: 0; height: 100%; max-height: 150px; - width: 300px; z-index: -1; transform: skew(33deg); } From d6b2c87061e02be5949a80d7c107b3d34fa6e14b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 26 Jul 2017 17:25:49 +0100 Subject: [PATCH 012/108] style updates fixed weird dropdown items in issues when on merge request page --- app/assets/stylesheets/new_sidebar.scss | 28 +++++++------ .../nav/_new_project_sidebar.html.haml | 41 ++++++++----------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 92d6e1152e6..06c2efb3a4d 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -195,6 +195,7 @@ $new-sidebar-width: 220px; > li { > a { @media (min-width: $screen-sm-min) { + margin-left: 4px; margin-right: 2px; } } @@ -204,10 +205,13 @@ $new-sidebar-width: 220px; @media (min-width: $screen-sm-min) { position: fixed; top: 0; - left: 220px; + left: 219px; width: 150px; - background-color: $hover-background; + margin-top: -1px; + padding: 1px; + background-color: $white-light; box-shadow: 2px 1px 3px $dropdown-shadow-color; + border: 1px solid $gray-darker; border-radius: 0 3px 3px 0; &::before { @@ -233,19 +237,20 @@ $new-sidebar-width: 220px; transform: skew(33deg); } - &.is-above::after { - top: auto; - bottom: 44px; - transform: skew(-30deg); + &.is-above { + margin-top: 1px; + + &::after { + top: auto; + bottom: 44px; + transform: skew(-30deg); + } } a { - color: rgba($white-light, .9); - &:hover, &:focus { - color: $white-light; - background-color: rgba($black, .12); + background-color: $gray-darker; } } } @@ -273,8 +278,7 @@ $new-sidebar-width: 220px; &:not(.active):hover > a, > a:hover { - background-color: $hover-background; - color: $hover-color; + background-color: $white-light; .badge { background-color: $indigo-500; diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index 15327847b17..6f31919b360 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -83,34 +83,25 @@ Issues %ul.sidebar-sub-level-items - - if project_nav_tab?(:issues) && !current_controller?(:merge_requests) - = nav_link(controller: :issues) do - = link_to project_issues_path(@project), title: 'Issues' do - %span - List + = nav_link(controller: :issues) do + = link_to project_issues_path(@project), title: 'Issues' do + %span + List - = nav_link(controller: :boards) do - = link_to project_boards_path(@project), title: 'Board' do - %span - Board + = nav_link(controller: :boards) do + = link_to project_boards_path(@project), title: 'Board' do + %span + Board - - if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests) - = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project), title: 'Merge Requests' do - %span - Merge Requests + = nav_link(controller: :labels) do + = link_to project_labels_path(@project), title: 'Labels' do + %span + Labels - - if project_nav_tab? :labels - = nav_link(controller: :labels) do - = link_to project_labels_path(@project), title: 'Labels' do - %span - Labels - - - if project_nav_tab? :milestones - = nav_link(controller: :milestones) do - = link_to project_milestones_path(@project), title: 'Milestones' do - %span - Milestones + = nav_link(controller: :milestones) do + = link_to project_milestones_path(@project), title: 'Milestones' do + %span + Milestones - if project_nav_tab? :merge_requests = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do From 8961b7ec602d583c00b2df4c310092a2472a0ee2 Mon Sep 17 00:00:00 2001 From: winh Date: Wed, 26 Jul 2017 13:45:09 +0200 Subject: [PATCH 013/108] Make header dropdown styles consistent --- app/assets/stylesheets/framework/header.scss | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 605f4284bb5..33d016652e5 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -313,6 +313,25 @@ header { .impersonation i { color: $red-500; } + + // TODO: fallback to global style + .dropdown-menu, + .dropdown-menu-nav { + li { + padding: 0 1px; + + a { + border-radius: 0; + padding: 8px 16px; + + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .navbar-nav { From 948deb97cb40dbffaf3c290534bb9ffe2f3499c8 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Jul 2017 10:14:32 +0100 Subject: [PATCH 014/108] updated hover text color of main links in sidebar fixed colors in fly-out navigation fixed spacing in fly-out navigation --- app/assets/stylesheets/new_sidebar.scss | 26 +++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 06c2efb3a4d..d0568e92f84 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -195,23 +195,32 @@ $new-sidebar-width: 220px; > li { > a { @media (min-width: $screen-sm-min) { - margin-left: 4px; margin-right: 2px; } + + &:hover { + color: $gl-text-color; + } } &:not(.active) { + a { + margin-left: 1px; + margin-right: 3px; + } + .sidebar-sub-level-items { @media (min-width: $screen-sm-min) { position: fixed; top: 0; - left: 219px; + left: 220px; width: 150px; margin-top: -1px; - padding: 1px; + padding: 8px 1px; background-color: $white-light; box-shadow: 2px 1px 3px $dropdown-shadow-color; border: 1px solid $gray-darker; + border-left: 0; border-radius: 0 3px 3px 0; &::before { @@ -248,6 +257,8 @@ $new-sidebar-width: 220px; } a { + color: $gl-text-color; + &:hover, &:focus { background-color: $gray-darker; @@ -266,6 +277,10 @@ $new-sidebar-width: 220px; &.active { background: $active-background; + > a { + margin-left: 4px; + } + .badge { color: $active-color; font-weight: 600; @@ -279,11 +294,6 @@ $new-sidebar-width: 220px; &:not(.active):hover > a, > a:hover { background-color: $white-light; - - .badge { - background-color: $indigo-500; - color: $hover-color; - } } } } From f20a48494a4d60ddf311b85ce51ba0cb788390be Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Jul 2017 15:56:08 +0100 Subject: [PATCH 015/108] fixed inside padding of fly-out menu --- app/assets/stylesheets/new_sidebar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index d0568e92f84..05b72e9f425 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -204,7 +204,7 @@ $new-sidebar-width: 220px; } &:not(.active) { - a { + > a { margin-left: 1px; margin-right: 3px; } From bab49fdf9f27c44e830c470b749c5bc0022a25f5 Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Tue, 25 Jul 2017 16:51:37 -0700 Subject: [PATCH 016/108] Protect backups from stale cache for repo exists --- lib/backup/repository.rb | 1 + spec/lib/gitlab/backup/repository_spec.rb | 54 +++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index a1685c77916..02ed1e49ef8 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -189,6 +189,7 @@ module Backup end def empty_repo?(project_or_wiki) + project_or_wiki.repository.expire_exists_cache # protect backups from stale cache project_or_wiki.repository.empty_repo? rescue => e progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.path_with_namespace} - #{e.message}".color(:orange) diff --git a/spec/lib/gitlab/backup/repository_spec.rb b/spec/lib/gitlab/backup/repository_spec.rb index db860b01ba4..79b0b5008c5 100644 --- a/spec/lib/gitlab/backup/repository_spec.rb +++ b/spec/lib/gitlab/backup/repository_spec.rb @@ -60,4 +60,58 @@ describe Backup::Repository do end end end + + describe '#empty_repo?' do + context 'for a wiki' do + let(:wiki) { create(:project_wiki) } + + context 'wiki repo has content' do + let!(:wiki_page) { create(:wiki_page, wiki: wiki) } + + before do + wiki.repository.exists? # initial cache + end + + context '`repository.exists?` is incorrectly cached as false' do + before do + repo = wiki.repository + repo.send(:cache).expire(:exists?) + repo.send(:cache).fetch(:exists?) { false } + repo.send(:instance_variable_set, :@exists, false) + end + + it 'returns false, regardless of bad cache value' do + expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_falsey + end + end + + context '`repository.exists?` is correctly cached as true' do + it 'returns false' do + expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_falsey + end + end + end + + context 'wiki repo does not have content' do + context '`repository.exists?` is incorrectly cached as true' do + before do + repo = wiki.repository + repo.send(:cache).expire(:exists?) + repo.send(:cache).fetch(:exists?) { true } + repo.send(:instance_variable_set, :@exists, true) + end + + it 'returns true, regardless of bad cache value' do + expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_truthy + end + end + + context '`repository.exists?` is correctly cached as false' do + it 'returns true' do + expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_truthy + end + end + end + end + end end From 61239585c97f1b092e776d6f8ba0713188860f78 Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Tue, 25 Jul 2017 17:04:42 -0700 Subject: [PATCH 017/108] Add changelog --- changelogs/unreleased/mk-fix-wiki-backup.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/mk-fix-wiki-backup.yml diff --git a/changelogs/unreleased/mk-fix-wiki-backup.yml b/changelogs/unreleased/mk-fix-wiki-backup.yml new file mode 100644 index 00000000000..ba9c1e85955 --- /dev/null +++ b/changelogs/unreleased/mk-fix-wiki-backup.yml @@ -0,0 +1,4 @@ +--- +title: Fix improperly skipped backups of wikis. +merge_request: 13096 +author: From a459e45eac290914c864616468f1527f6b1fdaab Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Fri, 28 Jul 2017 09:53:12 -0700 Subject: [PATCH 018/108] Fix Rubocop offense --- spec/lib/gitlab/backup/repository_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/lib/gitlab/backup/repository_spec.rb b/spec/lib/gitlab/backup/repository_spec.rb index 79b0b5008c5..3af69daa585 100644 --- a/spec/lib/gitlab/backup/repository_spec.rb +++ b/spec/lib/gitlab/backup/repository_spec.rb @@ -81,13 +81,13 @@ describe Backup::Repository do end it 'returns false, regardless of bad cache value' do - expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_falsey + expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey end end context '`repository.exists?` is correctly cached as true' do it 'returns false' do - expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_falsey + expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey end end end @@ -102,13 +102,13 @@ describe Backup::Repository do end it 'returns true, regardless of bad cache value' do - expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_truthy + expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy end end context '`repository.exists?` is correctly cached as false' do it 'returns true' do - expect(Backup::Repository.new.send(:empty_repo?, wiki)).to be_truthy + expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy end end end From f5fc912b33e0d343b8ef88b543a3b5b0b1f3cf9b Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Fri, 28 Jul 2017 09:25:13 -0700 Subject: [PATCH 019/108] Exclude keys linked to other projects --- app/models/project.rb | 13 +++++- .../unreleased/mk-fix-deploy-key-deletion.yml | 4 ++ spec/models/project_spec.rb | 42 +++++++++++++++---- 3 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/mk-fix-deploy-key-deletion.yml diff --git a/app/models/project.rb b/app/models/project.rb index d827bfaa806..90967a12b96 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1265,7 +1265,18 @@ class Project < ActiveRecord::Base end def remove_private_deploy_keys - deploy_keys.where(public: false).delete_all + exclude_keys_linked_to_other_projects = <<-SQL + NOT EXISTS ( + SELECT 1 + FROM deploy_keys_projects dkp2 + WHERE dkp2.deploy_key_id = deploy_keys_projects.deploy_key_id + AND dkp2.project_id != deploy_keys_projects.project_id + ) + SQL + + deploy_keys.where(public: false) + .where(exclude_keys_linked_to_other_projects) + .delete_all end def remove_pages diff --git a/changelogs/unreleased/mk-fix-deploy-key-deletion.yml b/changelogs/unreleased/mk-fix-deploy-key-deletion.yml new file mode 100644 index 00000000000..9ff2e49b14c --- /dev/null +++ b/changelogs/unreleased/mk-fix-deploy-key-deletion.yml @@ -0,0 +1,4 @@ +--- +title: Fix deletion of deploy keys linked to other projects +merge_request: 13162 +author: diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 473b7a88d61..19808e7d36a 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2238,19 +2238,43 @@ describe Project do end describe '#remove_private_deploy_keys' do - it 'removes the private deploy keys of a project' do - project = create(:empty_project) + let!(:project) { create(:empty_project) } - private_key = create(:deploy_key, public: false) - public_key = create(:deploy_key, public: true) + context 'for a private deploy key' do + let!(:key) { create(:deploy_key, public: false) } + let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) } - create(:deploy_keys_project, deploy_key: private_key, project: project) - create(:deploy_keys_project, deploy_key: public_key, project: project) + context 'when the key is not linked to another project' do + it 'removes the key' do + project.remove_private_deploy_keys - project.remove_private_deploy_keys + expect(project.deploy_keys).not_to include(key) + end + end - expect(project.deploy_keys.where(public: false).any?).to eq(false) - expect(project.deploy_keys.where(public: true).any?).to eq(true) + context 'when the key is linked to another project' do + before do + another_project = create(:empty_project) + create(:deploy_keys_project, deploy_key: key, project: another_project) + end + + it 'does not remove the key' do + project.remove_private_deploy_keys + + expect(project.deploy_keys).to include(key) + end + end + end + + context 'for a public deploy key' do + let!(:key) { create(:deploy_key, public: true) } + let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) } + + it 'does not remove the key' do + project.remove_private_deploy_keys + + expect(project.deploy_keys).to include(key) + end end end end From 8bcfdebf650dee1aec35192bee5ab45b9d6cbd44 Mon Sep 17 00:00:00 2001 From: winh Date: Mon, 31 Jul 2017 11:02:45 +0200 Subject: [PATCH 020/108] Make dropdown style on repository page consistent --- app/assets/stylesheets/pages/tree.scss | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index dc88cf3e699..e0f46172769 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -202,6 +202,28 @@ } } } + + // TODO: fallback to global style + .dropdown-menu:not(.dropdown-menu-selectable) { + li { + padding: 0 1px; + + &.dropdown-header { + padding: 8px 16px; + } + + a { + border-radius: 0; + padding: 8px 16px; + + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .blob-commit-info { From c468628527756884e3b25c3c7d1bcff396637cd8 Mon Sep 17 00:00:00 2001 From: winh Date: Mon, 31 Jul 2017 10:10:59 +0200 Subject: [PATCH 021/108] Make projects dropdown style in new navigation consistent --- app/assets/stylesheets/new_nav.scss | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index 360ffda8d71..7cabf371985 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -309,6 +309,25 @@ header.navbar-gitlab-new { outline: 0; } } + + // TODO: fallback to global style + .dropdown-menu { + li { + padding: 0 1px; + + a { + border-radius: 0; + padding: 8px 16px; + + &.is-focused, + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .breadcrumbs-container { From 57a5544f883ad9687c38270519edc7914912af5d Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 28 Jul 2017 10:44:33 +0100 Subject: [PATCH 022/108] Remove events column from notification settings This was migrated to separate columns in 9.4, and now just needs to be removed for real. --- app/models/notification_setting.rb | 34 +++---------------- ...move-events-from-notification_settings.yml | 4 +++ ...emove_events_from_notification_settings.rb | 9 +++++ db/schema.rb | 3 +- spec/factories/notification_settings.rb | 1 - spec/models/notification_setting_spec.rb | 12 +++---- .../api/notification_settings_spec.rb | 4 +-- spec/spec_helper.rb | 4 +++ 8 files changed, 29 insertions(+), 42 deletions(-) create mode 100644 changelogs/unreleased/33620-remove-events-from-notification_settings.yml create mode 100644 db/post_migrate/20170728101014_remove_events_from_notification_settings.rb diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 81844b1e2ca..9b1cac64c44 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -1,4 +1,8 @@ class NotificationSetting < ActiveRecord::Base + include IgnorableColumn + + ignore_column :events + enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0, custom: 5 } default_value_for :level, NotificationSetting.levels[:global] @@ -41,9 +45,6 @@ class NotificationSetting < ActiveRecord::Base :success_pipeline ].freeze - store :events, coder: JSON - before_save :convert_events - def self.find_or_create_for(source) setting = find_or_initialize_by(source: source) @@ -54,42 +55,17 @@ class NotificationSetting < ActiveRecord::Base setting end - # 1. Check if this event has a value stored in its database column. - # 2. If it does, return that value. - # 3. If it doesn't (the value is nil), return the value from the serialized - # JSON hash in `events`. - (EMAIL_EVENTS - [:failed_pipeline]).each do |event| - define_method(event) do - bool = super() - - bool.nil? ? !!events[event] : bool - end - - alias_method :"#{event}?", event - end - # Allow people to receive failed pipeline notifications if they already have # custom notifications enabled, as these are more like mentions than the other # custom settings. def failed_pipeline bool = super - bool = events[:failed_pipeline] if bool.nil? bool.nil? || bool end alias_method :failed_pipeline?, :failed_pipeline def event_enabled?(event) - respond_to?(event) && public_send(event) - end - - def convert_events - return if events_before_type_cast.nil? - - EMAIL_EVENTS.each do |event| - write_attribute(event, public_send(event)) - end - - write_attribute(:events, nil) + respond_to?(event) && !!public_send(event) end end diff --git a/changelogs/unreleased/33620-remove-events-from-notification_settings.yml b/changelogs/unreleased/33620-remove-events-from-notification_settings.yml new file mode 100644 index 00000000000..f5f3ef3fb82 --- /dev/null +++ b/changelogs/unreleased/33620-remove-events-from-notification_settings.yml @@ -0,0 +1,4 @@ +--- +title: Remove events column from notification settings table +merge_request: +author: diff --git a/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb b/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb new file mode 100644 index 00000000000..cd533391d8d --- /dev/null +++ b/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb @@ -0,0 +1,9 @@ +class RemoveEventsFromNotificationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + remove_column :notification_settings, :events, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 0abdb987b77..1a60589261a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170725145659) do +ActiveRecord::Schema.define(version: 20170728101014) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -981,7 +981,6 @@ ActiveRecord::Schema.define(version: 20170725145659) do t.integer "level", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.text "events" t.boolean "new_note" t.boolean "new_issue" t.boolean "reopen_issue" diff --git a/spec/factories/notification_settings.rb b/spec/factories/notification_settings.rb index b5e96d18b8f..ee41997e41a 100644 --- a/spec/factories/notification_settings.rb +++ b/spec/factories/notification_settings.rb @@ -3,6 +3,5 @@ FactoryGirl.define do source factory: :empty_project user level 3 - events [] end end diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb index 07e296424ca..2a0d102d3fe 100644 --- a/spec/models/notification_setting_spec.rb +++ b/spec/models/notification_setting_spec.rb @@ -63,24 +63,20 @@ RSpec.describe NotificationSetting do end end - describe 'event_enabled?' do + describe '#event_enabled?' do before do subject.update!(user: create(:user)) end context 'for an event with a matching column name' do - before do - subject.update!(events: { new_note: true }.to_json) - end - it 'returns the value of the column' do - subject.update!(new_note: false) + subject.update!(new_note: true) - expect(subject.event_enabled?(:new_note)).to be(false) + expect(subject.event_enabled?(:new_note)).to be(true) end context 'when the column has a nil value' do - it 'returns the value from the events hash' do + it 'returns false' do expect(subject.event_enabled?(:new_note)).to be(false) end end diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb index f619b7e6eaf..d0e7a82e607 100644 --- a/spec/requests/api/notification_settings_spec.rb +++ b/spec/requests/api/notification_settings_spec.rb @@ -72,8 +72,8 @@ describe API::NotificationSettings do expect(response).to have_http_status(200) expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level) - expect(json_response['events']['new_note']).to eq(true) - expect(json_response['events']['new_issue']).to eq(false) + expect(json_response['events']['new_note']).to be_truthy + expect(json_response['events']['new_issue']).to be_falsey end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 85335643921..609998d6e9c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -129,10 +129,14 @@ RSpec.configure do |config| config.before(:example, :migration) do ActiveRecord::Migrator .migrate(migrations_paths, previous_migration.version) + + ActiveRecord::Base.descendants.each(&:reset_column_information) end config.after(:example, :migration) do ActiveRecord::Migrator.migrate(migrations_paths) + + ActiveRecord::Base.descendants.each(&:reset_column_information) end config.around(:each, :nested_groups) do |example| From b535be35ae8bbb003f550e51d22a8e3b4c46b07c Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Mon, 31 Jul 2017 16:03:39 +0530 Subject: [PATCH 023/108] Group Identicon for groups without avatars --- .../groups/components/group_identicon.vue | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 app/assets/javascripts/groups/components/group_identicon.vue diff --git a/app/assets/javascripts/groups/components/group_identicon.vue b/app/assets/javascripts/groups/components/group_identicon.vue new file mode 100644 index 00000000000..4e0898b6c44 --- /dev/null +++ b/app/assets/javascripts/groups/components/group_identicon.vue @@ -0,0 +1,44 @@ + + + From 84568997431d778644d41dd3e3a4f98eee850935 Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Mon, 31 Jul 2017 16:04:25 +0530 Subject: [PATCH 024/108] Use GroupIdenticon for missing avatars --- .../javascripts/groups/components/group_item.vue | 13 +++++++++++++ app/assets/javascripts/groups/index.js | 2 ++ 2 files changed, 15 insertions(+) diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index b1db34b9c50..c704aa65df2 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -92,6 +92,13 @@ export default { hasGroups() { return Object.keys(this.group.subGroups).length > 0; }, + hasAvatar() { + if (this.group.avatarUrl) { + return this.group.avatarUrl.indexOf('/assets/no_group_avatar') === -1; + } else { + return false; + } + }, }, }; @@ -194,9 +201,15 @@ export default { +
{ Vue.component('groups-component', GroupsComponent); Vue.component('group-folder', GroupFolder); Vue.component('group-item', GroupItem); + Vue.component('group-identicon', GroupIdenticon); // eslint-disable-next-line no-new new Vue({ From ec0ea51f67724be9ae2a81827abeec9022cd2f46 Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Mon, 31 Jul 2017 16:04:45 +0530 Subject: [PATCH 025/108] Add styles for Identicons for Groups --- app/assets/stylesheets/framework/lists.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 868e65a8f46..ab754f4a492 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -369,6 +369,10 @@ ul.indent-list { background-color: $row-hover; cursor: pointer; } + + .avatar-container > a { + width: 100%; + } } } From 339469099d64e9f87cccb375cc31c0158a99480f Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Mon, 31 Jul 2017 16:05:06 +0530 Subject: [PATCH 026/108] Update tests --- spec/javascripts/groups/groups_spec.js | 15 +++++++++++++++ spec/javascripts/groups/mock_data.js | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/groups/groups_spec.js b/spec/javascripts/groups/groups_spec.js index aaffb56fa94..7e38b49c792 100644 --- a/spec/javascripts/groups/groups_spec.js +++ b/spec/javascripts/groups/groups_spec.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import eventHub from '~/groups/event_hub'; import groupFolderComponent from '~/groups/components/group_folder.vue'; import groupItemComponent from '~/groups/components/group_item.vue'; +import groupIdenticonComponent from '~/groups/components/group_identicon.vue'; import groupsComponent from '~/groups/components/groups.vue'; import GroupsStore from '~/groups/stores/groups_store'; import { groupsData } from './mock_data'; @@ -14,6 +15,7 @@ describe('Groups Component', () => { beforeEach((done) => { Vue.component('group-folder', groupFolderComponent); + Vue.component('group-identicon', groupIdenticonComponent); Vue.component('group-item', groupItemComponent); store = new GroupsStore(); @@ -64,6 +66,19 @@ describe('Groups Component', () => { expect(lists[2].querySelector('#group-1120').textContent).toContain(groups.id1119.subGroups.id1120.name); }); + it('should render group identicon when group avatar is not present', () => { + const avatar = component.$el.querySelector('#group-12 .avatar-container .avatar'); + expect(avatar.nodeName).toBe('DIV'); + expect(avatar.classList.contains('identicon')).toBeTruthy(); + expect(avatar.getAttribute('style').indexOf('background-color') > -1).toBeTruthy(); + }); + + it('should render group avatar when group avatar is present', () => { + const avatar = component.$el.querySelector('#group-1120 .avatar-container .avatar'); + expect(avatar.nodeName).toBe('IMG'); + expect(avatar.classList.contains('identicon')).toBeFalsy(); + }); + it('should remove prefix of parent group', () => { expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4'); }); diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js index b3f5d791b89..9e1f414514a 100644 --- a/spec/javascripts/groups/mock_data.js +++ b/spec/javascripts/groups/mock_data.js @@ -71,7 +71,7 @@ const group21 = { path: 'chef', description: 'foo', visibility: 'public', - avatar_url: null, + avatar_url: '/uploads/-/system/group/avatar/2/GitLab.png', web_url: 'http://localhost:3000/groups/devops/chef', group_path: '/devops/chef', full_name: 'devops / chef', From ce83e5635d1903cfadf4e2d9a7088b505f6c28ff Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 28 Jul 2017 23:02:21 +0200 Subject: [PATCH 027/108] add kube_namespace and standardize common variables for additional metrics queries --- .../queries/additional_metrics_deployment_query.rb | 12 ++++++------ .../queries/additional_metrics_environment_query.rb | 12 ++++++------ .../prometheus/queries/query_additional_metrics.rb | 12 ++++++++++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb index 67c69d9ccf3..51d934b9ae2 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb @@ -6,12 +6,12 @@ module Gitlab def query(deployment_id) Deployment.find_by(id: deployment_id).try do |deployment| - query_context = { - environment_slug: deployment.environment.slug, - environment_filter: %{container_name!="POD",environment="#{deployment.environment.slug}"}, - timeframe_start: (deployment.created_at - 30.minutes).to_f, - timeframe_end: (deployment.created_at + 30.minutes).to_f - } + query_context = common_query_context(deployment.environment).merge( + { + timeframe_start: (deployment.created_at - 30.minutes).to_f, + timeframe_end: (deployment.created_at + 30.minutes).to_f + } + ) query_metrics(query_context) end diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb index b5a679ddd79..9f798f5b892 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb @@ -6,12 +6,12 @@ module Gitlab def query(environment_id) Environment.find_by(id: environment_id).try do |environment| - query_context = { - environment_slug: environment.slug, - environment_filter: %{container_name!="POD",environment="#{environment.slug}"}, - timeframe_start: 8.hours.ago.to_f, - timeframe_end: Time.now.to_f - } + query_context = common_query_context(environment).merge( + { + timeframe_start: 8.hours.ago.to_f, + timeframe_end: Time.now.to_f + } + ) query_metrics(query_context) end diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb index e44be770544..003e6aa6c87 100644 --- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb +++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb @@ -67,6 +67,18 @@ module Gitlab result.select { |group| group.metrics.any? } end + + def common_query_context(environment) + variables = { + ci_environment_slug: environment.slug, + kube_namespace: environment.project.kubernetes_service.actual_namespace, + }.flat_map { |k, v| [[k, v], [k.upcase, v]] }.to_h + + macros = { + environment_filter: %{container_name!="POD",environment="#{environment.slug}"} + } + variables.merge(macros) + end end end end From 6df5bd8b848322aa3e2ce5fd8cad0e2a20b06000 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 31 Jul 2017 13:29:00 +0200 Subject: [PATCH 028/108] Context handling and tests cleanup + simple test kube_namespace context test --- .../project_services/prometheus_service.rb | 5 ++++ .../queries/query_additional_metrics.rb | 20 ++++++------- ...dditional_metrics_deployment_query_spec.rb | 11 ++----- ...ditional_metrics_environment_query_spec.rb | 10 ++----- .../additional_metrics_shared_examples.rb | 29 +++++++++++++++++++ 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb index 217f753f05f..73cf4e97b56 100644 --- a/app/models/project_services/prometheus_service.rb +++ b/app/models/project_services/prometheus_service.rb @@ -75,6 +75,11 @@ class PrometheusService < MonitoringService with_reactive_cache(Gitlab::Prometheus::Queries::MatchedMetricsQuery.name, &:itself) end + def with_reactive_cache(name, *args, &block) + vals = args.map(&:to_s) + yield calculate_reactive_cache(name, *vals) + end + # Cache metrics for specific environment def calculate_reactive_cache(query_class_name, *args) return unless active? && project && !project.pending_delete? diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb index 003e6aa6c87..5827c117556 100644 --- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb +++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb @@ -42,15 +42,17 @@ module Gitlab end def process_query(context, query) - query_with_result = query.dup + query = query.dup result = if query.key?(:query_range) - client_query_range(query[:query_range] % context, start: context[:timeframe_start], stop: context[:timeframe_end]) + query[:query_range] %= context + client_query_range(query[:query_range], start: context[:timeframe_start], stop: context[:timeframe_end]) else - client_query(query[:query] % context, time: context[:timeframe_end]) + query[:query] %= context + client_query(query[:query], time: context[:timeframe_end]) end - query_with_result[:result] = result&.map(&:deep_symbolize_keys) - query_with_result + query[:result] = result&.map(&:deep_symbolize_keys) + query end def available_metrics @@ -69,15 +71,11 @@ module Gitlab end def common_query_context(environment) - variables = { + { ci_environment_slug: environment.slug, - kube_namespace: environment.project.kubernetes_service.actual_namespace, - }.flat_map { |k, v| [[k, v], [k.upcase, v]] }.to_h - - macros = { + kube_namespace: environment.project.kubernetes_service&.actual_namespace || '', environment_filter: %{container_name!="POD",environment="#{environment.slug}"} } - variables.merge(macros) end end end diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb index e42e034f4fb..c7169717fc1 100644 --- a/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb @@ -1,19 +1,14 @@ require 'spec_helper' describe Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery do - include Prometheus::MetricBuilders - - let(:client) { double('prometheus_client') } - let(:environment) { create(:environment, slug: 'environment-slug') } - let(:deployment) { create(:deployment, environment: environment) } - - subject(:query_result) { described_class.new(client).query(deployment.id) } - around do |example| Timecop.freeze(Time.local(2008, 9, 1, 12, 0, 0)) { example.run } end include_examples 'additional metrics query' do + let(:deployment) { create(:deployment, environment: environment) } + let(:query_params) { [deployment.id] } + it 'queries using specific time' do expect(client).to receive(:query_range).with(anything, start: (deployment.created_at - 30.minutes).to_f, diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb index e9fd66d45fe..a02d2a90b97 100644 --- a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb @@ -1,18 +1,14 @@ require 'spec_helper' describe Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery do - include Prometheus::MetricBuilders - - let(:client) { double('prometheus_client') } - let(:environment) { create(:environment, slug: 'environment-slug') } - - subject(:query_result) { described_class.new(client).query(environment.id) } - around do |example| Timecop.freeze { example.run } end include_examples 'additional metrics query' do + let(:query_result) { described_class.new(client).query(environment.id) } + let(:query_params) { [environment.id] } + it 'queries using specific time' do expect(client).to receive(:query_range).with(anything, start: 8.hours.ago.to_f, stop: Time.now.to_f) expect(query_result).not_to be_nil diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index 016e16fc8d4..fcb5c315b98 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -10,12 +10,39 @@ RSpec.shared_examples 'additional metrics query' do [{ 'metric': {}, 'values': [[1488758662.506, '0.00002996364761904785'], [1488758722.506, '0.00003090239047619091']] }] end + let(:client) { double('prometheus_client') } + let(:environment) { create(:environment, slug: 'environment-slug') } + before do allow(client).to receive(:label_values).and_return(metric_names) allow(metric_group_class).to receive(:all).and_return([simple_metric_group(metrics: [simple_metric])]) end + context 'metrics rendering' do + subject! { described_class.new(client) } + + before do + + end + + describe 'project has kubernetes service' do + let(:project) { create(:kubernetes_project) } + let(:environment) { create(:environment, slug: 'environment-slug', project: project) } + let(:kube_namespace) { project.kubernetes_service.actual_namespace } + + it 'query context contains kube namespace' do + expect(subject).to receive(:query_metrics).with( + hash_including( + kube_namespace: kube_namespace) + ) + subject.query(*query_params) + end + end + end + context 'with one group where two metrics is found' do + let(:query_result) { described_class.new(client).query(*query_params) } + before do allow(metric_group_class).to receive(:all).and_return([simple_metric_group]) end @@ -50,7 +77,9 @@ RSpec.shared_examples 'additional metrics query' do end context 'with two groups with one metric each' do + let(:query_result) { described_class.new(client).query(*query_params) } let(:metrics) { [simple_metric(queries: [simple_query])] } + before do allow(metric_group_class).to receive(:all).and_return( [ From 48778ac58973fa7cab09deaaf2e93806aa37113d Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 31 Jul 2017 15:26:38 +0200 Subject: [PATCH 029/108] Tests for query context variables --- .../project_services/prometheus_service.rb | 5 -- ...ditional_metrics_environment_query_spec.rb | 2 +- .../additional_metrics_shared_examples.rb | 47 ++++++++++++++----- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb index 73cf4e97b56..217f753f05f 100644 --- a/app/models/project_services/prometheus_service.rb +++ b/app/models/project_services/prometheus_service.rb @@ -75,11 +75,6 @@ class PrometheusService < MonitoringService with_reactive_cache(Gitlab::Prometheus::Queries::MatchedMetricsQuery.name, &:itself) end - def with_reactive_cache(name, *args, &block) - vals = args.map(&:to_s) - yield calculate_reactive_cache(name, *vals) - end - # Cache metrics for specific environment def calculate_reactive_cache(query_class_name, *args) return unless active? && project && !project.pending_delete? diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb index a02d2a90b97..5a88b23aa82 100644 --- a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb @@ -6,11 +6,11 @@ describe Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery do end include_examples 'additional metrics query' do - let(:query_result) { described_class.new(client).query(environment.id) } let(:query_params) { [environment.id] } it 'queries using specific time' do expect(client).to receive(:query_range).with(anything, start: 8.hours.ago.to_f, stop: Time.now.to_f) + expect(query_result).not_to be_nil end end diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index fcb5c315b98..174297af158 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -11,6 +11,7 @@ RSpec.shared_examples 'additional metrics query' do end let(:client) { double('prometheus_client') } + let(:query_result) { described_class.new(client).query(*query_params) } let(:environment) { create(:environment, slug: 'environment-slug') } before do @@ -18,31 +19,52 @@ RSpec.shared_examples 'additional metrics query' do allow(metric_group_class).to receive(:all).and_return([simple_metric_group(metrics: [simple_metric])]) end - context 'metrics rendering' do + context 'metrics query context' do subject! { described_class.new(client) } - before do + shared_examples 'query context containing environment slug and filter' do + it 'query context contains ci_environment_slug' do + expect(subject).to receive(:query_metrics).with(hash_including(ci_environment_slug: environment.slug)) - end + subject.query(*query_params) + end - describe 'project has kubernetes service' do - let(:project) { create(:kubernetes_project) } - let(:environment) { create(:environment, slug: 'environment-slug', project: project) } - let(:kube_namespace) { project.kubernetes_service.actual_namespace } - - it 'query context contains kube namespace' do + it 'query context contains environment filter' do expect(subject).to receive(:query_metrics).with( hash_including( - kube_namespace: kube_namespace) + environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\"" + ) ) subject.query(*query_params) end end + + describe 'project has Kubernetes service' do + let(:project) { create(:kubernetes_project) } + let(:environment) { create(:environment, slug: 'environment-slug', project: project) } + let(:kube_namespace) { project.kubernetes_service.actual_namespace } + + it_behaves_like 'query context containing environment slug and filter' + + it 'query context contains kube_namespace' do + expect(subject).to receive(:query_metrics).with(hash_including(kube_namespace: kube_namespace)) + + subject.query(*query_params) + end + end + + describe 'project without Kubernetes service' do + it_behaves_like 'query context containing environment slug and filter' + + it 'query context contains empty kube_namespace' do + expect(subject).to receive(:query_metrics).with(hash_including(kube_namespace: '')) + + subject.query(*query_params) + end + end end context 'with one group where two metrics is found' do - let(:query_result) { described_class.new(client).query(*query_params) } - before do allow(metric_group_class).to receive(:all).and_return([simple_metric_group]) end @@ -77,7 +99,6 @@ RSpec.shared_examples 'additional metrics query' do end context 'with two groups with one metric each' do - let(:query_result) { described_class.new(client).query(*query_params) } let(:metrics) { [simple_metric(queries: [simple_query])] } before do From 6232543db3a784744e1cb03bb6a1a1648bb84b15 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 31 Jul 2017 16:31:07 +0200 Subject: [PATCH 030/108] Add changelog: add support for kube_namespace in Metrics queries + small whitespace fix to better separate tests --- .../pawel-add_more_variables_to_additional_metrics-35267.yml | 4 ++++ spec/support/prometheus/additional_metrics_shared_examples.rb | 1 + 2 files changed, 5 insertions(+) create mode 100644 changelogs/unreleased/pawel-add_more_variables_to_additional_metrics-35267.yml diff --git a/changelogs/unreleased/pawel-add_more_variables_to_additional_metrics-35267.yml b/changelogs/unreleased/pawel-add_more_variables_to_additional_metrics-35267.yml new file mode 100644 index 00000000000..c1e831306df --- /dev/null +++ b/changelogs/unreleased/pawel-add_more_variables_to_additional_metrics-35267.yml @@ -0,0 +1,4 @@ +--- +title: Add support for kube_namespace in Metrics queries +merge_request: 16169 +author: diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index 174297af158..18b02920104 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -35,6 +35,7 @@ RSpec.shared_examples 'additional metrics query' do environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\"" ) ) + subject.query(*query_params) end end From 7b18c424644f98f04e527dd0c5e10b1c23f5bae4 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 31 Jul 2017 17:19:25 +0200 Subject: [PATCH 031/108] Remove unused (?) code --- lib/gitlab/diff/file.rb | 18 ------------------ spec/lib/gitlab/diff/file_spec.rb | 8 -------- 2 files changed, 26 deletions(-) diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index d2863a4da71..6d7de52cb80 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -79,13 +79,6 @@ module Gitlab @new_content_sha = refs&.head_sha end - def new_content_commit - return @new_content_commit if defined?(@new_content_commit) - - sha = new_content_commit - @new_content_commit = repository.commit(sha) if sha - end - def old_content_sha return if new_file? return @old_content_sha if defined?(@old_content_sha) @@ -94,13 +87,6 @@ module Gitlab @old_content_sha = refs&.base_sha end - def old_content_commit - return @old_content_commit if defined?(@old_content_commit) - - sha = old_content_sha - @old_content_commit = repository.commit(sha) if sha - end - def new_blob return @new_blob if defined?(@new_blob) @@ -123,10 +109,6 @@ module Gitlab new_content_sha || old_content_sha end - def content_commit - new_content_commit || old_content_commit - end - def blob new_blob || old_blob end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index cd2fa98b14c..d3d841b0668 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -47,14 +47,6 @@ describe Gitlab::Diff::File do end end - describe '#old_content_commit' do - it 'returns base commit' do - old_content_commit = diff_file.old_content_commit - - expect(old_content_commit.id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') - end - end - describe '#old_blob' do it 'returns blob of commit of base commit' do old_data = diff_file.old_blob.data From 7b5a96b53c801d7fabef529481a525f3ab2ed2e3 Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Mon, 31 Jul 2017 08:43:27 -0700 Subject: [PATCH 032/108] Fix LDAP documentation and example config --- config/gitlab.yml.example | 2 +- doc/administration/auth/ldap.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index e9bf2df490f..73a68c6da1b 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -282,7 +282,7 @@ production: &base # # Example: '/etc/ca.pem' # - ca_cert: '' + ca_file: '' # Specifies the SSL version for OpenSSL to use, if the OpenSSL default # is not appropriate. diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index a7395e03d1c..425c924cdf2 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -96,7 +96,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # # Example: '/etc/ca.pem' # - ca_cert: '' + ca_file: '' # Specifies the SSL version for OpenSSL to use, if the OpenSSL default # is not appropriate. @@ -259,9 +259,9 @@ group you can use the following syntax: (memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com) ``` -Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at +Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx. Support for -nested members in the user filter should not be confused with +nested members in the user filter should not be confused with [group sync nested groups support (EE only)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#supported-ldap-group-types-attributes). Please note that GitLab does not support the custom filter syntax used by From 15911ef32792b19daf192a0123a8a44ff393eb02 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 31 Jul 2017 17:33:57 +0100 Subject: [PATCH 033/108] Fix group milestone path on issuable sidebar --- app/helpers/gitlab_routing_helper.rb | 14 ++++- app/views/shared/issuable/_sidebar.html.haml | 2 +- ...oup-milestone-link-in-issuable-sidebar.yml | 4 ++ spec/factories/milestones.rb | 2 +- spec/helpers/gitlab_routing_helper_spec.rb | 53 +++++++++++++++---- 5 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 0517a699ae0..1f7db9b2eb8 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -48,7 +48,11 @@ module GitlabRoutingHelper end def milestone_path(entity, *args) - project_milestone_path(entity.project, entity, *args) + if entity.is_group_milestone? + group_milestone_path(entity.group, entity, *args) + elsif entity.is_project_milestone? + project_milestone_path(entity.project, entity, *args) + end end def issue_url(entity, *args) @@ -63,6 +67,14 @@ module GitlabRoutingHelper project_pipeline_url(pipeline.project, pipeline.id, *args) end + def milestone_url(entity, *args) + if entity.is_group_milestone? + group_milestone_url(entity.group, entity, *args) + elsif entity.is_project_milestone? + project_milestone_url(entity.project, entity, *args) + end + end + def pipeline_job_url(pipeline, build, *args) project_job_url(pipeline.project, build.id, *args) end diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index b08267357e5..e7510c1d1ec 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -37,7 +37,7 @@ = link_to 'Edit', '#', class: 'edit-link pull-right' .value.hide-collapsed - if issuable.milestone - = link_to issuable.milestone.title, project_milestone_path(@project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } + = link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } - else %span.no-value None diff --git a/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml b/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml new file mode 100644 index 00000000000..1558e575e6d --- /dev/null +++ b/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml @@ -0,0 +1,4 @@ +--- +title: Fix links to group milestones from issue and merge request sidebar +merge_request: +author: diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb index 113665ff11b..b5e2ec60072 100644 --- a/spec/factories/milestones.rb +++ b/spec/factories/milestones.rb @@ -17,7 +17,7 @@ FactoryGirl.define do state "closed" end - after(:build) do |milestone, evaluator| + after(:build, :stub) do |milestone, evaluator| if evaluator.group milestone.group = evaluator.group elsif evaluator.group_id diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb index 717ac1962d1..9aaed0edf87 100644 --- a/spec/helpers/gitlab_routing_helper_spec.rb +++ b/spec/helpers/gitlab_routing_helper_spec.rb @@ -1,6 +1,9 @@ require 'spec_helper' describe GitlabRoutingHelper do + let(:project) { build_stubbed(:empty_project) } + let(:group) { build_stubbed(:group) } + describe 'Project URL helpers' do describe '#project_member_path' do let(:project_member) { create(:project_member) } @@ -9,14 +12,10 @@ describe GitlabRoutingHelper do end describe '#request_access_project_members_path' do - let(:project) { build_stubbed(:empty_project) } - it { expect(request_access_project_members_path(project)).to eq request_access_project_project_members_path(project) } end describe '#leave_project_members_path' do - let(:project) { build_stubbed(:empty_project) } - it { expect(leave_project_members_path(project)).to eq leave_project_project_members_path(project) } end @@ -35,8 +34,6 @@ describe GitlabRoutingHelper do describe 'Group URL helpers' do describe '#group_members_url' do - let(:group) { build_stubbed(:group) } - it { expect(group_members_url(group)).to eq group_group_members_url(group) } end @@ -47,14 +44,10 @@ describe GitlabRoutingHelper do end describe '#request_access_group_members_path' do - let(:group) { build_stubbed(:group) } - it { expect(request_access_group_members_path(group)).to eq request_access_group_group_members_path(group) } end describe '#leave_group_members_path' do - let(:group) { build_stubbed(:group) } - it { expect(leave_group_members_path(group)).to eq leave_group_group_members_path(group) } end @@ -70,4 +63,44 @@ describe GitlabRoutingHelper do it { expect(resend_invite_group_member_path(group_member)).to eq resend_invite_group_group_member_path(group_member.source, group_member) } end end + + describe '#milestone_path' do + context 'for a group milestone' do + let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) } + + it 'links to the group milestone page' do + expect(milestone_path(milestone)) + .to eq(group_milestone_path(group, milestone)) + end + end + + context 'for a project milestone' do + let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) } + + it 'links to the project milestone page' do + expect(milestone_path(milestone)) + .to eq(project_milestone_path(project, milestone)) + end + end + end + + describe '#milestone_url' do + context 'for a group milestone' do + let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) } + + it 'links to the group milestone page' do + expect(milestone_url(milestone)) + .to eq(group_milestone_url(group, milestone)) + end + end + + context 'for a project milestone' do + let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) } + + it 'links to the project milestone page' do + expect(milestone_url(milestone)) + .to eq(project_milestone_url(project, milestone)) + end + end + end end From 82b5fe51eccc70eaa083822107b0aa620dab1b0b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 31 Jul 2017 19:57:37 +0100 Subject: [PATCH 034/108] Uses jQuery to scroll since setting document.body.scrollTop does not work in firefox --- app/assets/javascripts/build.js | 19 ++++++++++--------- changelogs/unreleased/34492-firefox-job.yml | 4 ++++ 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/34492-firefox-job.yml diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 1dfa064acfd..b3d3bbcf84f 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -64,7 +64,7 @@ window.Build = (function () { $(window) .off('scroll') .on('scroll', () => { - const contentHeight = this.$buildTraceOutput.prop('scrollHeight'); + const contentHeight = this.$buildTraceOutput.height(); if (contentHeight > this.windowSize) { // means the user did not scroll, the content was updated. this.windowSize = contentHeight; @@ -105,16 +105,17 @@ window.Build = (function () { }; Build.prototype.canScroll = function () { - return document.body.scrollHeight > window.innerHeight; + return $(document).height() > $(window).height(); }; Build.prototype.toggleScroll = function () { - const currentPosition = document.body.scrollTop; - const windowHeight = window.innerHeight; + const currentPosition = $(document).scrollTop(); + const scrollHeight = $(document).height(); + const windowHeight = $(window).height(); if (this.canScroll()) { if (currentPosition > 0 && - (document.body.scrollHeight - currentPosition !== windowHeight)) { + (scrollHeight - currentPosition !== windowHeight)) { // User is in the middle of the log this.toggleDisableButton(this.$scrollTopBtn, false); @@ -124,7 +125,7 @@ window.Build = (function () { this.toggleDisableButton(this.$scrollTopBtn, true); this.toggleDisableButton(this.$scrollBottomBtn, false); - } else if (document.body.scrollHeight - currentPosition === windowHeight) { + } else if (scrollHeight - currentPosition === windowHeight) { // User is at the bottom of the build log. this.toggleDisableButton(this.$scrollTopBtn, false); @@ -137,7 +138,7 @@ window.Build = (function () { }; Build.prototype.scrollDown = function () { - document.body.scrollTop = document.body.scrollHeight; + $(document).scrollTop($(document).height()); }; Build.prototype.scrollToBottom = function () { @@ -147,7 +148,7 @@ window.Build = (function () { }; Build.prototype.scrollToTop = function () { - document.body.scrollTop = 0; + $(document).scrollTop(0); this.hasBeenScrolled = true; this.toggleScroll(); }; @@ -178,7 +179,7 @@ window.Build = (function () { this.state = log.state; } - this.windowSize = this.$buildTraceOutput.prop('scrollHeight'); + this.windowSize = this.$buildTraceOutput.height(); if (log.append) { this.$buildTraceOutput.append(log.html); diff --git a/changelogs/unreleased/34492-firefox-job.yml b/changelogs/unreleased/34492-firefox-job.yml new file mode 100644 index 00000000000..881b8f649ea --- /dev/null +++ b/changelogs/unreleased/34492-firefox-job.yml @@ -0,0 +1,4 @@ +--- +title: Use jQuery to control scroll behavior in job log for cross browser consistency +merge_request: +author: From 618c3e7d2a525543f8fbc5fcfd81e9bbc8de60c1 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Mon, 10 Jul 2017 00:05:21 +0900 Subject: [PATCH 035/108] Bump rubocop to 0.49.1 and rubocop-rspec to 1.15.1 --- .rubocop.yml | 454 ++++++++++-------- .rubocop_todo.yml | 225 ++++----- Gemfile | 4 +- Gemfile.lock | 12 +- ...-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml | 4 + 5 files changed, 384 insertions(+), 315 deletions(-) create mode 100644 changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml diff --git a/.rubocop.yml b/.rubocop.yml index f661a29d9d1..3e4275c271d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,7 @@ inherit_from: .rubocop_todo.yml AllCops: TargetRubyVersion: 2.3 + TargetRailsVersion: 4.2 # Cop names are not d§splayed in offense messages by default. Change behavior # by overriding DisplayCopNames, or by giving the -D/--display-cop-names # option. @@ -29,12 +30,221 @@ AllCops: Bundler/OrderedGems: Enabled: false -# Style ####################################################################### +# Layout ###################################################################### # Check indentation of private/protected visibility modifiers. -Style/AccessModifierIndentation: +Layout/AccessModifierIndentation: Enabled: true +# Align the elements of an array literal if they span more than one line. +Layout/AlignArray: + Enabled: true + +# Align the elements of a hash literal if they span more than one line. +Layout/AlignHash: + Enabled: true + +# Here we check if the parameters on a multi-line method call or +# definition are aligned. +Layout/AlignParameters: + Enabled: false + +# Put end statement of multiline block on its own line. +Layout/BlockEndNewline: + Enabled: true + +# Indentation of when in a case/when/[else/]end. +Layout/CaseIndentation: + Enabled: true + +# Indentation of comments. +Layout/CommentIndentation: + Enabled: true + +# Multi-line method chaining should be done with leading dots. +Layout/DotPosition: + Enabled: true + EnforcedStyle: leading + +# Align elses and elsifs correctly. +Layout/ElseAlignment: + Enabled: true + +# Add an empty line after magic comments to separate them from code. +Layout/EmptyLineAfterMagicComment: + Enabled: false + +# Use empty lines between defs. +Layout/EmptyLineBetweenDefs: + Enabled: true + +# Don't use several empty lines in a row. +Layout/EmptyLines: + Enabled: true + +# Keep blank lines around access modifiers. +Layout/EmptyLinesAroundAccessModifier: + Enabled: true + +# Keeps track of empty lines around block bodies. +Layout/EmptyLinesAroundBlockBody: + Enabled: true + +# Keeps track of empty lines around class bodies. +Layout/EmptyLinesAroundClassBody: + Enabled: true + +# Keeps track of empty lines around exception handling keywords. +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Enabled: false + +# Keeps track of empty lines around method bodies. +Layout/EmptyLinesAroundMethodBody: + Enabled: true + +# Keeps track of empty lines around module bodies. +Layout/EmptyLinesAroundModuleBody: + Enabled: true + +# Use Unix-style line endings. +Layout/EndOfLine: + Enabled: true + +# Checks for a line break before the first parameter in a multi-line method +# parameter definition. +Layout/FirstMethodParameterLineBreak: + Enabled: true + +# Keep indentation straight. +Layout/IndentationConsistency: + Enabled: true + +# Use 2 spaces for indentation. +Layout/IndentationWidth: + Enabled: true + +# Checks the indentation of the first line of the right-hand-side of a +# multi-line assignment. +Layout/IndentAssignment: + Enabled: true + +# This cops checks the indentation of the here document bodies. +Layout/IndentHeredoc: + Enabled: false + +# Comments should start with a space. +Layout/LeadingCommentSpace: + Enabled: true + +# Checks that the closing brace in an array literal is either on the same line +# as the last array element, or a new line. +Layout/MultilineArrayBraceLayout: + Enabled: true + EnforcedStyle: symmetrical + +# Ensures newlines after multiline block do statements. +Layout/MultilineBlockLayout: + Enabled: true + +# Checks that the closing brace in a hash literal is either on the same line as +# the last hash element, or a new line. +Layout/MultilineHashBraceLayout: + Enabled: true + EnforcedStyle: symmetrical + +# Checks that the closing brace in a method call is either on the same line as +# the last method argument, or a new line. +Layout/MultilineMethodCallBraceLayout: + Enabled: false + EnforcedStyle: symmetrical + +# Checks indentation of method calls with the dot operator that span more than +# one line. +Layout/MultilineMethodCallIndentation: + Enabled: false + +# Checks that the closing brace in a method definition is symmetrical with +# respect to the opening brace and the method parameters. +Layout/MultilineMethodDefinitionBraceLayout: + Enabled: false + +# Checks indentation of binary operations that span more than one line. +Layout/MultilineOperationIndentation: + Enabled: true + EnforcedStyle: indented + +# Use spaces after colons. +Layout/SpaceAfterColon: + Enabled: true + +# Use spaces after commas. +Layout/SpaceAfterComma: + Enabled: true + +# Do not put a space between a method name and the opening parenthesis in a +# method definition. +Layout/SpaceAfterMethodName: + Enabled: true + +# Tracks redundant space after the ! operator. +Layout/SpaceAfterNot: + Enabled: true + +# Use spaces after semicolons. +Layout/SpaceAfterSemicolon: + Enabled: true + +# Use space around equals in parameter default +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: true + +# Use a space around keywords if appropriate. +Layout/SpaceAroundKeyword: + Enabled: true + +# Use a single space around operators. +Layout/SpaceAroundOperators: + Enabled: true + +# No spaces before commas. +Layout/SpaceBeforeComma: + Enabled: true + +# Checks for missing space between code and a comment on the same line. +Layout/SpaceBeforeComment: + Enabled: true + +# No spaces before semicolons. +Layout/SpaceBeforeSemicolon: + Enabled: true + +# Checks for spaces inside square brackets. +Layout/SpaceInsideBrackets: + Enabled: true + +# Use spaces inside hash literal braces - or don't. +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + +# No spaces inside range literals. +Layout/SpaceInsideRangeLiteral: + Enabled: true + +# Checks for padding/surrounding spaces inside string interpolation. +Layout/SpaceInsideStringInterpolation: + EnforcedStyle: no_space + Enabled: true + +# No hard tabs. +Layout/Tab: + Enabled: true + +# Checks trailing blank lines and final newline. +Layout/TrailingBlankLines: + Enabled: true + +# Style ####################################################################### + # Check the naming of accessor methods for get_/set_. Style/AccessorMethodName: Enabled: false @@ -44,19 +254,6 @@ Style/Alias: EnforcedStyle: prefer_alias_method Enabled: true -# Align the elements of an array literal if they span more than one line. -Style/AlignArray: - Enabled: true - -# Align the elements of a hash literal if they span more than one line. -Style/AlignHash: - Enabled: true - -# Here we check if the parameters on a multi-line method call or -# definition are aligned. -Style/AlignParameters: - Enabled: false - # Whether `and` and `or` are banned only in conditionals (conditionals) # or completely (always). Style/AndOr: @@ -91,10 +288,6 @@ Style/BlockComments: Style/BlockDelimiters: Enabled: true -# Put end statement of multiline block on its own line. -Style/BlockEndNewline: - Enabled: true - # This cop checks for braces around the last parameter in a method call # if the last parameter is a hash. Style/BracesAroundHashParameters: @@ -104,10 +297,6 @@ Style/BracesAroundHashParameters: Style/CaseEquality: Enabled: false -# Indentation of when in a case/when/[else/]end. -Style/CaseIndentation: - Enabled: true - # Checks for uses of character literals. Style/CharacterLiteral: Enabled: true @@ -142,10 +331,6 @@ Style/ColonMethodCall: Style/CommentAnnotation: Enabled: false -# Indentation of comments. -Style/CommentIndentation: - Enabled: true - # Check for `if` and `case` statements where each branch is used for # assignment to the same variable when using the return of the # condition can be used instead. @@ -164,57 +349,16 @@ Style/DefWithParentheses: Style/Documentation: Enabled: false -# Multi-line method chaining should be done with leading dots. -Style/DotPosition: - Enabled: true - EnforcedStyle: leading - # This cop checks for uses of double negation (!!) to convert something # to a boolean value. As this is both cryptic and usually redundant, it # should be avoided. Style/DoubleNegation: Enabled: false -# Align elses and elsifs correctly. -Style/ElseAlignment: - Enabled: true - -# Use empty lines between defs. -Style/EmptyLineBetweenDefs: - Enabled: true - -# Don't use several empty lines in a row. -Style/EmptyLines: - Enabled: true - -# Keep blank lines around access modifiers. -Style/EmptyLinesAroundAccessModifier: - Enabled: true - -# Keeps track of empty lines around block bodies. -Style/EmptyLinesAroundBlockBody: - Enabled: true - -# Keeps track of empty lines around class bodies. -Style/EmptyLinesAroundClassBody: - Enabled: true - -# Keeps track of empty lines around method bodies. -Style/EmptyLinesAroundMethodBody: - Enabled: true - -# Keeps track of empty lines around module bodies. -Style/EmptyLinesAroundModuleBody: - Enabled: true - # Avoid the use of END blocks. Style/EndBlock: Enabled: true -# Use Unix-style line endings. -Style/EndOfLine: - Enabled: true - # Favor the use of Fixnum#even? && Fixnum#odd? Style/EvenOdd: Enabled: true @@ -223,11 +367,6 @@ Style/EvenOdd: Style/FileName: Enabled: true -# Checks for a line break before the first parameter in a multi-line method -# parameter definition. -Style/FirstMethodParameterLineBreak: - Enabled: true - # Checks for flip flops. Style/FlipFlop: Enabled: true @@ -236,6 +375,10 @@ Style/FlipFlop: Style/For: Enabled: true +# Use a consistent style for format string tokens. +Style/FormatStringToken: + Enabled: false + # Checks if there is a magic comment to enforce string literals Style/FrozenStringLiteralComment: Enabled: false @@ -261,31 +404,19 @@ Style/IdenticalConditionalBranches: Style/IfWithSemicolon: Enabled: true -# Checks the indentation of the first line of the right-hand-side of a -# multi-line assignment. -Style/IndentAssignment: - Enabled: true - -# Keep indentation straight. -Style/IndentationConsistency: - Enabled: true - -# Use 2 spaces for indentation. -Style/IndentationWidth: - Enabled: true - # Use Kernel#loop for infinite loops. Style/InfiniteLoop: Enabled: true +# Use the inverse method instead of `!.method` +# if an inverse method is defined. +Style/InverseMethods: + Enabled: false + # Use lambda.call(...) instead of lambda.(...). Style/LambdaCall: Enabled: true -# Comments should start with a space. -Style/LeadingCommentSpace: - Enabled: true - # Checks if the method definitions have or don't have parentheses. Style/MethodDefParentheses: Enabled: true @@ -298,55 +429,23 @@ Style/MethodName: Style/ModuleFunction: Enabled: false -# Checks that the closing brace in an array literal is either on the same line -# as the last array element, or a new line. -Style/MultilineArrayBraceLayout: - Enabled: true - EnforcedStyle: symmetrical - # Avoid multi-line chains of blocks. Style/MultilineBlockChain: Enabled: true -# Ensures newlines after multiline block do statements. -Style/MultilineBlockLayout: - Enabled: true - -# Checks that the closing brace in a hash literal is either on the same line as -# the last hash element, or a new line. -Style/MultilineHashBraceLayout: - Enabled: true - EnforcedStyle: symmetrical - # Do not use then for multi-line if/unless. Style/MultilineIfThen: Enabled: true -# Checks that the closing brace in a method call is either on the same line as -# the last method argument, or a new line. -Style/MultilineMethodCallBraceLayout: - Enabled: false - EnforcedStyle: symmetrical - -# Checks indentation of method calls with the dot operator that span more than -# one line. -Style/MultilineMethodCallIndentation: - Enabled: false - -# Checks that the closing brace in a method definition is symmetrical with -# respect to the opening brace and the method parameters. -Style/MultilineMethodDefinitionBraceLayout: - Enabled: false - -# Checks indentation of binary operations that span more than one line. -Style/MultilineOperationIndentation: - Enabled: true - EnforcedStyle: indented - # Avoid multi-line `? :` (the ternary operator), use if/unless instead. Style/MultilineTernaryOperator: Enabled: true +# Avoid comparing a variable with multiple items in a conditional, +# use Array#include? instead. +Style/MultipleComparison: + Enabled: false + # This cop checks whether some constant value isn't a # mutable literal (e.g. array or hash). Style/MutableConstant: @@ -421,68 +520,6 @@ Style/SignalException: EnforcedStyle: only_raise Enabled: true -# Use spaces after colons. -Style/SpaceAfterColon: - Enabled: true - -# Use spaces after commas. -Style/SpaceAfterComma: - Enabled: true - -# Do not put a space between a method name and the opening parenthesis in a -# method definition. -Style/SpaceAfterMethodName: - Enabled: true - -# Tracks redundant space after the ! operator. -Style/SpaceAfterNot: - Enabled: true - -# Use spaces after semicolons. -Style/SpaceAfterSemicolon: - Enabled: true - -# Use space around equals in parameter default -Style/SpaceAroundEqualsInParameterDefault: - Enabled: true - -# Use a space around keywords if appropriate. -Style/SpaceAroundKeyword: - Enabled: true - -# Use a single space around operators. -Style/SpaceAroundOperators: - Enabled: true - -# No spaces before commas. -Style/SpaceBeforeComma: - Enabled: true - -# Checks for missing space between code and a comment on the same line. -Style/SpaceBeforeComment: - Enabled: true - -# No spaces before semicolons. -Style/SpaceBeforeSemicolon: - Enabled: true - -# Checks for spaces inside square brackets. -Style/SpaceInsideBrackets: - Enabled: true - -# Use spaces inside hash literal braces - or don't. -Style/SpaceInsideHashLiteralBraces: - Enabled: true - -# No spaces inside range literals. -Style/SpaceInsideRangeLiteral: - Enabled: true - -# Checks for padding/surrounding spaces inside string interpolation. -Style/SpaceInsideStringInterpolation: - EnforcedStyle: no_space - Enabled: true - # Check for the usage of parentheses around stabby lambda arguments. Style/StabbyLambdaParentheses: EnforcedStyle: require_parentheses @@ -498,13 +535,9 @@ Style/StringMethods: intern: to_sym Enabled: true -# No hard tabs. -Style/Tab: - Enabled: true - -# Checks trailing blank lines and final newline. -Style/TrailingBlankLines: - Enabled: true +# Use %i or %I for arrays of symbols. +Style/SymbolArray: + Enabled: false # This cop checks for trailing comma in array and hash literals. Style/TrailingCommaInLiteral: @@ -553,6 +586,10 @@ Style/WhileUntilModifier: Style/WordArray: Enabled: true +# Do not use literals as the first operand of a comparison. +Style/YodaCondition: + Enabled: false + # Use `proc` instead of `Proc.new`. Style/Proc: Enabled: true @@ -608,6 +645,11 @@ Metrics/PerceivedComplexity: # Lint ######################################################################## +# Checks for ambiguous block association with method when param passed without +# parentheses. +Lint/AmbiguousBlockAssociation: + Enabled: false + # Checks for ambiguous operators in the first argument of a method invocation # without parentheses. Lint/AmbiguousOperator: @@ -809,6 +851,10 @@ Lint/Void: # Performance ################################################################# +# Use `caller(n..n)` instead of `caller`. +Performance/Caller: + Enabled: false + # Use `casecmp` rather than `downcase ==`. Performance/Casecmp: Enabled: true @@ -883,6 +929,14 @@ Rails/ActionFilter: Enabled: true EnforcedStyle: action +# Check that models subclass ApplicationRecord. +Rails/ApplicationRecord: + Enabled: false + +# Enforce using `blank?` and `present?`. +Rails/Blank: + Enabled: false + # Checks the correct usage of date aware methods, such as `Date.today`, # `Date.current`, etc. Rails/Date: @@ -939,10 +993,18 @@ Rails/OutputSafety: Rails/PluralizationGrammar: Enabled: true +# Enforce using `blank?` and `present?`. +Rails/Present: + Enabled: false + # Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`. Rails/ReadWriteAttribute: Enabled: false +# Do not assign relative date to constants. +Rails/RelativeDateConstant: + Enabled: false + # Checks the arguments of ActiveRecord scopes. Rails/ScopeArgs: Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2ec558e274f..9caef3bde08 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,26 +1,89 @@ # This configuration was generated by # `rubocop --auto-gen-config --exclude-limit 0` -# on 2017-04-07 20:17:35 -0400 using RuboCop version 0.47.1. +# on 2017-07-10 01:48:30 +0900 using RuboCop version 0.49.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 233 +# Offense count: 181 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Enabled: false + +# Offense count: 119 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_brackets +Layout/IndentArray: + Enabled: false + +# Offense count: 208 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/IndentHash: + Enabled: false + +# Offense count: 174 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: space, no_space +Layout/SpaceBeforeBlockBraces: + Enabled: false + +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceBeforeFirstArg: + Enabled: false + +# Offense count: 64 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: require_no_space, require_space +Layout/SpaceInLambdaLiteral: + Enabled: false + +# Offense count: 256 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Enabled: false + +# Offense count: 135 +# Cop supports --auto-correct. +Layout/SpaceInsideParens: + Enabled: false + +# Offense count: 14 +# Cop supports --auto-correct. +Layout/SpaceInsidePercentLiteralDelimiters: + Enabled: false + +# Offense count: 89 +# Cop supports --auto-correct. +Layout/TrailingWhitespace: + Enabled: false + +# Offense count: 272 RSpec/EmptyLineAfterFinalLet: Enabled: false -# Offense count: 167 +# Offense count: 181 RSpec/EmptyLineAfterSubject: Enabled: false -# Offense count: 72 +# Offense count: 78 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: implicit, each, example RSpec/HookArgument: Enabled: false -# Offense count: 11 +# Offense count: 9 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: it_behaves_like, it_should_behave_like RSpec/ItBehavesLike: @@ -30,19 +93,19 @@ RSpec/ItBehavesLike: RSpec/IteratedExpectation: Enabled: false -# Offense count: 3 +# Offense count: 2 RSpec/OverwritingSetup: Enabled: false -# Offense count: 34 +# Offense count: 36 RSpec/RepeatedExample: Enabled: false -# Offense count: 43 +# Offense count: 86 RSpec/ScatteredLet: Enabled: false -# Offense count: 32 +# Offense count: 20 RSpec/ScatteredSetup: Enabled: false @@ -50,7 +113,7 @@ RSpec/ScatteredSetup: RSpec/SharedContext: Enabled: false -# Offense count: 150 +# Offense count: 115 Rails/FilePath: Enabled: false @@ -60,90 +123,71 @@ Rails/FilePath: Rails/ReversibleMigration: Enabled: false -# Offense count: 302 +# Offense count: 336 # Configuration parameters: Blacklist. # Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters Rails/SkipsModelValidations: Enabled: false -# Offense count: 7 +# Offense count: 11 # Cop supports --auto-correct. Security/YAMLLoad: Enabled: false -# Offense count: 59 +# Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: percent_q, bare_percent Style/BarePercentLiterals: Enabled: false -# Offense count: 5 +# Offense count: 6 # Cop supports --auto-correct. Style/EachWithObject: Enabled: false -# Offense count: 28 +# Offense count: 31 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: empty, nil, both Style/EmptyElse: Enabled: false -# Offense count: 4 +# Offense count: 9 # Cop supports --auto-correct. Style/EmptyLiteral: Enabled: false -# Offense count: 59 +# Offense count: 78 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, expanded Style/EmptyMethod: Enabled: false -# Offense count: 214 +# Offense count: 23 # Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. -Style/ExtraSpacing: - Enabled: false - -# Offense count: 9 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: format, sprintf, percent Style/FormatString: Enabled: false -# Offense count: 285 +# Offense count: 301 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false -# Offense count: 16 +# Offense count: 18 Style/IfInsideElse: Enabled: false -# Offense count: 186 +# Offense count: 182 # Cop supports --auto-correct. # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Enabled: false -# Offense count: 99 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_brackets -Style/IndentArray: - Enabled: false - -# Offense count: 160 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_braces -Style/IndentHash: - Enabled: false - -# Offense count: 50 +# Offense count: 52 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: line_count_dependent, lambda, literal @@ -155,63 +199,63 @@ Style/Lambda: Style/LineEndConcatenation: Enabled: false -# Offense count: 34 +# Offense count: 40 # Cop supports --auto-correct. Style/MethodCallWithoutArgsParentheses: Enabled: false -# Offense count: 10 +# Offense count: 13 Style/MethodMissing: Enabled: false -# Offense count: 3 +# Offense count: 6 # Cop supports --auto-correct. Style/MultilineIfModifier: Enabled: false -# Offense count: 24 +# Offense count: 26 # Cop supports --auto-correct. Style/NestedParenthesizedCalls: Enabled: false -# Offense count: 18 +# Offense count: 20 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. # SupportedStyles: skip_modifier_ifs, always Style/Next: Enabled: false -# Offense count: 37 +# Offense count: 45 # Cop supports --auto-correct. # Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles. # SupportedOctalStyles: zero_with_o, zero_only Style/NumericLiteralPrefix: Enabled: false -# Offense count: 88 +# Offense count: 98 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. # SupportedStyles: predicate, comparison Style/NumericPredicate: Enabled: false -# Offense count: 36 +# Offense count: 42 # Cop supports --auto-correct. Style/ParallelAssignment: Enabled: false -# Offense count: 570 +# Offense count: 800 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Enabled: false -# Offense count: 14 +# Offense count: 15 # Cop supports --auto-correct. Style/PerlBackrefs: Enabled: false -# Offense count: 83 +# Offense count: 105 # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. # NamePrefix: is_, has_, have_ # NamePrefixBlacklist: is_, has_, have_ @@ -219,47 +263,47 @@ Style/PerlBackrefs: Style/PredicateName: Enabled: false -# Offense count: 65 +# Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, exploded Style/RaiseArgs: Enabled: false -# Offense count: 5 +# Offense count: 6 # Cop supports --auto-correct. Style/RedundantBegin: Enabled: false -# Offense count: 32 +# Offense count: 37 # Cop supports --auto-correct. Style/RedundantFreeze: Enabled: false -# Offense count: 15 +# Offense count: 14 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Enabled: false -# Offense count: 382 +# Offense count: 406 # Cop supports --auto-correct. Style/RedundantSelf: Enabled: false -# Offense count: 111 +# Offense count: 115 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed Style/RegexpLiteral: Enabled: false -# Offense count: 24 +# Offense count: 29 # Cop supports --auto-correct. Style/RescueModifier: Enabled: false -# Offense count: 7 +# Offense count: 8 # Cop supports --auto-correct. Style/SelfAssignment: Enabled: false @@ -270,101 +314,58 @@ Style/SelfAssignment: Style/SingleLineMethods: Enabled: false -# Offense count: 168 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: space, no_space -Style/SpaceBeforeBlockBraces: - Enabled: false - -# Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment. -Style/SpaceBeforeFirstArg: - Enabled: false - -# Offense count: 46 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: require_no_space, require_space -Style/SpaceInLambdaLiteral: - Enabled: false - -# Offense count: 229 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Style/SpaceInsideBlockBraces: - Enabled: false - -# Offense count: 116 -# Cop supports --auto-correct. -Style/SpaceInsideParens: - Enabled: false - -# Offense count: 12 -# Cop supports --auto-correct. -Style/SpaceInsidePercentLiteralDelimiters: - Enabled: false - -# Offense count: 57 +# Offense count: 64 # Cop supports --auto-correct. # Configuration parameters: SupportedStyles. # SupportedStyles: use_perl_names, use_english_names Style/SpecialGlobalVars: EnforcedStyle: use_perl_names -# Offense count: 42 +# Offense count: 44 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: single_quotes, double_quotes Style/StringLiteralsInInterpolation: Enabled: false -# Offense count: 64 +# Offense count: 84 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: respond_to, define_method Style/SymbolProc: Enabled: false -# Offense count: 6 +# Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment. # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex Style/TernaryParentheses: Enabled: false -# Offense count: 18 +# Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: AllowNamedUnderscoreVariables. Style/TrailingUnderscoreVariable: Enabled: false -# Offense count: 78 -# Cop supports --auto-correct. -Style/TrailingWhitespace: - Enabled: false - -# Offense count: 3 +# Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist. # Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym Style/TrivialAccessors: Enabled: false -# Offense count: 6 +# Offense count: 5 # Cop supports --auto-correct. Style/UnlessElse: Enabled: false -# Offense count: 24 +# Offense count: 28 # Cop supports --auto-correct. Style/UnneededInterpolation: Enabled: false -# Offense count: 8 +# Offense count: 11 # Cop supports --auto-correct. Style/ZeroLengthPredicate: Enabled: false diff --git a/Gemfile b/Gemfile index afea07ee6ff..a9a1cbc144d 100644 --- a/Gemfile +++ b/Gemfile @@ -339,8 +339,8 @@ group :development, :test do gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-spinach', '~> 1.1.0' - gem 'rubocop', '~> 0.47.1', require: false - gem 'rubocop-rspec', '~> 1.15.0', require: false + gem 'rubocop', '~> 0.49.1', require: false + gem 'rubocop-rspec', '~> 1.15.1', require: false gem 'scss_lint', '~> 0.54.0', require: false gem 'haml_lint', '~> 0.21.0', require: false gem 'simplecov', '~> 0.14.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 05a70704513..5a327a14c4a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -545,6 +545,7 @@ GEM rubypants (~> 0.2) orm_adapter (0.5.0) os (0.9.6) + parallel (1.11.2) paranoia (2.3.1) activerecord (>= 4.0, < 5.2) parser (2.4.0.0) @@ -730,13 +731,14 @@ GEM pg rails sqlite3 - rubocop (0.47.1) + rubocop (0.49.1) + parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - rubocop-rspec (1.15.0) + rubocop-rspec (1.15.1) rubocop (>= 0.42.0) ruby-fogbugz (0.2.1) crack (~> 0.4) @@ -868,7 +870,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.1.3) + unicode-display_width (1.3.0) unicorn (5.1.0) kgio (~> 2.6) raindrops (~> 0.7) @@ -1078,8 +1080,8 @@ DEPENDENCIES rspec-retry (~> 0.4.5) rspec-set (~> 0.1.3) rspec_profiling (~> 0.0.5) - rubocop (~> 0.47.1) - rubocop-rspec (~> 1.15.0) + rubocop (~> 0.49.1) + rubocop-rspec (~> 1.15.1) ruby-fogbugz (~> 0.2.1) ruby-prof (~> 0.16.2) ruby_parser (~> 3.8) diff --git a/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml b/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml new file mode 100644 index 00000000000..0eb2d069719 --- /dev/null +++ b/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml @@ -0,0 +1,4 @@ +--- +title: Bump rubocop to 0.49.1 and rubocop-rspec to 1.15.1 +merge_request: +author: Takuya Noguchi From 547c6e8505c2fb3a0af321d20919812012008d2c Mon Sep 17 00:00:00 2001 From: John Landa Date: Tue, 1 Aug 2017 01:35:16 +0000 Subject: [PATCH 036/108] Fix username typo (jonh to John) --- doc/user/group/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 2691cf7d671..08da721c71d 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -42,7 +42,7 @@ In GitLab, a namespace is a unique name to be used as a user name, a group name, For example, consider a user called John: -1. John creates his account on GitLab.com with the username `jonh`; +1. John creates his account on GitLab.com with the username `john`; his profile will be accessed under `https://gitlab.example.com/john` 1. John creates a group for his team with the groupname `john-team`; his group and its projects will be accessed under `https://gitlab.example.com/john-team` From faa2a123911eaf84bb57163ea7af759d4632601b Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Mon, 31 Jul 2017 22:49:12 -0500 Subject: [PATCH 037/108] Update CHANGELOG.md for 9.4.3 [ci skip] --- CHANGELOG.md | 10 ++++++++++ .../35567-fix-relative-urls-in-webpack-public-path.yml | 5 ----- changelogs/unreleased/35672-edge-top-bar.yml | 4 ---- .../dm-ldap-authentication-ssl-verification.yml | 4 ---- ...gb-fix-chatops-deploy-multiple-actions-matching.yml | 4 ---- .../unreleased/help-page-breadcrumb-title-fix.yml | 4 ---- ...sure_temp_files_are_deleted_in_fs_metrics-35457.yml | 4 ---- .../pawel-prometheus_client_pid_reuse_error.yml | 4 ---- 8 files changed, 10 insertions(+), 29 deletions(-) delete mode 100644 changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml delete mode 100644 changelogs/unreleased/35672-edge-top-bar.yml delete mode 100644 changelogs/unreleased/dm-ldap-authentication-ssl-verification.yml delete mode 100644 changelogs/unreleased/fix-gb-fix-chatops-deploy-multiple-actions-matching.yml delete mode 100644 changelogs/unreleased/help-page-breadcrumb-title-fix.yml delete mode 100644 changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml delete mode 100644 changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 412cd86bad6..706823ed693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 9.4.3 (2017-07-31) + +- Fix Prometheus client PID reuse bug. !13130 +- Improve deploy environment chatops slash command. !13150 +- Fix asynchronous javascript paths when GitLab is installed under a relative URL. !13165 +- Fix LDAP authentication to Git repository or container registry. +- Fixed new navigation breadcrumb title on help pages. +- Ensure filesystem metrics test files are deleted. +- Properly affixes nav bar in job view in microsoft edge. + ## 9.4.2 (2017-07-28) - Fix job merge request link to a forked source project. !12965 diff --git a/changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml b/changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml deleted file mode 100644 index 41b506681f9..00000000000 --- a/changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix asynchronous javascript paths when GitLab is installed under a relative - URL -merge_request: 13165 -author: diff --git a/changelogs/unreleased/35672-edge-top-bar.yml b/changelogs/unreleased/35672-edge-top-bar.yml deleted file mode 100644 index 0424dee19af..00000000000 --- a/changelogs/unreleased/35672-edge-top-bar.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Properly affixes nav bar in job view in microsoft edge -merge_request: -author: diff --git a/changelogs/unreleased/dm-ldap-authentication-ssl-verification.yml b/changelogs/unreleased/dm-ldap-authentication-ssl-verification.yml deleted file mode 100644 index 59462a6666d..00000000000 --- a/changelogs/unreleased/dm-ldap-authentication-ssl-verification.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix LDAP authentication to Git repository or container registry -merge_request: -author: diff --git a/changelogs/unreleased/fix-gb-fix-chatops-deploy-multiple-actions-matching.yml b/changelogs/unreleased/fix-gb-fix-chatops-deploy-multiple-actions-matching.yml deleted file mode 100644 index 62488a0a2e3..00000000000 --- a/changelogs/unreleased/fix-gb-fix-chatops-deploy-multiple-actions-matching.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve deploy environment chatops slash command -merge_request: 13150 -author: diff --git a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml b/changelogs/unreleased/help-page-breadcrumb-title-fix.yml deleted file mode 100644 index 040fe4b9916..00000000000 --- a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed new navigation breadcrumb title on help pages -merge_request: -author: diff --git a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml b/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml deleted file mode 100644 index 1186dc59dc7..00000000000 --- a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Ensure filesystem metrics test files are deleted -merge_request: -author: diff --git a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml b/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml deleted file mode 100644 index dfff4c23308..00000000000 --- a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Prometheus client PID reuse bug -merge_request: 13130 -author: From 953aa380ebbbcaef8974c3cfb0e15925dbc2c9b6 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 1 Aug 2017 14:16:10 +0900 Subject: [PATCH 038/108] ini --- rubocop/cop/migration/add_column_with_default_to_large_table.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/rubocop/cop/migration/add_column_with_default_to_large_table.rb b/rubocop/cop/migration/add_column_with_default_to_large_table.rb index 87788b0d9c2..fb363f95b56 100644 --- a/rubocop/cop/migration/add_column_with_default_to_large_table.rb +++ b/rubocop/cop/migration/add_column_with_default_to_large_table.rb @@ -20,6 +20,7 @@ module RuboCop 'necessary'.freeze LARGE_TABLES = %i[ + ci_pipelines ci_builds events issues From b5f11ee90ca1d119160c27e5377ce812edaf5848 Mon Sep 17 00:00:00 2001 From: Semen Romanov Date: Tue, 1 Aug 2017 05:25:26 +0000 Subject: [PATCH 039/108] Invalid variable --- lib/support/init.d/gitlab | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index c5f93336346..2f2de083dc0 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -291,7 +291,7 @@ start_gitlab() { fi if [ "$gitlab_workhorse_status" = "0" ]; then - echo "The GitLab Workhorse is already running with pid $spid, not restarting" + echo "The GitLab Workhorse is already running with pid $hpid, not restarting" else # No need to remove a socket, gitlab-workhorse does this itself. # Because gitlab-workhorse has multiple executables we need to fix @@ -313,7 +313,7 @@ start_gitlab() { if [ "$gitlab_pages_enabled" = true ]; then if [ "$gitlab_pages_status" = "0" ]; then - echo "The GitLab Pages is already running with pid $spid, not restarting" + echo "The GitLab Pages is already running with pid $gppid, not restarting" else $app_root/bin/daemon_with_pidfile $gitlab_pages_pid_path \ $gitlab_pages_dir/gitlab-pages $gitlab_pages_options \ @@ -421,7 +421,7 @@ print_status() { fi if [ "$gitlab_pages_enabled" = true ]; then if [ "$gitlab_pages_status" = "0" ]; then - echo "The GitLab Pages with pid $mpid is running." + echo "The GitLab Pages with pid $gppid is running." else printf "The GitLab Pages is \033[31mnot running\033[0m.\n" fi From 98615318883e37a4c9cb201e3e65bb7b775112cd Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Wed, 5 Jul 2017 02:19:02 +0200 Subject: [PATCH 040/108] Move storage specific code from Namespace and Project to concerns --- .../concerns/storage/legacy_namespace.rb | 109 ++++++++++++++++++ app/models/concerns/storage/legacy_project.rb | 76 ++++++++++++ app/models/namespace.rb | 104 +---------------- app/models/project.rb | 73 +----------- 4 files changed, 194 insertions(+), 168 deletions(-) create mode 100644 app/models/concerns/storage/legacy_namespace.rb create mode 100644 app/models/concerns/storage/legacy_project.rb diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb new file mode 100644 index 00000000000..5cbd52e5c75 --- /dev/null +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -0,0 +1,109 @@ +module Storage + module LegacyNamespace + extend ActiveSupport::Concern + + def move_dir + if any_project_has_container_registry_tags? + raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry') + end + + # Move the namespace directory in all storages paths used by member projects + repository_storage_paths.each do |repository_storage_path| + # Ensure old directory exists before moving it + gitlab_shell.add_namespace(repository_storage_path, full_path_was) + + unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path) + Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}" + + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise Gitlab::UpdatePathError.new('namespace directory cannot be moved') + end + end + + Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path) + Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path) + + remove_exports! + + # If repositories moved successfully we need to + # send update instructions to users. + # However we cannot allow rollback since we moved namespace dir + # So we basically we mute exceptions in next actions + begin + send_update_instructions + true + rescue + # Returning false does not rollback after_* transaction but gives + # us information about failing some of tasks + false + end + end + + # Hooks + + # Save the storage paths before the projects are destroyed to use them on after destroy + def prepare_for_destroy + old_repository_storage_paths + end + + private + + def send_update_instructions + projects.each do |project| + project.send_move_instructions("#{full_path_was}/#{project.path}") + end + end + + def old_repository_storage_paths + @old_repository_storage_paths ||= repository_storage_paths + end + + def repository_storage_paths + # We need to get the storage paths for all the projects, even the ones that are + # pending delete. Unscoping also get rids of the default order, which causes + # problems with SELECT DISTINCT. + Project.unscoped do + all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path) + end + end + + def rm_dir + # Remove the namespace directory in all storages paths used by member projects + old_repository_storage_paths.each do |repository_storage_path| + # Move namespace directory into trash. + # We will remove it later async + new_path = "#{full_path}+#{id}+deleted" + + if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path) + message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\"" + Gitlab::AppLogger.info message + + # Remove namespace directroy async with delay so + # GitLab has time to remove all projects first + run_after_commit do + GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path) + end + end + end + + remove_exports! + end + + def remove_exports! + Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete)) + end + + def export_path + File.join(Gitlab::ImportExport.storage_path, full_path_was) + end + + def full_path_was + if parent + parent.full_path + '/' + path_was + else + path_was + end + end + end +end diff --git a/app/models/concerns/storage/legacy_project.rb b/app/models/concerns/storage/legacy_project.rb new file mode 100644 index 00000000000..b537f40548e --- /dev/null +++ b/app/models/concerns/storage/legacy_project.rb @@ -0,0 +1,76 @@ +module Storage + module LegacyProject + extend ActiveSupport::Concern + + def disk_path + full_path + end + + def ensure_dir_exist + gitlab_shell.add_namespace(repository_storage_path, namespace.full_path) + end + + def rename_repo + path_was = previous_changes['path'].first + old_path_with_namespace = File.join(namespace.full_path, path_was) + new_path_with_namespace = File.join(namespace.full_path, path) + + Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" + + if has_container_registry_tags? + Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" + + # we currently doesn't support renaming repository if it contains images in container registry + raise StandardError.new('Project cannot be renamed, because images are present in its container registry') + end + + expire_caches_before_rename(old_path_with_namespace) + + if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) + # If repository moved successfully we need to send update instructions to users. + # However we cannot allow rollback since we moved repository + # So we basically we mute exceptions in next actions + begin + gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") + send_move_instructions(old_path_with_namespace) + expires_full_path_cache + + @old_path_with_namespace = old_path_with_namespace + + SystemHooksService.new.execute_hooks_for(self, :rename) + + @repository = nil + rescue => e + Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}" + # Returning false does not rollback after_* transaction but gives + # us information about failing some of tasks + false + end + else + Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" + + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise StandardError.new('repository cannot be renamed') + end + + Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" + + Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) + Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) + end + + def create_repository(force: false) + # Forked import is handled asynchronously + return if forked? && !force + + if gitlab_shell.add_repository(repository_storage_path, path_with_namespace) + repository.after_create + true + else + errors.add(:base, 'Failed to create repository via gitlab-shell') + false + end + end + end +end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 0bb04194bdb..010c97c507e 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -8,6 +8,7 @@ class Namespace < ActiveRecord::Base include Gitlab::VisibilityLevel include Routable include AfterCommitQueue + include Storage::LegacyNamespace # Prevent users from creating unreasonably deep level of nesting. # The number 20 was taken based on maximum nesting level of @@ -41,10 +42,11 @@ class Namespace < ActiveRecord::Base delegate :name, to: :owner, allow_nil: true, prefix: true - after_update :move_dir, if: :path_changed? after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') } - # Save the storage paths before the projects are destroyed to use them on after destroy + # Legacy Storage specific hooks + + after_update :move_dir, if: :path_changed? before_destroy(prepend: true) { prepare_for_destroy } after_destroy :rm_dir @@ -118,53 +120,10 @@ class Namespace < ActiveRecord::Base owner_name end - def move_dir - if any_project_has_container_registry_tags? - raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry') - end - - # Move the namespace directory in all storages paths used by member projects - repository_storage_paths.each do |repository_storage_path| - # Ensure old directory exists before moving it - gitlab_shell.add_namespace(repository_storage_path, full_path_was) - - unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path) - Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}" - - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise Gitlab::UpdatePathError.new('namespace directory cannot be moved') - end - end - - Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path) - Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path) - - remove_exports! - - # If repositories moved successfully we need to - # send update instructions to users. - # However we cannot allow rollback since we moved namespace dir - # So we basically we mute exceptions in next actions - begin - send_update_instructions - rescue - # Returning false does not rollback after_* transaction but gives - # us information about failing some of tasks - false - end - end - def any_project_has_container_registry_tags? all_projects.any?(&:has_container_registry_tags?) end - def send_update_instructions - projects.each do |project| - project.send_move_instructions("#{full_path_was}/#{project.path}") - end - end - def kind type == 'Group' ? 'group' : 'user' end @@ -206,14 +165,6 @@ class Namespace < ActiveRecord::Base parent_id_changed? end - def prepare_for_destroy - old_repository_storage_paths - end - - def old_repository_storage_paths - @old_repository_storage_paths ||= repository_storage_paths - end - # Includes projects from this namespace and projects from all subgroups # that belongs to this namespace def all_projects @@ -232,37 +183,6 @@ class Namespace < ActiveRecord::Base private - def repository_storage_paths - # We need to get the storage paths for all the projects, even the ones that are - # pending delete. Unscoping also get rids of the default order, which causes - # problems with SELECT DISTINCT. - Project.unscoped do - all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path) - end - end - - def rm_dir - # Remove the namespace directory in all storages paths used by member projects - old_repository_storage_paths.each do |repository_storage_path| - # Move namespace directory into trash. - # We will remove it later async - new_path = "#{full_path}+#{id}+deleted" - - if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path) - message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\"" - Gitlab::AppLogger.info message - - # Remove namespace directroy async with delay so - # GitLab has time to remove all projects first - run_after_commit do - GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path) - end - end - end - - remove_exports! - end - def refresh_access_of_projects_invited_groups Group .joins(project_group_links: :project) @@ -270,22 +190,6 @@ class Namespace < ActiveRecord::Base .find_each(&:refresh_members_authorized_projects) end - def remove_exports! - Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete)) - end - - def export_path - File.join(Gitlab::ImportExport.storage_path, full_path_was) - end - - def full_path_was - if parent - parent.full_path + '/' + path_was - else - path_was - end - end - def nesting_level_allowed if ancestors.count > Group::NUMBER_OF_ANCESTORS_ALLOWED errors.add(:parent_id, "has too deep level of nesting") diff --git a/app/models/project.rb b/app/models/project.rb index d827bfaa806..39949e6dbfd 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -17,6 +17,7 @@ class Project < ActiveRecord::Base include ProjectFeaturesCompatibility include SelectForProjectAuthorization include Routable + include Storage::LegacyProject extend Gitlab::ConfigHelper @@ -45,7 +46,6 @@ class Project < ActiveRecord::Base after_create :ensure_dir_exist after_create :create_project_feature, unless: :project_feature - after_save :ensure_dir_exist, if: :namespace_id_changed? after_save :update_project_statistics, if: :namespace_id_changed? # set last_activity_at to the same as created_at @@ -67,6 +67,10 @@ class Project < ActiveRecord::Base after_validation :check_pending_delete + # Legacy Storage specific hooks + + after_save :ensure_dir_exist, if: :namespace_id_changed? + acts_as_taggable attr_accessor :new_default_branch @@ -974,56 +978,6 @@ class Project < ActiveRecord::Base !group end - def rename_repo - path_was = previous_changes['path'].first - old_path_with_namespace = File.join(namespace.full_path, path_was) - new_path_with_namespace = File.join(namespace.full_path, path) - - Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" - - if has_container_registry_tags? - Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" - - # we currently doesn't support renaming repository if it contains images in container registry - raise StandardError.new('Project cannot be renamed, because images are present in its container registry') - end - - expire_caches_before_rename(old_path_with_namespace) - - if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) - # If repository moved successfully we need to send update instructions to users. - # However we cannot allow rollback since we moved repository - # So we basically we mute exceptions in next actions - begin - gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") - send_move_instructions(old_path_with_namespace) - expires_full_path_cache - - @old_path_with_namespace = old_path_with_namespace - - SystemHooksService.new.execute_hooks_for(self, :rename) - - @repository = nil - rescue => e - Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}" - # Returning false does not rollback after_* transaction but gives - # us information about failing some of tasks - false - end - else - Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" - - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise StandardError.new('repository cannot be renamed') - end - - Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" - - Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) - Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) - end - # Expires various caches before a project is renamed. def expire_caches_before_rename(old_path) repo = Repository.new(old_path, self) @@ -1109,19 +1063,6 @@ class Project < ActiveRecord::Base merge_requests.where(source_project_id: self.id) end - def create_repository(force: false) - # Forked import is handled asynchronously - return if forked? && !force - - if gitlab_shell.add_repository(repository_storage_path, path_with_namespace) - repository.after_create - true - else - errors.add(:base, 'Failed to create repository via gitlab-shell') - false - end - end - def ensure_repository create_repository(force: true) unless repository_exists? end @@ -1327,10 +1268,6 @@ class Project < ActiveRecord::Base status.zero? end - def ensure_dir_exist - gitlab_shell.add_namespace(repository_storage_path, namespace.full_path) - end - def predefined_variables [ { key: 'CI_PROJECT_ID', value: id.to_s, public: true }, From abb878326c5cac283fff19716149211658ce25d1 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 20 Jul 2017 11:34:09 +0200 Subject: [PATCH 041/108] Rename many path_with_namespace -> full_path --- app/helpers/merge_requests_helper.rb | 2 +- app/helpers/projects_helper.rb | 4 +- app/mailers/notify.rb | 2 +- app/models/ci/pipeline.rb | 2 +- app/models/merge_request.rb | 4 +- app/models/project.rb | 23 ++++----- .../project_services/flowdock_service.rb | 6 +-- app/models/project_services/jira_service.rb | 2 +- app/models/project_wiki.rb | 4 +- app/services/git_operation_service.rb | 2 +- app/services/projects/destroy_service.rb | 2 +- app/services/projects/import_service.rb | 2 +- app/services/projects/transfer_service.rb | 2 +- app/services/system_hooks_service.rb | 8 ++-- app/workers/irker_worker.rb | 4 +- app/workers/repository_import_worker.rb | 2 +- ...1322_migrate_process_commit_worker_jobs.rb | 1 + .../steps/project/forked_merge_requests.rb | 12 ++--- features/steps/project/redirects.rb | 4 +- features/steps/project/wiki.rb | 2 +- lib/api/runner.rb | 4 +- lib/backup/repository.rb | 6 +-- .../filter/abstract_reference_filter.rb | 4 +- lib/banzai/filter/upload_link_filter.rb | 2 +- lib/ci/api/builds.rb | 4 +- lib/github/import.rb | 2 +- lib/gitlab/bitbucket_import/importer.rb | 2 +- lib/gitlab/email/message/repository_push.rb | 2 +- lib/gitlab/github_import/wiki_formatter.rb | 2 +- .../projects/blob_controller_spec.rb | 2 +- .../projects/branches_controller_spec.rb | 6 +-- .../merge_requests_controller_spec.rb | 2 +- .../projects/tree_controller_spec.rb | 6 +-- .../merge_requests/create_new_mr_spec.rb | 8 ++-- .../projects/wiki/markdown_preview_spec.rb | 48 +++++++++---------- spec/helpers/labels_helper_spec.rb | 4 +- spec/helpers/markup_helper_spec.rb | 4 +- spec/helpers/merge_requests_helper_spec.rb | 4 +- spec/helpers/projects_helper_spec.rb | 4 +- .../filter/abstract_reference_filter_spec.rb | 14 +++--- .../commit_range_reference_filter_spec.rb | 10 ++-- .../filter/commit_reference_filter_spec.rb | 10 ++-- .../filter/issue_reference_filter_spec.rb | 12 ++--- .../filter/label_reference_filter_spec.rb | 10 ++-- .../merge_request_reference_filter_spec.rb | 4 +- .../filter/milestone_reference_filter_spec.rb | 34 +++++++++++-- .../filter/relative_link_filter_spec.rb | 2 +- .../filter/snippet_reference_filter_spec.rb | 4 +- spec/lib/extracts_path_spec.rb | 4 +- .../email/message/repository_push_spec.rb | 2 +- spec/lib/gitlab/gfm/uploads_rewriter_spec.rb | 4 +- spec/lib/gitlab/import_export/fork_spec.rb | 2 +- .../import_export/project_tree_saver_spec.rb | 2 +- .../import_export/repo_restorer_spec.rb | 2 +- .../gitlab/import_export/repo_saver_spec.rb | 2 +- .../import_export/wiki_repo_saver_spec.rb | 2 +- spec/lib/gitlab/url_builder_spec.rb | 18 +++---- ...migrate_process_commit_worker_jobs_spec.rb | 2 +- ...oups_into_regular_groups_for_mysql_spec.rb | 2 +- spec/models/commit_spec.rb | 2 +- spec/models/merge_request_spec.rb | 2 +- spec/models/project_label_spec.rb | 4 +- .../gitlab_issue_tracker_service_spec.rb | 12 ++--- spec/models/project_spec.rb | 30 +++++++++++- spec/requests/api/internal_spec.rb | 4 +- spec/services/git_push_service_spec.rb | 2 +- spec/services/projects/import_service_spec.rb | 10 ++-- spec/services/system_note_service_spec.rb | 4 +- spec/support/notify_shared_examples.rb | 2 +- .../project_hook_data_shared_example.rb | 4 +- 70 files changed, 239 insertions(+), 185 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 78cf7b26a31..c31023f2d9a 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -40,7 +40,7 @@ module MergeRequestsHelper def merge_path_description(merge_request, separator) if merge_request.for_fork? - "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" + "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}" else "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9a8d296d514..34ff6107eab 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -398,7 +398,7 @@ module ProjectsHelper if project import_path = "/Home/Stacks/import" - repo = project.path_with_namespace + repo = project.full_path branch ||= project.default_branch sha ||= project.commit.short_id @@ -458,7 +458,7 @@ module ProjectsHelper def readme_cache_key sha = @project.commit.try(:sha) || 'nil' - [@project.path_with_namespace, sha, "readme"].join('-') + [@project.full_path, sha, "readme"].join('-') end def current_ref diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index eaac6fcb548..9efabe3f44e 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -165,7 +165,7 @@ class Notify < BaseMailer headers['X-GitLab-Project'] = @project.name headers['X-GitLab-Project-Id'] = @project.id - headers['X-GitLab-Project-Path'] = @project.path_with_namespace + headers['X-GitLab-Project-Path'] = @project.full_path end def add_unsubscription_headers_and_links diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index d2abcf30034..ea7331cb27f 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -317,7 +317,7 @@ module Ci return @config_processor if defined?(@config_processor) @config_processor ||= begin - Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace) + Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.full_path) rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e self.yaml_errors = e.message nil diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 81e0776e79c..8ca850b6d96 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -630,7 +630,7 @@ class MergeRequest < ActiveRecord::Base def target_project_path if target_project - target_project.path_with_namespace + target_project.full_path else "(removed)" end @@ -638,7 +638,7 @@ class MergeRequest < ActiveRecord::Base def source_project_path if source_project - source_project.path_with_namespace + source_project.full_path else "(removed)" end diff --git a/app/models/project.rb b/app/models/project.rb index 39949e6dbfd..7d3c91dc1b9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -379,7 +379,7 @@ class Project < ActiveRecord::Base begin Projects::HousekeepingService.new(project).execute rescue Projects::HousekeepingService::LeaseTaken => e - Rails.logger.info("Could not perform housekeeping for project #{project.path_with_namespace} (#{project.id}): #{e}") + Rails.logger.info("Could not perform housekeeping for project #{project.full_path} (#{project.id}): #{e}") end end end @@ -485,7 +485,7 @@ class Project < ActiveRecord::Base def container_registry_url if Gitlab.config.registry.enabled - "#{Gitlab.config.registry.host_port}/#{path_with_namespace.downcase}" + "#{Gitlab.config.registry.host_port}/#{full_path.downcase}" end end @@ -524,16 +524,16 @@ class Project < ActiveRecord::Base job_id = if forked? RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, - forked_from_project.path_with_namespace, + forked_from_project.full_path, self.namespace.full_path) else RepositoryImportWorker.perform_async(self.id) end if job_id - Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}" + Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}" else - Rails.logger.error "Import job failed to start for #{path_with_namespace}" + Rails.logger.error "Import job failed to start for #{full_path}" end end @@ -694,7 +694,7 @@ class Project < ActiveRecord::Base # `from` argument can be a Namespace or Project. def to_reference(from = nil, full: false) if full || cross_namespace_reference?(from) - path_with_namespace + full_path elsif cross_project_reference?(from) path end @@ -718,7 +718,7 @@ class Project < ActiveRecord::Base author.ensure_incoming_email_token! Gitlab::IncomingEmail.reply_address( - "#{path_with_namespace}+#{author.incoming_email_token}") + "#{full_path}+#{author.incoming_email_token}") end def build_commit_note(commit) @@ -1002,7 +1002,7 @@ class Project < ActiveRecord::Base git_http_url: http_url_to_repo, namespace: namespace.name, visibility_level: visibility_level, - path_with_namespace: path_with_namespace, + path_with_namespace: full_path, default_branch: default_branch, ci_config_path: ci_config_path } @@ -1272,8 +1272,8 @@ class Project < ActiveRecord::Base [ { key: 'CI_PROJECT_ID', value: id.to_s, public: true }, { key: 'CI_PROJECT_NAME', value: path, public: true }, - { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true }, - { key: 'CI_PROJECT_PATH_SLUG', value: path_with_namespace.parameterize, public: true }, + { key: 'CI_PROJECT_PATH', value: full_path, public: true }, + { key: 'CI_PROJECT_PATH_SLUG', value: full_path.parameterize, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true }, { key: 'CI_PROJECT_URL', value: web_url, public: true } ] @@ -1378,6 +1378,7 @@ class Project < ActiveRecord::Base alias_method :name_with_namespace, :full_name alias_method :human_name, :full_name + # @deprecated cannot remove yet because it has an index with its name in elasticsearch alias_method :path_with_namespace, :full_path private @@ -1432,7 +1433,7 @@ class Project < ActiveRecord::Base def pending_delete_twin return false unless path - Project.pending_delete.find_by_full_path(path_with_namespace) + Project.pending_delete.find_by_full_path(full_path) end ## diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 2db95b9aaa3..4d23a17a545 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -35,9 +35,9 @@ class FlowdockService < Service data[:after], token: token, repo: project.repository.path_to_repo, - repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", - commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s", - diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s" + repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}", + commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/%s", + diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s" ) end end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2aa19443198..c2414885368 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -140,7 +140,7 @@ class JiraService < IssueTrackerService url: resource_url(user_path(author)) }, project: { - name: project.path_with_namespace, + name: project.full_path, url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper }, entity: { diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index dfca0031af8..b44ee9b1766 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -27,7 +27,7 @@ class ProjectWiki end def path_with_namespace - @project.path_with_namespace + ".wiki" + @project.full_path + '.wiki' end def web_url @@ -47,7 +47,7 @@ class ProjectWiki end def wiki_base_path - [Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('') + [Gitlab.config.gitlab.relative_url_root, "/", @project.full_path, "/wikis"].join('') end # Returns the Gollum::Wiki object. diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb index 32925e9c1f2..545ca0742e4 100644 --- a/app/services/git_operation_service.rb +++ b/app/services/git_operation_service.rb @@ -60,7 +60,7 @@ class GitOperationService start_branch_name = nil if start_repository.empty_repo? if start_branch_name && !start_repository.branch_exists?(start_branch_name) - raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}" + raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.full_path}" end update_branch_with_hooks(branch_name) do diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index f6e8b6655f2..45af2f7b503 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -9,7 +9,7 @@ module Projects def async_execute project.update_attribute(:pending_delete, true) job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params) - Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}") + Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}") end def execute diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index eea17e24903..cf6a401db2a 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -11,7 +11,7 @@ module Projects success rescue => e - error("Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}") + error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}") end private diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 4bb98e5cb4e..386d49e58e7 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -34,7 +34,7 @@ module Projects private def transfer(project) - @old_path = project.path_with_namespace + @old_path = project.full_path @old_group = project.group @new_path = File.join(@new_namespace.try(:full_path) || '', project.path) @old_namespace = project.namespace diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index bd58a54592f..cbcd4478af6 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -24,7 +24,7 @@ class SystemHooksService key: model.key, id: model.id ) - + if model.user data[:username] = model.user.username end @@ -56,7 +56,7 @@ class SystemHooksService when GroupMember data.merge!(group_member_data(model)) end - + data end @@ -79,7 +79,7 @@ class SystemHooksService { name: model.name, path: model.path, - path_with_namespace: model.path_with_namespace, + path_with_namespace: model.full_path, project_id: model.id, owner_name: owner.name, owner_email: owner.respond_to?(:email) ? owner.email : "", @@ -93,7 +93,7 @@ class SystemHooksService { project_name: project.name, project_path: project.path, - project_path_with_namespace: project.path_with_namespace, + project_path_with_namespace: project.full_path, project_id: project.id, user_username: model.user.username, user_name: model.user.name, diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb index 22f67fa9e9f..3dd14466994 100644 --- a/app/workers/irker_worker.rb +++ b/app/workers/irker_worker.rb @@ -66,7 +66,7 @@ class IrkerWorker end def send_new_branch(project, repo_name, committer, branch) - repo_path = project.path_with_namespace + repo_path = project.full_path newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches" newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors @@ -109,7 +109,7 @@ class IrkerWorker end def send_commits_count(data, project, repo, committer, branch) - url = compare_url data, project.path_with_namespace + url = compare_url data, project.full_path commits = colorize_commits data['total_commits_count'] new_commits = 'new commit' diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 625476b7e01..6be541abd3e 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -16,7 +16,7 @@ class RepositoryImportWorker Gitlab::Metrics.add_event(:import_repository, import_url: @project.import_url, - path: @project.path_with_namespace) + path: @project.full_path) project.update_columns(import_jid: self.jid, import_error: nil) diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb index c0cb9d78748..bcdae272209 100644 --- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb +++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb @@ -16,6 +16,7 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration end def repository_path + # TODO: review if the change from Legacy storage needs to reflect here as well. File.join(repository_storage_path, read_attribute(:path_with_namespace) + '.git') end diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index c6cabace25b..420ac8a695a 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -30,8 +30,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(@merge_request.source_project).to eq @forked_project expect(@merge_request.source_branch).to eq "fix" expect(@merge_request.target_branch).to eq "master" - expect(page).to have_content @forked_project.path_with_namespace - expect(page).to have_content @project.path_with_namespace + expect(page).to have_content @forked_project.full_path + expect(page).to have_content @project.full_path expect(page).to have_content @merge_request.source_branch expect(page).to have_content @merge_request.target_branch @@ -43,10 +43,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(page).to have_content('Target branch') first('.js-source-project').click - first('.dropdown-source-project a', text: @forked_project.path_with_namespace) + first('.dropdown-source-project a', text: @forked_project.full_path) first('.js-target-project').click - first('.dropdown-target-project a', text: @project.path_with_namespace) + first('.dropdown-target-project a', text: @project.full_path) first('.js-source-branch').click wait_for_requests @@ -81,8 +81,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(@merge_request.source_project).to eq @forked_project expect(@merge_request.source_branch).to eq "fix" expect(@merge_request.target_branch).to eq "master" - expect(page).to have_content @forked_project.path_with_namespace - expect(page).to have_content @project.path_with_namespace + expect(page).to have_content @forked_project.full_path + expect(page).to have_content @project.full_path expect(page).to have_content @merge_request.source_branch expect(page).to have_content @merge_request.target_branch end diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb index b2ceb8dd9a8..cbe6f93f87e 100644 --- a/features/steps/project/redirects.rb +++ b/features/steps/project/redirects.rb @@ -47,7 +47,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps step 'I should be redirected to "Community" page' do project = Project.find_by(name: 'Community') - expect(current_path).to eq "/#{project.path_with_namespace}" + expect(current_path).to eq "/#{project.full_path}" expect(status_code).to eq 200 end @@ -61,7 +61,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps step 'I should be redirected to "Enterprise" page' do project = Project.find_by(name: 'Enterprise') - expect(current_path).to eq "/#{project.path_with_namespace}" + expect(current_path).to eq "/#{project.full_path}" expect(status_code).to eq 200 end end diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 6a478c50e5e..2b8da2a6f19 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -142,7 +142,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I should see non-escaped link in the pages list' do - expect(page).to have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three-test']") + expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']") end step 'I edit the Wiki page with a path' do diff --git a/lib/api/runner.rb b/lib/api/runner.rb index 405d25ca3c1..88fc62d33df 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -90,7 +90,7 @@ module API if result.valid? if result.build Gitlab::Metrics.add_event(:build_found, - project: result.build.project.path_with_namespace) + project: result.build.project.full_path) present result.build, with: Entities::JobRequest::Response else Gitlab::Metrics.add_event(:build_not_found) @@ -119,7 +119,7 @@ module API job.trace.set(params[:trace]) if params[:trace] Gitlab::Metrics.add_event(:update_build, - project: job.project.path_with_namespace) + project: job.project.full_path) case params[:state].to_s when 'success' diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 02ed1e49ef8..f8cb8f089e7 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -7,7 +7,7 @@ module Backup prepare Project.find_each(batch_size: 1000) do |project| - progress.print " * #{project.path_with_namespace} ... " + progress.print " * #{project.full_path} ... " path_to_project_repo = path_to_repo(project) path_to_project_bundle = path_to_bundle(project) @@ -71,7 +71,7 @@ module Backup end Project.find_each(batch_size: 1000) do |project| - progress.print " * #{project.path_with_namespace} ... " + progress.print " * #{project.full_path} ... " path_to_project_repo = path_to_repo(project) path_to_project_bundle = path_to_bundle(project) @@ -185,7 +185,7 @@ module Backup def progress_warn(project, cmd, output) progress.puts "[WARNING] Executing #{cmd}".color(:orange) - progress.puts "Ignoring error on #{project.path_with_namespace} - #{output}".color(:orange) + progress.puts "Ignoring error on #{project.full_path} - #{output}".color(:orange) end def empty_repo?(project_or_wiki) diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 7a262dd025c..685b43605ae 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -259,7 +259,7 @@ module Banzai found = [] projects.each do |project| - ref = project.path_with_namespace + ref = project.full_path get_or_set_cache(cache, ref) { project } found << ref end @@ -277,7 +277,7 @@ module Banzai end def current_project_path - @current_project_path ||= project.path_with_namespace + @current_project_path ||= project.full_path end def current_project_namespace_path diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb index 45bb66dc99f..09844931be5 100644 --- a/lib/banzai/filter/upload_link_filter.rb +++ b/lib/banzai/filter/upload_link_filter.rb @@ -28,7 +28,7 @@ module Banzai end def build_url(uri) - File.join(Gitlab.config.gitlab.url, project.path_with_namespace, uri) + File.join(Gitlab.config.gitlab.url, project.full_path, uri) end def project diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index e2e91ce99cd..79058c02ce5 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -29,7 +29,7 @@ module Ci if result.valid? if result.build Gitlab::Metrics.add_event(:build_found, - project: result.build.project.path_with_namespace) + project: result.build.project.full_path) present result.build, with: Entities::BuildDetails else @@ -64,7 +64,7 @@ module Ci build.trace.set(params[:trace]) if params[:trace] Gitlab::Metrics.add_event(:update_build, - project: build.project.path_with_namespace) + project: build.project.full_path) case params[:state].to_s when 'success' diff --git a/lib/github/import.rb b/lib/github/import.rb index ff5d7db2705..cea4be5460b 100644 --- a/lib/github/import.rb +++ b/lib/github/import.rb @@ -93,7 +93,7 @@ module Github def fetch_wiki_repository wiki_url = "https://#{options.fetch(:token)}@github.com/#{repo}.wiki.git" - wiki_path = "#{project.path_with_namespace}.wiki" + wiki_path = "#{project.full_path}.wiki" unless project.wiki.repository_exists? gitlab_shell.import_repository(project.repository_storage_path, wiki_path, wiki_url) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 5a6d9ae99a0..28bbf3b384e 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -61,7 +61,7 @@ module Gitlab def import_wiki return if project.wiki.repository_exists? - path_with_namespace = "#{project.path_with_namespace}.wiki" + path_with_namespace = "#{project.full_path}.wiki" import_url = project.import_url.sub(/\.git\z/, ".git/wiki") gitlab_shell.import_repository(project.repository_storage_path, path_with_namespace, import_url) rescue StandardError => e diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index dd1d9dcd555..cd9d3a6483f 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -117,7 +117,7 @@ module Gitlab def subject subject_text = '[Git]' - subject_text << "[#{project.path_with_namespace}]" + subject_text << "[#{project.full_path}]" subject_text << "[#{ref_name}]" if @action == :push subject_text << ' ' diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb index 6c592ff469c..30cff1ba804 100644 --- a/lib/gitlab/github_import/wiki_formatter.rb +++ b/lib/gitlab/github_import/wiki_formatter.rb @@ -8,7 +8,7 @@ module Gitlab end def path_with_namespace - "#{project.path_with_namespace}.wiki" + "#{project.full_path}.wiki" end def import_url diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 02bbc48dc59..19ee5e3bb7e 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -48,7 +48,7 @@ describe Projects::BlobController do let(:id) { 'markdown/doc' } it 'redirects' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") + .to redirect_to("/#{project.full_path}/tree/markdown/doc") end end end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index 9cd4e9dbf84..24defb4d657 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -33,7 +33,7 @@ describe Projects::BranchesController do let(:ref) { "master" } it 'redirects' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/merge_branch") + .to redirect_to("/#{project.full_path}/tree/merge_branch") end end @@ -42,7 +42,7 @@ describe Projects::BranchesController do let(:ref) { "master" } it 'redirects' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');") + .to redirect_to("/#{project.full_path}/tree/alert('merge');") end end @@ -82,7 +82,7 @@ describe Projects::BranchesController do issue_iid: issue.iid expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch") + .to redirect_to("/#{project.full_path}/tree/1-feature-branch") end it 'posts a system note' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 2fce4b7a85f..0bfe83f1d98 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -609,7 +609,7 @@ describe Projects::MergeRequestsController do end it 'links to the environment on that project' do - expect(json_response.first['url']).to match /#{forked.path_with_namespace}/ + expect(json_response.first['url']).to match /#{forked.full_path}/ end end end diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index 16cd2e076e5..775f3998f5d 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -81,7 +81,7 @@ describe Projects::TreeController do context 'redirect to blob' do let(:id) { 'master/README.md' } it 'redirects' do - redirect_url = "/#{project.path_with_namespace}/blob/master/README.md" + redirect_url = "/#{project.full_path}/blob/master/README.md" expect(subject) .to redirect_to(redirect_url) end @@ -107,7 +107,7 @@ describe Projects::TreeController do it 'redirects to the new directory' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}") + .to redirect_to("/#{project.full_path}/tree/#{branch_name}/#{path}") expect(flash[:notice]).to eq('The directory has been successfully created.') end end @@ -118,7 +118,7 @@ describe Projects::TreeController do it 'does not allow overwriting of existing files' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/master") + .to redirect_to("/#{project.full_path}/tree/master") expect(flash[:alert]).to eq('A file with this name already exists') end end diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index 11a74276898..d7f3d91e625 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -67,8 +67,8 @@ feature 'Create New Merge Request', js: true do visit project_new_merge_request_path(project, merge_request: { target_project_id: private_project.id }) - expect(page).not_to have_content private_project.path_with_namespace - expect(page).to have_content project.path_with_namespace + expect(page).not_to have_content private_project.full_path + expect(page).to have_content project.full_path end end @@ -78,8 +78,8 @@ feature 'Create New Merge Request', js: true do visit project_new_merge_request_path(project, merge_request: { source_project_id: private_project.id }) - expect(page).not_to have_content private_project.path_with_namespace - expect(page).to have_content project.path_with_namespace + expect(page).not_to have_content private_project.full_path + expect(page).to have_content project.full_path end end diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb index dbe98a38197..1dc89665020 100644 --- a/spec/features/projects/wiki/markdown_preview_spec.rb +++ b/spec/features/projects/wiki/markdown_preview_spec.rb @@ -38,10 +38,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("regular link") - expect(page.html).to include("relative link 1") - expect(page.html).to include("relative link 2") - expect(page.html).to include("relative link 3") + expect(page.html).to include("regular link") + expect(page.html).to include("relative link 1") + expect(page.html).to include("relative link 2") + expect(page.html).to include("relative link 3") end end @@ -60,10 +60,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("regular link") - expect(page.html).to include("relative link 1") - expect(page.html).to include("relative link 2") - expect(page.html).to include("relative link 3") + expect(page.html).to include("regular link") + expect(page.html).to include("relative link 1") + expect(page.html).to include("relative link 2") + expect(page.html).to include("relative link 3") end end @@ -82,10 +82,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("regular link") - expect(page.html).to include("relative link 1") - expect(page.html).to include("relative link 2") - expect(page.html).to include("relative link 3") + expect(page.html).to include("regular link") + expect(page.html).to include("relative link 1") + expect(page.html).to include("relative link 2") + expect(page.html).to include("relative link 3") end end end @@ -115,10 +115,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("regular link") - expect(page.html).to include("relative link 1") - expect(page.html).to include("relative link 2") - expect(page.html).to include("relative link 3") + expect(page.html).to include("regular link") + expect(page.html).to include("relative link 1") + expect(page.html).to include("relative link 2") + expect(page.html).to include("relative link 3") end end @@ -132,10 +132,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("regular link") - expect(page.html).to include("relative link 1") - expect(page.html).to include("relative link 2") - expect(page.html).to include("relative link 3") + expect(page.html).to include("regular link") + expect(page.html).to include("relative link 1") + expect(page.html).to include("relative link 2") + expect(page.html).to include("relative link 3") end end @@ -149,10 +149,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("regular link") - expect(page.html).to include("relative link 1") - expect(page.html).to include("relative link 2") - expect(page.html).to include("relative link 3") + expect(page.html).to include("regular link") + expect(page.html).to include("relative link 1") + expect(page.html).to include("relative link 2") + expect(page.html).to include("relative link 3") end end end diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index a8d6044fda7..8fc94ce09db 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -7,7 +7,7 @@ describe LabelsHelper do context 'without subject' do it "uses the label's project" do - expect(link_to_label(label)).to match %r{.*} + expect(link_to_label(label)).to match %r{.*} end end @@ -32,7 +32,7 @@ describe LabelsHelper do ['issue', :issue, 'merge_request', :merge_request].each do |type| context "set to #{type}" do it 'links to correct page' do - expect(link_to_label(label, type: type)).to match %r{.*} + expect(link_to_label(label, type: type)).to match %r{.*} end end end diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 4b6a351cf70..70eb01c9c44 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -210,11 +210,11 @@ describe MarkupHelper do describe '#cross_project_reference' do it 'shows the full MR reference' do - expect(helper.cross_project_reference(project, merge_request)).to include(project.path_with_namespace) + expect(helper.cross_project_reference(project, merge_request)).to include(project.full_path) end it 'shows the full issue reference' do - expect(helper.cross_project_reference(project, issue)).to include(project.path_with_namespace) + expect(helper.cross_project_reference(project, issue)).to include(project.full_path) end end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 493a4ff9a93..b2a9e277a1c 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -34,8 +34,8 @@ describe MergeRequestsHelper do let(:fork_project) { create(:empty_project, forked_from_project: project) } let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) } subject { format_mr_branch_names(merge_request) } - let(:source_title) { "#{fork_project.path_with_namespace}:#{merge_request.source_branch}" } - let(:target_title) { "#{project.path_with_namespace}:#{merge_request.target_branch}" } + let(:source_title) { "#{fork_project.full_path}:#{merge_request.source_branch}" } + let(:target_title) { "#{project.full_path}:#{merge_request.target_branch}" } it { is_expected.to eq([source_title, target_title]) } end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 45066a60f50..faf26931efc 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -53,13 +53,13 @@ describe ProjectsHelper do end it "returns a valid cach key" do - expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-#{project.commit.id}-readme") + expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-#{project.commit.id}-readme") end it "returns a valid cache key if HEAD does not exist" do allow(project).to receive(:commit) { nil } - expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-nil-readme") + expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-nil-readme") end end diff --git a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb index 27532f96f56..32d027b026b 100644 --- a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb @@ -5,7 +5,7 @@ describe Banzai::Filter::AbstractReferenceFilter do describe '#references_per_project' do it 'returns a Hash containing references grouped per project paths' do - doc = Nokogiri::HTML.fragment("#1 #{project.path_with_namespace}#2") + doc = Nokogiri::HTML.fragment("#1 #{project.full_path}#2") filter = described_class.new(doc, project: project) expect(filter).to receive(:object_class).exactly(4).times.and_return(Issue) @@ -14,7 +14,7 @@ describe Banzai::Filter::AbstractReferenceFilter do refs = filter.references_per_project expect(refs).to be_an_instance_of(Hash) - expect(refs[project.path_with_namespace]).to eq(Set.new(%w[1 2])) + expect(refs[project.full_path]).to eq(Set.new(%w[1 2])) end end @@ -24,10 +24,10 @@ describe Banzai::Filter::AbstractReferenceFilter do filter = described_class.new(doc, project: project) expect(filter).to receive(:references_per_project) - .and_return({ project.path_with_namespace => Set.new(%w[1]) }) + .and_return({ project.full_path => Set.new(%w[1]) }) expect(filter.projects_per_reference) - .to eq({ project.path_with_namespace => project }) + .to eq({ project.full_path => project }) end end @@ -37,7 +37,7 @@ describe Banzai::Filter::AbstractReferenceFilter do context 'with RequestStore disabled' do it 'returns a list of Projects for a list of paths' do - expect(filter.find_projects_for_paths([project.path_with_namespace])) + expect(filter.find_projects_for_paths([project.full_path])) .to eq([project]) end @@ -49,7 +49,7 @@ describe Banzai::Filter::AbstractReferenceFilter do context 'with RequestStore enabled', :request_store do it 'returns a list of Projects for a list of paths' do - expect(filter.find_projects_for_paths([project.path_with_namespace])) + expect(filter.find_projects_for_paths([project.full_path])) .to eq([project]) end @@ -88,7 +88,7 @@ describe Banzai::Filter::AbstractReferenceFilter do doc = Nokogiri::HTML.fragment('') filter = described_class.new(doc, project: project) - expect(filter.current_project_path).to eq(project.path_with_namespace) + expect(filter.current_project_path).to eq(project.full_path) end end end diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb index 11d48387544..935146c17fc 100644 --- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb @@ -100,7 +100,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do context 'cross-project / cross-namespace complete reference' do let(:project2) { create(:project, :public, :repository) } - let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" } + let(:reference) { "#{project2.full_path}@#{commit1.id}...#{commit2.id}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") @@ -113,20 +113,20 @@ describe Banzai::Filter::CommitRangeReferenceFilter do doc = reference_filter("Fixed (#{reference}.)") expect(doc.css('a').first.text) - .to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}") + .to eql("#{project2.full_path}@#{commit1.short_id}...#{commit2.short_id}") end it 'has valid text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.text).to eql("Fixed (#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}.)") + expect(doc.text).to eql("Fixed (#{project2.full_path}@#{commit1.short_id}...#{commit2.short_id}.)") end it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id.reverse}...#{commit2.id}" + exp = act = "Fixed #{project2.full_path}@#{commit1.id.reverse}...#{commit2.id}" expect(reference_filter(act).to_html).to eq exp - exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id}...#{commit2.id.reverse}" + exp = act = "Fixed #{project2.full_path}@#{commit1.id}...#{commit2.id.reverse}" expect(reference_filter(act).to_html).to eq exp end end diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb index fb2a36d1ba1..c7cf1c1d582 100644 --- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb @@ -98,18 +98,18 @@ describe Banzai::Filter::CommitReferenceFilter do let(:namespace) { create(:namespace) } let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } + let(:reference) { "#{project2.full_path}@#{commit.short_id}" } it 'link has valid text' do doc = reference_filter("See (#{reference}.)") - expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}@#{commit.short_id}") + expect(doc.css('a').first.text).to eql("#{project2.full_path}@#{commit.short_id}") end it 'has valid text' do doc = reference_filter("See (#{reference}.)") - expect(doc.text).to eql("See (#{project2.path_with_namespace}@#{commit.short_id}.)") + expect(doc.text).to eql("See (#{project2.full_path}@#{commit.short_id}.)") end it 'ignores invalid commit IDs on the referenced project' do @@ -124,7 +124,7 @@ describe Banzai::Filter::CommitReferenceFilter do let(:project) { create(:empty_project, namespace: namespace) } let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } + let(:reference) { "#{project2.full_path}@#{commit.short_id}" } it 'link has valid text' do doc = reference_filter("See (#{reference}.)") @@ -150,7 +150,7 @@ describe Banzai::Filter::CommitReferenceFilter do let(:project) { create(:empty_project, namespace: namespace) } let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } + let(:reference) { "#{project2.full_path}@#{commit.short_id}" } it 'link has valid text' do doc = reference_filter("See (#{reference}.)") diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index 045bf3e0cc9..024a5cafb41 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -127,7 +127,7 @@ describe Banzai::Filter::IssueReferenceFilter do let(:project2) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project2) } - let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" } + let(:reference) { "#{project2.full_path}##{issue.iid}" } it 'ignores valid references when cross-reference project uses external tracker' do expect_any_instance_of(described_class).to receive(:find_object) @@ -148,13 +148,13 @@ describe Banzai::Filter::IssueReferenceFilter do it 'link has valid text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}##{issue.iid}") + expect(doc.css('a').first.text).to eql("#{project2.full_path}##{issue.iid}") end it 'has valid text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.text).to eq("Fixed (#{project2.path_with_namespace}##{issue.iid}.)") + expect(doc.text).to eq("Fixed (#{project2.full_path}##{issue.iid}.)") end it 'ignores invalid issue IDs on the referenced project' do @@ -171,7 +171,7 @@ describe Banzai::Filter::IssueReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:issue) { create(:issue, project: project2) } - let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" } + let(:reference) { "#{project2.full_path}##{issue.iid}" } it 'ignores valid references when cross-reference project uses external tracker' do expect_any_instance_of(described_class).to receive(:find_object) @@ -324,10 +324,10 @@ describe Banzai::Filter::IssueReferenceFilter do filter = described_class.new(doc, project: project) expect(filter).to receive(:projects_per_reference) - .and_return({ project.path_with_namespace => project }) + .and_return({ project.full_path => project }) expect(filter).to receive(:references_per_project) - .and_return({ project.path_with_namespace => Set.new([issue.iid]) }) + .and_return({ project.full_path => Set.new([issue.iid]) }) expect(filter.issues_per_project) .to eq({ project => { issue.iid => issue } }) diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 1daa6ac7f9e..dfd4c7a7279 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -368,7 +368,7 @@ describe Banzai::Filter::LabelReferenceFilter do describe 'cross-project / cross-namespace complete reference' do let(:project2) { create(:empty_project) } let(:label) { create(:label, project: project2, color: '#00ff00') } - let(:reference) { "#{project2.path_with_namespace}~#{label.name}" } + let(:reference) { "#{project2.full_path}~#{label.name}" } let!(:result) { reference_filter("See #{reference}") } it 'links to a valid reference' do @@ -400,7 +400,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:project) { create(:empty_project, namespace: namespace) } let(:project2) { create(:empty_project, namespace: namespace) } let(:label) { create(:label, project: project2, color: '#00ff00') } - let(:reference) { "#{project2.path_with_namespace}~#{label.name}" } + let(:reference) { "#{project2.full_path}~#{label.name}" } let!(:result) { reference_filter("See #{reference}") } it 'links to a valid reference' do @@ -466,7 +466,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:another_group) { create(:group) } let(:another_project) { create(:empty_project, :public, namespace: another_group) } let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') } - let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" } + let(:reference) { "#{another_project.full_path}~#{group_label.name}" } let!(:result) { reference_filter("See #{reference}", project: project) } it 'points to referenced project issues page' do @@ -501,7 +501,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:project) { create(:empty_project, :public, namespace: group) } let(:another_project) { create(:empty_project, :public, namespace: group) } let(:group_label) { create(:group_label, group: group, color: '#00ff00') } - let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" } + let(:reference) { "#{another_project.full_path}~#{group_label.name}" } let!(:result) { reference_filter("See #{reference}", project: project) } it 'points to referenced project issues page' do @@ -535,7 +535,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:group) { create(:group) } let(:project) { create(:empty_project, :public, namespace: group) } let(:group_label) { create(:group_label, group: group, color: '#00ff00') } - let(:reference) { "#{project.path_with_namespace}~#{group_label.name}" } + let(:reference) { "#{project.full_path}~#{group_label.name}" } let!(:result) { reference_filter("See #{reference}", project: project) } it 'points to referenced project issues page' do diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb index 683972a3112..b693ae3eca2 100644 --- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb @@ -102,7 +102,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do context 'cross-project / cross-namespace complete reference' do let(:project2) { create(:empty_project, :public) } let(:merge) { create(:merge_request, source_project: project2) } - let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" } + let(:reference) { "#{project2.full_path}!#{merge.iid}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") @@ -135,7 +135,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) } let!(:merge) { create(:merge_request, source_project: project2) } - let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" } + let(:reference) { "#{project2.full_path}!#{merge.iid}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 8fe05dc2e53..79ff9419e4b 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -152,7 +152,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do let(:namespace) { create(:namespace) } let(:another_project) { create(:empty_project, :public, namespace: namespace) } let(:milestone) { create(:milestone, project: another_project) } - let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" } + let(:reference) { "#{another_project.full_path}%#{milestone.iid}" } let!(:result) { reference_filter("See #{reference}") } it 'points to referenced project milestone page' do @@ -164,14 +164,14 @@ describe Banzai::Filter::MilestoneReferenceFilter do doc = reference_filter("See (#{reference}.)") expect(doc.css('a').first.text) - .to eq("#{milestone.name} in #{another_project.path_with_namespace}") + .to eq("#{milestone.name} in #{another_project.full_path}") end it 'has valid text' do doc = reference_filter("See (#{reference}.)") expect(doc.text) - .to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)") + .to eq("See (#{milestone.name} in #{another_project.full_path}.)") end it 'escapes the name attribute' do @@ -180,7 +180,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do doc = reference_filter("See #{reference}") expect(doc.css('a').first.text) - .to eq "#{milestone.name} in #{another_project.path_with_namespace}" + .to eq "#{milestone.name} in #{another_project.full_path}" end end @@ -189,7 +189,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:another_project) { create(:empty_project, :public, namespace: namespace) } let(:milestone) { create(:milestone, project: another_project) } - let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" } + let(:reference) { "#{another_project.full_path}%#{milestone.iid}" } let!(:result) { reference_filter("See #{reference}") } it 'points to referenced project milestone page' do @@ -257,4 +257,28 @@ describe Banzai::Filter::MilestoneReferenceFilter do .to eq "#{milestone.name} in #{another_project.path}" end end + + describe 'cross project milestone references' do + let(:another_project) { create(:empty_project, :public) } + let(:project_path) { another_project.full_path } + let(:milestone) { create(:milestone, project: another_project) } + let(:reference) { milestone.to_reference(project) } + + let!(:result) { reference_filter("See #{reference}") } + + it 'points to referenced project milestone page' do + expect(result.css('a').first.attr('href')).to eq urls + .project_milestone_url(another_project, milestone) + end + + it 'contains cross project content' do + expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}" + end + + it 'escapes the name attribute' do + allow_any_instance_of(Milestone).to receive(:title).and_return(%{">whatever Date: Fri, 21 Jul 2017 06:13:26 +0200 Subject: [PATCH 042/108] Rename path_with_namespace -> disk_path when dealing with the filesystem --- app/models/project.rb | 4 ++-- app/services/projects/destroy_service.rb | 2 +- .../projects/import_export/export_service.rb | 2 +- app/services/projects/import_service.rb | 2 +- lib/backup/repository.rb | 4 ++-- lib/gitlab/import_export/repo_restorer.rb | 2 +- spec/factories/projects.rb | 4 ++-- .../gitaly_client/notification_service_spec.rb | 2 +- spec/lib/gitlab/gitaly_client/ref_service_spec.rb | 2 +- spec/models/project_services/jira_service_spec.rb | 6 +++--- spec/models/project_spec.rb | 12 ++++++------ spec/models/repository_spec.rb | 2 +- spec/services/projects/transfer_service_spec.rb | 2 +- spec/tasks/gitlab/backup_rake_spec.rb | 6 +++--- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 7d3c91dc1b9..776b8f4600b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1198,7 +1198,7 @@ class Project < ActiveRecord::Base end def pages_path - File.join(Settings.pages.path, path_with_namespace) + File.join(Settings.pages.path, disk_path) end def public_pages_path @@ -1256,7 +1256,7 @@ class Project < ActiveRecord::Base end def export_path - File.join(Gitlab::ImportExport.storage_path, path_with_namespace) + File.join(Gitlab::ImportExport.storage_path, disk_path) end def export_project_path diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 45af2f7b503..952272903f3 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -40,7 +40,7 @@ module Projects private def repo_path - project.path_with_namespace + project.disk_path end def wiki_path diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index 535da706159..fe4e8ea10bf 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -2,7 +2,7 @@ module Projects module ImportExport class ExportService < BaseService def execute(_options = {}) - @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work')) + @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work')) save_all end diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index cf6a401db2a..50ec3651515 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -51,7 +51,7 @@ module Projects end def clone_repository - gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url) + gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url) end def fetch_repository diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index f8cb8f089e7..8d56b74bfd6 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -142,11 +142,11 @@ module Backup end def path_to_bundle(project) - File.join(backup_repos_path, project.path_with_namespace + '.bundle') + File.join(backup_repos_path, project.disk_path + '.bundle') end def path_to_tars(project, dir = nil) - path = File.join(backup_repos_path, project.path_with_namespace) + path = File.join(backup_repos_path, project.disk_path) if dir File.join(path, "#{dir}.tar") diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb index c824d3ea9fc..32ca2809b2f 100644 --- a/lib/gitlab/import_export/repo_restorer.rb +++ b/lib/gitlab/import_export/repo_restorer.rb @@ -13,7 +13,7 @@ module Gitlab def restore return true unless File.exist?(@path_to_bundle) - gitlab_shell.import_repository(@project.repository_storage_path, @project.path_with_namespace, @path_to_bundle) + gitlab_shell.import_repository(@project.repository_storage_path, @project.disk_path, @path_to_bundle) rescue => e @shared.error(e) false diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 485ed48d2de..a04befe809a 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -64,7 +64,7 @@ FactoryGirl.define do # We delete hooks so that gitlab-shell will not try to authenticate with # an API that isn't running - FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'hooks')) + FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.disk_path}.git", 'hooks')) end end @@ -72,7 +72,7 @@ FactoryGirl.define do after(:create) do |project| raise "Failed to create repository!" unless project.create_repository - FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'refs')) + FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.disk_path}.git", 'refs')) end end diff --git a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb index d9597c4aa78..1bcdd5e5497 100644 --- a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::GitalyClient::NotificationService do describe '#post_receive' do let(:project) { create(:empty_project) } let(:storage_name) { project.repository_storage } - let(:relative_path) { project.path_with_namespace + '.git' } + let(:relative_path) { project.disk_path + '.git' } subject { described_class.new(project.repository) } it 'sends a post_receive message' do diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb index 0b1c890f956..2ad24119476 100644 --- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::GitalyClient::RefService do let(:project) { create(:empty_project) } let(:storage_name) { project.repository_storage } - let(:relative_path) { project.path_with_namespace + '.git' } + let(:relative_path) { project.disk_path + '.git' } let(:client) { described_class.new(project.repository) } describe '#branches' do diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 8f34b44930e..bc9374d6dbb 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 do body: hash_including( GlobalID: "GitLab", object: { - url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{merge_request.diff_head_sha}", + url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/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 } @@ -159,7 +159,7 @@ describe JiraService do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) expect(WebMock).to have_requested(:post, @comment_url).with( - body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ + body: /#{custom_base_url}\/#{project.full_path}\/commit\/#{merge_request.diff_head_sha}/ ).once end @@ -174,7 +174,7 @@ describe JiraService do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) expect(WebMock).to have_requested(:post, @comment_url).with( - body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ + body: /#{Gitlab.config.gitlab.url}\/#{project.full_path}\/commit\/#{merge_request.diff_head_sha}/ ).once end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 7a0508e8758..623d55fe525 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1369,7 +1369,7 @@ describe Project do context 'using a regular repository' do it 'creates the repository' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(true) expect(project.repository).to receive(:after_create) @@ -1379,7 +1379,7 @@ describe Project do it 'adds an error if the repository could not be created' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(false) expect(project.repository).not_to receive(:after_create) @@ -1412,7 +1412,7 @@ describe Project do .and_return(false) allow(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(true) expect(project).to receive(:create_repository).with(force: true) @@ -1436,7 +1436,7 @@ describe Project do .and_return(false) expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(true) project.ensure_repository @@ -1600,7 +1600,7 @@ describe Project do before do allow_any_instance_of(Gitlab::Shell).to receive(:import_repository) - .with(project.repository_storage_path, project.path_with_namespace, project.import_url) + .with(project.repository_storage_path, project.disk_path, project.import_url) .and_return(true) expect_any_instance_of(Repository).to receive(:after_import) @@ -1738,7 +1738,7 @@ describe Project do it 'schedules a RepositoryForkWorker job' do expect(RepositoryForkWorker).to receive(:perform_async) .with(project.id, forked_from_project.repository_storage_path, - forked_from_project.path_with_namespace, project.namespace.full_path) + forked_from_project.disk_path, project.namespace.full_path) project.add_import_job end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 07ed66e127a..0fd3a4d622a 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -962,7 +962,7 @@ describe Repository do end it 'returns false if no full path can be constructed' do - allow(repository).to receive(:path_with_namespace).and_return(nil) + allow(repository).to receive(:full_path).and_return(nil) expect(repository.exists?).to eq(false) end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 36db1aab557..2cb60cbcfc4 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -59,7 +59,7 @@ describe Projects::TransferService do end def project_path(project) - File.join(project.repository_storage_path, "#{project.path_with_namespace}.git") + File.join(project.repository_storage_path, "#{project.disk_path}.git") end def current_path diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 71580a788d0..fae92451b46 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -117,7 +117,7 @@ describe 'gitlab:app namespace rake task' do describe 'backup creation and deletion using custom_hooks' do let(:project) { create(:project, :repository) } - let(:user_backup_path) { "repositories/#{project.path_with_namespace}" } + let(:user_backup_path) { "repositories/#{project.disk_path}" } before(:each) do @origin_cd = Dir.pwd @@ -261,8 +261,8 @@ describe 'gitlab:app namespace rake task' do %W{tar -tvf #{@backup_tar} repositories} ) expect(exit_status).to eq(0) - expect(tar_contents).to match("repositories/#{project_a.path_with_namespace}.bundle") - expect(tar_contents).to match("repositories/#{project_b.path_with_namespace}.bundle") + expect(tar_contents).to match("repositories/#{project_a.disk_path}.bundle") + expect(tar_contents).to match("repositories/#{project_b.disk_path}.bundle") end end end # backup_create task From fb06a4d8fe7bb10c2784f323261cfde04718aec9 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sat, 22 Jul 2017 02:37:22 +0200 Subject: [PATCH 043/108] Rename more path_with_namespace -> full_path or disk_path --- .../concerns/storage/legacy_project_wiki.rb | 9 ++++ .../concerns/storage/legacy_repository.rb | 7 ++++ app/models/project.rb | 10 +++-- app/models/project_wiki.rb | 24 ++++++----- app/models/repository.rb | 22 +++++----- app/services/projects/destroy_service.rb | 2 +- app/uploaders/file_uploader.rb | 2 +- app/views/admin/groups/show.html.haml | 4 +- app/views/import/_githubish_status.html.haml | 2 +- app/views/import/base/create.js.haml | 2 +- app/views/import/bitbucket/status.html.haml | 2 +- app/views/import/fogbugz/status.html.haml | 2 +- app/views/import/gitlab/status.html.haml | 2 +- app/views/import/google_code/status.html.haml | 2 +- app/views/projects/commits/_commit.html.haml | 2 +- .../mattermosts/_team_selection.html.haml | 2 +- .../creations/_new_compare.html.haml | 2 +- .../dropdowns/_project.html.haml | 2 +- .../_detailed_help.html.haml | 2 +- .../slack_slash_commands/_help.html.haml | 2 +- app/views/projects/wikis/git_access.html.haml | 2 +- lib/backup/repository.rb | 6 +-- lib/banzai/filter/relative_link_filter.rb | 2 +- lib/gitlab/github_import/importer.rb | 2 +- lib/gitlab/github_import/wiki_formatter.rb | 4 +- lib/tasks/gitlab/check.rake | 2 +- lib/tasks/gitlab/list_repos.rake | 2 +- lib/tasks/gitlab/shell.rake | 2 +- .../wiki/user_git_access_wiki_page_spec.rb | 2 +- .../banzai/filter/upload_link_filter_spec.rb | 10 ++--- .../gitaly_client/commit_service_spec.rb | 2 +- .../github_import/wiki_formatter_spec.rb | 4 +- spec/models/project_wiki_spec.rb | 18 ++++---- spec/requests/git_http_spec.rb | 42 +++++++++---------- spec/requests/lfs_http_spec.rb | 2 +- spec/requests/request_profiler_spec.rb | 2 +- ...er_registry_authentication_service_spec.rb | 36 ++++++++-------- spec/services/system_note_service_spec.rb | 2 +- 38 files changed, 137 insertions(+), 109 deletions(-) create mode 100644 app/models/concerns/storage/legacy_project_wiki.rb create mode 100644 app/models/concerns/storage/legacy_repository.rb diff --git a/app/models/concerns/storage/legacy_project_wiki.rb b/app/models/concerns/storage/legacy_project_wiki.rb new file mode 100644 index 00000000000..ff82cb0ffa9 --- /dev/null +++ b/app/models/concerns/storage/legacy_project_wiki.rb @@ -0,0 +1,9 @@ +module Storage + module LegacyProjectWiki + extend ActiveSupport::Concern + + def disk_path + project.disk_path + '.wiki' + end + end +end diff --git a/app/models/concerns/storage/legacy_repository.rb b/app/models/concerns/storage/legacy_repository.rb new file mode 100644 index 00000000000..593749bf019 --- /dev/null +++ b/app/models/concerns/storage/legacy_repository.rb @@ -0,0 +1,7 @@ +module Storage + module LegacyRepository + extend ActiveSupport::Concern + + delegate :disk_path, to: :project + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 776b8f4600b..fe85e3e289a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -480,7 +480,7 @@ class Project < ActiveRecord::Base end def repository - @repository ||= Repository.new(path_with_namespace, self) + @repository ||= Repository.new(full_path, disk_path, self) end def container_registry_url @@ -945,7 +945,7 @@ class Project < ActiveRecord::Base end def url_to_repo - gitlab_shell.url_to_repo(path_with_namespace) + gitlab_shell.url_to_repo(full_path) end def repo_exists? @@ -980,8 +980,9 @@ class Project < ActiveRecord::Base # Expires various caches before a project is renamed. def expire_caches_before_rename(old_path) - repo = Repository.new(old_path, self) - wiki = Repository.new("#{old_path}.wiki", self) + # TODO: if we start using UUIDs for cache, we don't need to do this HACK anymore + repo = Repository.new(old_path, old_path, self) + wiki = Repository.new("#{old_path}.wiki", "#{old_path}.wiki", self) if repo.exists? repo.before_delete @@ -1209,6 +1210,7 @@ class Project < ActiveRecord::Base deploy_keys.where(public: false).delete_all end + # TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal? def remove_pages ::Projects::UpdatePagesConfigurationService.new(self).execute diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index b44ee9b1766..2dd49adc880 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -1,5 +1,6 @@ class ProjectWiki include Gitlab::ShellAdapter + include Storage::LegacyProjectWiki MARKUPS = { 'Markdown' => :markdown, @@ -26,16 +27,19 @@ class ProjectWiki @project.path + '.wiki' end - def path_with_namespace + def full_path @project.full_path + '.wiki' end + # @deprecated use full_path when you need it for an URL route or disk_path when you want to point to the filesystem + alias_method :path_with_namespace, :full_path + def web_url Gitlab::Routing.url_helpers.project_wiki_url(@project, :home) end def url_to_repo - gitlab_shell.url_to_repo(path_with_namespace) + gitlab_shell.url_to_repo(full_path) end def ssh_url_to_repo @@ -43,11 +47,11 @@ class ProjectWiki end def http_url_to_repo - "#{Gitlab.config.gitlab.url}/#{path_with_namespace}.git" + "#{Gitlab.config.gitlab.url}/#{full_path}.git" end def wiki_base_path - [Gitlab.config.gitlab.relative_url_root, "/", @project.full_path, "/wikis"].join('') + [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('') end # Returns the Gollum::Wiki object. @@ -134,7 +138,7 @@ class ProjectWiki end def repository - @repository ||= Repository.new(path_with_namespace, @project) + @repository ||= Repository.new(full_path, disk_path, @project) end def default_branch @@ -142,7 +146,7 @@ class ProjectWiki end def create_repo! - if init_repo(path_with_namespace) + if init_repo(disk_path) wiki = Gollum::Wiki.new(path_to_repo) else raise CouldNotCreateWikiError @@ -162,15 +166,15 @@ class ProjectWiki web_url: web_url, git_ssh_url: ssh_url_to_repo, git_http_url: http_url_to_repo, - path_with_namespace: path_with_namespace, + path_with_namespace: full_path, default_branch: default_branch } end private - def init_repo(path_with_namespace) - gitlab_shell.add_repository(project.repository_storage_path, path_with_namespace) + def init_repo(disk_path) + gitlab_shell.add_repository(project.repository_storage_path, disk_path) end def commit_details(action, message = nil, title = nil) @@ -184,7 +188,7 @@ class ProjectWiki end def path_to_repo - @path_to_repo ||= File.join(project.repository_storage_path, "#{path_with_namespace}.git") + @path_to_repo ||= File.join(project.repository_storage_path, "#{disk_path}.git") end def update_project_activity diff --git a/app/models/repository.rb b/app/models/repository.rb index 50b7a477904..5d39e2e271d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -4,7 +4,7 @@ class Repository include Gitlab::ShellAdapter include RepositoryMirroring - attr_accessor :path_with_namespace, :project + attr_accessor :full_path, :disk_path, :project delegate :ref_name_for_sha, to: :raw_repository @@ -52,13 +52,14 @@ class Repository end end - def initialize(path_with_namespace, project) - @path_with_namespace = path_with_namespace + def initialize(full_path, disk_path, project) + @full_path = full_path + @disk_path = disk_path @project = project end def raw_repository - return nil unless path_with_namespace + return nil unless full_path @raw_repository ||= initialize_raw_repository end @@ -66,7 +67,7 @@ class Repository # Return absolute path to repository def path_to_repo @path_to_repo ||= File.expand_path( - File.join(repository_storage_path, path_with_namespace + ".git") + File.join(repository_storage_path, disk_path + '.git') ) end @@ -469,7 +470,7 @@ class Repository # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314 def exists? - return false unless path_with_namespace + return false unless full_path Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| if enabled @@ -1005,7 +1006,7 @@ class Repository end def fetch_remote(remote, forced: false, no_tags: false) - gitlab_shell.fetch_remote(repository_storage_path, path_with_namespace, remote, forced: forced, no_tags: no_tags) + gitlab_shell.fetch_remote(repository_storage_path, disk_path, remote, forced: forced, no_tags: no_tags) end def fetch_ref(source_path, source_ref, target_ref) @@ -1104,7 +1105,8 @@ class Repository end def cache - @cache ||= RepositoryCache.new(path_with_namespace, @project.id) + # TODO: should we use UUIDs here? We could move repositories without clearing this cache + @cache ||= RepositoryCache.new(full_path, @project.id) end def tags_sorted_by_committed_date @@ -1127,7 +1129,7 @@ class Repository end def repository_event(event, tags = {}) - Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags)) + Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags)) end def create_commit(params = {}) @@ -1141,6 +1143,6 @@ class Repository end def initialize_raw_repository - Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git') + Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git') end end diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 952272903f3..b7f4dba08a9 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -127,7 +127,7 @@ module Projects def flush_caches(project) project.repository.before_delete - Repository.new(wiki_path, project).before_delete + Repository.new(wiki_path, repo_path, project).before_delete end end end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 652277e3b78..7027ac4b5db 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -30,7 +30,7 @@ class FileUploader < GitlabUploader # # Returns a String without a trailing slash def self.dynamic_path_segment(model) - File.join(CarrierWave.root, base_dir, model.path_with_namespace) + File.join(CarrierWave.root, base_dir, model.full_path) end attr_accessor :model diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 843c71af466..2aadc071c75 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -70,7 +70,7 @@ %span.badge = storage_counter(project.statistics.storage_size) %span.pull-right.light - %span.monospace= project.path_with_namespace + ".git" + %span.monospace= project.full_path + '.git' .panel-footer = paginate @projects, param_name: 'projects_page', theme: 'gitlab' @@ -88,7 +88,7 @@ %span.badge = storage_counter(project.statistics.storage_size) %span.pull-right.light - %span.monospace= project.path_with_namespace + ".git" + %span.monospace= project.full_path + '.git' .col-md-6 - if can?(current_user, :admin_group_member, @group) diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml index 0e7f0b5ed4f..e9a04e6c122 100644 --- a/app/views/import/_githubish_status.html.haml +++ b/app/views/import/_githubish_status.html.haml @@ -25,7 +25,7 @@ %td = provider_project_link(provider, project.import_source) %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml index fde671e25a9..4dc3a4a0acf 100644 --- a/app/views/import/base/create.js.haml +++ b/app/views/import/base/create.js.haml @@ -4,7 +4,7 @@ job.attr("id", "project_#{@project.id}") target_field = job.find(".import-target") target_field.empty() - target_field.append('#{link_to @project.path_with_namespace, project_path(@project)}') + target_field.append('#{link_to @project.full_path, project_path(@project)}') $("table.import-jobs tbody").prepend(job) job.addClass("active").find(".import-actions").html(" started") - else diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index e6058617ac9..9589e0956f4 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -35,7 +35,7 @@ %td = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer' %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index 5de5da5e6a2..7b832c6a23a 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -33,7 +33,7 @@ %td = project.import_source %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index 7456799ca0e..37734414835 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -28,7 +28,7 @@ %td = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index 60de6bfe816..bc61aeece72 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -38,7 +38,7 @@ %td = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer' %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 12b73ecdf13..e7da47032be 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -5,7 +5,7 @@ - notes = commit.notes - note_count = notes.user.count -- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)] +- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)] - cache_key.push(commit.status(ref)) if commit.status(ref) = cache(cache_key, expires_in: 1.day) do diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml index 3bdb5d0adc4..20acd476f73 100644 --- a/app/views/projects/mattermosts/_team_selection.html.haml +++ b/app/views/projects/mattermosts/_team_selection.html.haml @@ -33,7 +33,7 @@ Suggestions: %code= 'gitlab' %code= @project.path # Path contains no spaces, but dashes - %code= @project.path_with_namespace + %code= @project.full_path %p Reserved: = link_to 'https://docs.mattermost.com/help/messaging/executing-commands.html#built-in-commands', target: '__blank' do diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index 8958b2cf5e1..9d5cebdda53 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -41,7 +41,7 @@ - projects = target_projects(@project) .merge-request-select.dropdown = f.hidden_field :target_project_id - = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } + = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } .dropdown-menu.dropdown-menu-selectable.dropdown-target-project = dropdown_title("Select target project") = dropdown_filter("Search projects") diff --git a/app/views/projects/merge_requests/dropdowns/_project.html.haml b/app/views/projects/merge_requests/dropdowns/_project.html.haml index 25d5dc92f8a..aaf1ab00eeb 100644 --- a/app/views/projects/merge_requests/dropdowns/_project.html.haml +++ b/app/views/projects/merge_requests/dropdowns/_project.html.haml @@ -2,4 +2,4 @@ - projects.each do |project| %li %a{ href: "#", class: "#{('is-active' if selected == project.id)}", data: { id: project.id } } - = project.path_with_namespace + = project.full_path diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml index ef3599460f1..5dbcbf7eba6 100644 --- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml @@ -39,7 +39,7 @@ Suggestions: %code= 'gitlab' %code= @project.path # Path contains no spaces, but dashes - %code= @project.path_with_namespace + %code= @project.full_path .form-group = label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label' diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml index 73b99453a4b..c31c95608c6 100644 --- a/app/views/projects/services/slack_slash_commands/_help.html.haml +++ b/app/views/projects/services/slack_slash_commands/_help.html.haml @@ -33,7 +33,7 @@ Suggestions: %code= 'gitlab' %code= @project.path # Path contains no spaces, but dashes - %code= @project.path_with_namespace + %code= @project.full_path .form-group = label_tag :url, 'URL', class: 'col-sm-2 col-xs-12 control-label' diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index e64dd6085fe..e740fb93ea4 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -7,7 +7,7 @@ .git-access-header Clone repository - %strong= @project_wiki.path_with_namespace + %strong= @project_wiki.full_path = render "shared/clone_panel", project: @project_wiki diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 8d56b74bfd6..05175d51963 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -42,7 +42,7 @@ module Backup path_to_wiki_bundle = path_to_bundle(wiki) if File.exist?(path_to_wiki_repo) - progress.print " * #{wiki.path_with_namespace} ... " + progress.print " * #{wiki.full_path} ... " if empty_repo?(wiki) progress.puts " [SKIPPED]".color(:cyan) else @@ -104,7 +104,7 @@ module Backup path_to_wiki_bundle = path_to_bundle(wiki) if File.exist?(path_to_wiki_bundle) - progress.print " * #{wiki.path_with_namespace} ... " + progress.print " * #{wiki.full_path} ... " # If a wiki bundle exists, first remove the empty repo # that was initialized with ProjectWiki.new() and then @@ -192,7 +192,7 @@ module Backup project_or_wiki.repository.expire_exists_cache # protect backups from stale cache project_or_wiki.repository.empty_repo? rescue => e - progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.path_with_namespace} - #{e.message}".color(:orange) + progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.full_path} - #{e.message}".color(:orange) false end diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index c2fed57a0d8..758f15c8a67 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -51,7 +51,7 @@ module Banzai uri.path = [ relative_url_root, - context[:project].path_with_namespace, + context[:project].full_path, uri_type(file_path), Addressable::URI.escape(ref), Addressable::URI.escape(file_path) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index a8c0b47e786..266b1a6fece 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -254,7 +254,7 @@ module Gitlab def import_wiki unless project.wiki.repository_exists? wiki = WikiFormatter.new(project) - gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) + gitlab_shell.import_repository(project.repository_storage_path, wiki.disk_path, wiki.import_url) end rescue Gitlab::Shell::Error => e # GitHub error message when the wiki repo has not been created, diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb index 30cff1ba804..0396122eeb9 100644 --- a/lib/gitlab/github_import/wiki_formatter.rb +++ b/lib/gitlab/github_import/wiki_formatter.rb @@ -7,8 +7,8 @@ module Gitlab @project = project end - def path_with_namespace - "#{project.full_path}.wiki" + def disk_path + "#{project.disk_path}.wiki" end def import_url diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 858f1cd7b34..dbb3b827b9a 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -527,7 +527,7 @@ namespace :gitlab do repo_dirs = user.authorized_projects.map do |p| File.join( p.repository_storage_path, - "#{p.path_with_namespace}.git" + "#{p.disk_path}.git" ) end diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake index ffcc76e5498..b732db9db6e 100644 --- a/lib/tasks/gitlab/list_repos.rake +++ b/lib/tasks/gitlab/list_repos.rake @@ -9,7 +9,7 @@ namespace :gitlab do scope = scope.where('id IN (?) OR namespace_id in (?)', project_ids, namespace_ids) end scope.find_each do |project| - base = File.join(project.repository_storage_path, project.path_with_namespace) + base = File.join(project.repository_storage_path, project.disk_path) puts base + '.git' puts base + '.wiki.git' end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index ee2cdcdea1b..42825f29e32 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -80,7 +80,7 @@ namespace :gitlab do print '-' else if Gitlab::Shell.new.add_repository(project.repository_storage_path, - project.path_with_namespace) + project.disk_path) print '.' else print 'F' diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb index 9c58e336605..f1e7f5f2be8 100644 --- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb @@ -20,7 +20,7 @@ describe 'Projects > Wiki > User views Git access wiki page' do visit project_wiki_path(project, wiki_page) click_link 'Clone repository' - expect(page).to have_text("Clone repository #{project.wiki.path_with_namespace}") + expect(page).to have_text("Clone repository #{project.wiki.full_path}") expect(page).to have_text(project.wiki.http_url_to_repo) end end diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb index 3bc9635b50e..74a23a9ab5e 100644 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb @@ -52,21 +52,21 @@ describe Banzai::Filter::UploadLinkFilter do it 'rebuilds relative URL for a link' do doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('a')['href']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('a')['href']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" end it 'rebuilds relative URL for an image' do doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('img')['src']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('img')['src']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" end it 'does not modify absolute URL' do @@ -85,7 +85,7 @@ describe Banzai::Filter::UploadLinkFilter do .to receive(:image?).with(path).and_return(true) doc = filter(image(escaped)) - expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png" + expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png" end end diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 0868c793a33..d71e0f84c65 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::GitalyClient::CommitService do let(:project) { create(:project, :repository) } let(:storage_name) { project.repository_storage } - let(:relative_path) { project.path_with_namespace + '.git' } + let(:relative_path) { project.disk_path + '.git' } let(:repository) { project.repository } let(:repository_message) { repository.gitaly_repository } let(:revision) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index de50265bc14..fcd90fab547 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -9,9 +9,9 @@ describe Gitlab::GithubImport::WikiFormatter do subject(:wiki) { described_class.new(project) } - describe '#path_with_namespace' do + describe '#disk_path' do it 'appends .wiki to project path' do - expect(wiki.path_with_namespace).to eq 'gitlabhq/gitlabhq.wiki' + expect(wiki.disk_path).to eq project.disk_path + '.wiki' end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index c6ceb092810..484fcfc88a3 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -15,19 +15,23 @@ describe ProjectWiki do describe "#path_with_namespace" do it "returns the project path with namespace with the .wiki extension" do - expect(subject.path_with_namespace).to eq(project.path_with_namespace + ".wiki") + expect(subject.path_with_namespace).to eq(project.full_path + '.wiki') + end + + it 'returns the same value as #full_path' do + expect(subject.path_with_namespace).to eq(subject.full_path) end end describe '#web_url' do it 'returns the full web URL to the wiki' do - expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/wikis/home") + expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/wikis/home") end end describe "#url_to_repo" do it "returns the correct ssh url to the repo" do - expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace)) + expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.full_path)) end end @@ -41,7 +45,7 @@ describe ProjectWiki do let(:project) { create :empty_project } it 'returns the full http url to the repo' do - expected_url = "#{Gitlab.config.gitlab.url}/#{subject.path_with_namespace}.git" + expected_url = "#{Gitlab.config.gitlab.url}/#{subject.full_path}.git" expect(project_wiki.http_url_to_repo).to eq(expected_url) expect(project_wiki.http_url_to_repo).not_to include('@') @@ -50,7 +54,7 @@ describe ProjectWiki do describe "#wiki_base_path" do it "returns the wiki base path" do - wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.path_with_namespace}/wikis" + wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.full_path}/wikis" expect(subject.wiki_base_path).to eq(wiki_base_path) end @@ -77,7 +81,7 @@ describe ProjectWiki do allow_any_instance_of(Gitlab::Shell).to receive(:add_repository) do create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git") end - allow(project).to receive(:path_with_namespace).and_return("non-existant") + allow(project).to receive(:full_path).and_return("non-existant") end describe '#empty?' do @@ -269,7 +273,7 @@ describe ProjectWiki do describe '#create_repo!' do it 'creates a repository' do expect(subject).to receive(:init_repo) - .with(subject.path_with_namespace) + .with(subject.full_path) .and_return(true) expect(subject.repository).to receive(:after_create) diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index d4a3e8b13e1..d5c16d8f601 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -123,7 +123,7 @@ describe 'Git HTTP requests' do context "when requesting the Wiki" do let(:wiki) { ProjectWiki.new(project) } - let(:path) { "/#{wiki.repository.path_with_namespace}.git" } + let(:path) { "/#{wiki.repository.full_path}.git" } context "when the project is public" do let(:project) { create(:project, :repository, :public, :wiki_enabled) } @@ -139,7 +139,7 @@ describe 'Git HTTP requests' do download(path) do |response| json_body = ActiveSupport::JSON.decode(response.body) - expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace) + expect(json_body['RepoPath']).to include(wiki.repository.full_path) end end end @@ -222,7 +222,7 @@ describe 'Git HTTP requests' do end context "when the project exists" do - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } context "when the project is public" do let(:project) { create(:project, :repository, :public) } @@ -286,7 +286,7 @@ describe 'Git HTTP requests' do context 'when the request is not from gitlab-workhorse' do it 'raises an exception' do expect do - get("/#{project.path_with_namespace}.git/info/refs?service=git-upload-pack") + get("/#{project.full_path}.git/info/refs?service=git-upload-pack") end.to raise_error(JWT::DecodeError) end end @@ -294,7 +294,7 @@ describe 'Git HTTP requests' do context 'when the repo is public' do context 'but the repo is disabled' do let(:project) { create(:project, :public, :repository, :repository_disabled) } - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { {} } it_behaves_like 'pulls require Basic HTTP Authentication' @@ -303,7 +303,7 @@ describe 'Git HTTP requests' do context 'but the repo is enabled' do let(:project) { create(:project, :public, :repository, :repository_enabled) } - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { {} } it_behaves_like 'pulls are allowed' @@ -421,7 +421,7 @@ describe 'Git HTTP requests' do @token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") end - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { { user: 'oauth2', password: @token.token } } it_behaves_like 'pulls are allowed' @@ -431,7 +431,7 @@ describe 'Git HTTP requests' do context 'when user has 2FA enabled' do let(:user) { create(:user, :two_factor) } let(:access_token) { create(:personal_access_token, user: user) } - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } before do project.team << [user, :master] @@ -580,7 +580,7 @@ describe 'Git HTTP requests' do end context 'when build created by system is authenticated' do - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { { user: 'gitlab-ci-token', password: build.token } } it_behaves_like 'pulls are allowed' @@ -602,7 +602,7 @@ describe 'Git HTTP requests' do # We are "authenticated" as CI using a valid token here. But we are # not authorized to see any other project, so return "not found". it "rejects pulls for other project with 404 Not Found" do - clone_get("#{other_project.path_with_namespace}.git", env) + clone_get("#{other_project.full_path}.git", env) expect(response).to have_http_status(:not_found) expect(response.body).to eq(git_access_error(:project_not_found)) @@ -616,7 +616,7 @@ describe 'Git HTTP requests' do end shared_examples 'can download code only' do - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { { user: 'gitlab-ci-token', password: build.token } } it_behaves_like 'pulls are allowed' @@ -646,7 +646,7 @@ describe 'Git HTTP requests' do it_behaves_like 'can download code only' it 'downloads from other project get status 403' do - clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token + clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token expect(response).to have_http_status(:forbidden) end @@ -658,7 +658,7 @@ describe 'Git HTTP requests' do it_behaves_like 'can download code only' it 'downloads from other project get status 404' do - clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token + clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token expect(response).to have_http_status(:not_found) end @@ -671,7 +671,7 @@ describe 'Git HTTP requests' do let(:project) { create(:project, :repository, :public, path: 'project.git-project') } context "GET info/refs" do - let(:path) { "/#{project.path_with_namespace}/info/refs" } + let(:path) { "/#{project.full_path}/info/refs" } context "when no params are added" do before do @@ -679,7 +679,7 @@ describe 'Git HTTP requests' do end it "redirects to the .git suffix version" do - expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs") + expect(response).to redirect_to("/#{project.full_path}.git/info/refs") end end @@ -691,7 +691,7 @@ describe 'Git HTTP requests' do end it "redirects to the .git suffix version" do - expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}") + expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}") end end @@ -703,7 +703,7 @@ describe 'Git HTTP requests' do end it "redirects to the .git suffix version" do - expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}") + expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}") end end @@ -722,13 +722,13 @@ describe 'Git HTTP requests' do context "POST git-upload-pack" do it "fails to find a route" do - expect { clone_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError) + expect { clone_post(project.full_path) }.to raise_error(ActionController::RoutingError) end end context "POST git-receive-pack" do it "fails to find a route" do - expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError) + expect { push_post(project.full_path) }.to raise_error(ActionController::RoutingError) end end end @@ -744,7 +744,7 @@ describe 'Git HTTP requests' do Blob.decorate(Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt'), project) end - get "/#{project.path_with_namespace}/blob/master/info/refs" + get "/#{project.full_path}/blob/master/info/refs" end it "returns the file" do @@ -754,7 +754,7 @@ describe 'Git HTTP requests' do context "when the file does not exist" do before do - get "/#{project.path_with_namespace}/blob/master/info/refs" + get "/#{project.full_path}/blob/master/info/refs" end it "returns not found" do diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 697b150ab34..4f1a90750b3 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -701,7 +701,7 @@ describe 'Git LFS API and storage' do expect(json_response['objects']).to be_kind_of(Array) expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(json_response['objects'].first['size']).to eq(1575078) - expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization) end end diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb index 51fbfecec4b..9afeb2983b0 100644 --- a/spec/requests/request_profiler_spec.rb +++ b/spec/requests/request_profiler_spec.rb @@ -15,7 +15,7 @@ describe 'Request Profiler' do it 'creates a profile of the request' do project = create(:project, namespace: user.namespace) time = Time.now - path = "/#{project.path_with_namespace}" + path = "/#{project.full_path}" Timecop.freeze(time) do get path, nil, 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index 66a8a93b168..0ae839ce0b3 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -46,7 +46,7 @@ describe Auth::ContainerRegistryAuthenticationService do shared_examples 'an accessible' do let(:access) do [{ 'type' => 'repository', - 'name' => project.path_with_namespace, + 'name' => project.full_path, 'actions' => actions }] end @@ -97,7 +97,7 @@ describe Auth::ContainerRegistryAuthenticationService do describe '#full_access_token' do let(:project) { create(:empty_project) } - let(:token) { described_class.full_access_token(project.path_with_namespace) } + let(:token) { described_class.full_access_token(project.full_path) } subject { { token: token } } @@ -124,7 +124,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'a pushable' @@ -138,7 +138,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pulling from root level repository' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -152,7 +152,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push,pull" } + { scope: "repository:#{project.full_path}:push,pull" } end it_behaves_like 'a pullable' @@ -165,7 +165,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -178,7 +178,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'allow anyone to pull images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -187,7 +187,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to push images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'an inaccessible' @@ -210,7 +210,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'for internal user' do context 'allow anyone to pull images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -219,7 +219,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to push images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'an inaccessible' @@ -230,7 +230,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'for external user' do let(:current_user) { create(:user, external: true) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -255,7 +255,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'allow to pull and push images' do let(:current_params) do - { scope: "repository:#{current_project.path_with_namespace}:pull,push" } + { scope: "repository:#{current_project.full_path}:pull,push" } end it_behaves_like 'a pullable and pushable' do @@ -270,7 +270,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'for other projects' do context 'when pulling' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end context 'allow for public' do @@ -337,7 +337,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end context 'disallow for all' do @@ -371,7 +371,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow when pulling' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'an inaccessible' @@ -399,7 +399,7 @@ describe Auth::ContainerRegistryAuthenticationService do let(:project) { create(:empty_project, :private) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a forbidden' @@ -410,7 +410,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pulling and pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'a pullable' @@ -419,7 +419,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'a forbidden' diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 7129d282df1..5c20263a532 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -873,7 +873,7 @@ describe SystemNoteService do describe "existing reference" do before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) - message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.full_path}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]:\n'#{commit.title.chomp}'" + message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.full_path}|http://localhost/#{project.full_path}/commit/#{commit.id}]:\n'#{commit.title.chomp}'" allow_any_instance_of(JIRA::Resource::Issue).to receive(:comments).and_return([OpenStruct.new(body: message)]) end From 16107364b8f0904e25a70dac29a26c435118fb29 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sun, 23 Jul 2017 09:05:34 +0200 Subject: [PATCH 044/108] Make disk_path keyword argument and optional --- app/models/project.rb | 7 +++---- app/models/project_wiki.rb | 2 +- app/models/repository.rb | 4 ++-- app/services/projects/destroy_service.rb | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index fe85e3e289a..dd025f5574d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -480,7 +480,7 @@ class Project < ActiveRecord::Base end def repository - @repository ||= Repository.new(full_path, disk_path, self) + @repository ||= Repository.new(full_path, self, disk_path: disk_path) end def container_registry_url @@ -980,9 +980,8 @@ class Project < ActiveRecord::Base # Expires various caches before a project is renamed. def expire_caches_before_rename(old_path) - # TODO: if we start using UUIDs for cache, we don't need to do this HACK anymore - repo = Repository.new(old_path, old_path, self) - wiki = Repository.new("#{old_path}.wiki", "#{old_path}.wiki", self) + repo = Repository.new(old_path, self) + wiki = Repository.new("#{old_path}.wiki", self) if repo.exists? repo.before_delete diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 2dd49adc880..e8929a35836 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -138,7 +138,7 @@ class ProjectWiki end def repository - @repository ||= Repository.new(full_path, disk_path, @project) + @repository ||= Repository.new(full_path, @project, disk_path: disk_path) end def default_branch diff --git a/app/models/repository.rb b/app/models/repository.rb index 5d39e2e271d..7ea9f1459a0 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -52,9 +52,9 @@ class Repository end end - def initialize(full_path, disk_path, project) + def initialize(full_path, project, disk_path: nil) @full_path = full_path - @disk_path = disk_path + @disk_path = disk_path || full_path @project = project end diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index b7f4dba08a9..11ad4838471 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -127,7 +127,7 @@ module Projects def flush_caches(project) project.repository.before_delete - Repository.new(wiki_path, repo_path, project).before_delete + Repository.new(wiki_path, project, disk_path: repo_path).before_delete end end end From 07b574b41cb0ff4518895ea4e6c719e453fdc4e3 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 24 Jul 2017 01:41:58 +0200 Subject: [PATCH 045/108] Added some extra TODOs for the Legacy Storage refactor --- app/services/projects/transfer_service.rb | 2 ++ lib/gitlab/import_export/uploads_saver.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 386d49e58e7..5957f612e84 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -61,11 +61,13 @@ module Projects project.send_move_instructions(@old_path) # Move main repository + # TODO: check storage type and NOOP when not using Legacy unless move_repo_folder(@old_path, @new_path) raise TransferError.new('Cannot move project') end # Move wiki repo also if present + # TODO: check storage type and NOOP when not using Legacy move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki") # Move missing group labels to project diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb index 62a2553675c..f9ae5079d7c 100644 --- a/lib/gitlab/import_export/uploads_saver.rb +++ b/lib/gitlab/import_export/uploads_saver.rb @@ -24,6 +24,7 @@ module Gitlab end def uploads_path + # TODO: decide what to do with uploads. We will use UUIDs here too? File.join(Rails.root.join('public/uploads'), @project.path_with_namespace) end end From 597e619080c393daa765bf6e5de74f60121d1f0e Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 1 Aug 2017 07:44:25 +0200 Subject: [PATCH 046/108] Rename ensure_dir_exist -> ensure_storage_path_exist --- app/models/concerns/storage/legacy_project.rb | 2 +- app/models/project.rb | 4 ++-- lib/backup/repository.rb | 2 +- .../cleanup_namespaceless_pending_delete_projects_spec.rb | 2 +- spec/workers/namespaceless_project_destroy_worker_spec.rb | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/models/concerns/storage/legacy_project.rb b/app/models/concerns/storage/legacy_project.rb index b537f40548e..815db712285 100644 --- a/app/models/concerns/storage/legacy_project.rb +++ b/app/models/concerns/storage/legacy_project.rb @@ -6,7 +6,7 @@ module Storage full_path end - def ensure_dir_exist + def ensure_storage_path_exist gitlab_shell.add_namespace(repository_storage_path, namespace.full_path) end diff --git a/app/models/project.rb b/app/models/project.rb index dd025f5574d..d7e9e2ac973 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -44,7 +44,7 @@ class Project < ActiveRecord::Base default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :only_allow_merge_if_all_discussions_are_resolved, false - after_create :ensure_dir_exist + after_create :ensure_storage_path_exist after_create :create_project_feature, unless: :project_feature after_save :update_project_statistics, if: :namespace_id_changed? @@ -69,7 +69,7 @@ class Project < ActiveRecord::Base # Legacy Storage specific hooks - after_save :ensure_dir_exist, if: :namespace_id_changed? + after_save :ensure_storage_path_exist, if: :namespace_id_changed? acts_as_taggable diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 05175d51963..88821ae56e0 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -75,7 +75,7 @@ module Backup path_to_project_repo = path_to_repo(project) path_to_project_bundle = path_to_bundle(project) - project.ensure_dir_exist + project.ensure_storage_path_exist cmd = if File.exist?(path_to_project_bundle) %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo}) diff --git a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb index 49e750a3f4d..dd634f2c024 100644 --- a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb +++ b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb @@ -4,7 +4,7 @@ require Rails.root.join('db', 'post_migrate', '20170502101023_cleanup_namespacel describe CleanupNamespacelessPendingDeleteProjects do before do # Stub after_save callbacks that will fail when Project has no namespace - allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil) + allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil) allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil) end diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb index 8533b7b85e9..f9e23d648ec 100644 --- a/spec/workers/namespaceless_project_destroy_worker_spec.rb +++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb @@ -5,7 +5,7 @@ describe NamespacelessProjectDestroyWorker do before do # Stub after_save callbacks that will fail when Project has no namespace - allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil) + allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil) allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil) end From 2c2609620356a4693ec96f988bc96cb4601be5e8 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 1 Aug 2017 07:45:04 +0200 Subject: [PATCH 047/108] Small refactor in LegacyNamespace and moved back send_update_instructions --- app/models/concerns/storage/legacy_namespace.rb | 11 ++--------- app/models/namespace.rb | 6 ++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index 5cbd52e5c75..5ab5c80a2f5 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -7,7 +7,7 @@ module Storage raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry') end - # Move the namespace directory in all storages paths used by member projects + # Move the namespace directory in all storage paths used by member projects repository_storage_paths.each do |repository_storage_path| # Ensure old directory exists before moving it gitlab_shell.add_namespace(repository_storage_path, full_path_was) @@ -49,12 +49,6 @@ module Storage private - def send_update_instructions - projects.each do |project| - project.send_move_instructions("#{full_path_was}/#{project.path}") - end - end - def old_repository_storage_paths @old_repository_storage_paths ||= repository_storage_paths end @@ -76,8 +70,7 @@ module Storage new_path = "#{full_path}+#{id}+deleted" if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path) - message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\"" - Gitlab::AppLogger.info message + Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}") # Remove namespace directroy async with delay so # GitLab has time to remove all projects first diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 010c97c507e..6073fb94a3f 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -124,6 +124,12 @@ class Namespace < ActiveRecord::Base all_projects.any?(&:has_container_registry_tags?) end + def send_update_instructions + projects.each do |project| + project.send_move_instructions("#{full_path_was}/#{project.path}") + end + end + def kind type == 'Group' ? 'group' : 'user' end From 0d35b0818026e9a2528b44f9e72af76b98f1a162 Mon Sep 17 00:00:00 2001 From: "Lin Jen-Shin (godfat)" Date: Tue, 1 Aug 2017 07:46:13 +0000 Subject: [PATCH 048/108] Allow logged in users to read user list under public restriction --- app/policies/global_policy.rb | 2 +- ...allow-logged-in-user-to-read-user-list.yml | 4 ++ spec/requests/api/users_spec.rb | 40 +++++++++++-------- 3 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 1c91425f589..1be7bbe9953 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -44,7 +44,7 @@ class GlobalPolicy < BasePolicy prevent :log_in end - rule { admin | ~restricted_public_level }.policy do + rule { ~(anonymous & restricted_public_level) }.policy do enable :read_users_list end end diff --git a/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml b/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml new file mode 100644 index 00000000000..54b2e71bef9 --- /dev/null +++ b/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml @@ -0,0 +1,4 @@ +--- +title: Allow any logged in users to read_users_list even if it's restricted +merge_request: 13201 +author: diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 66b165b438b..2dc7be22f8f 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -16,38 +16,44 @@ describe API::Users do it "returns authorization error when the `username` parameter is not passed" do get api("/users") - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end it "returns the user when a valid `username` parameter is passed" do - user = create(:user) - get api("/users"), username: user.username - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response[0]['id']).to eq(user.id) expect(json_response[0]['username']).to eq(user.username) end - it "returns authorization error when the `username` parameter refers to an inaccessible user" do - user = create(:user) - - stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) - - get api("/users"), username: user.username - - expect(response).to have_http_status(403) - end - it "returns an empty response when an invalid `username` parameter is passed" do get api("/users"), username: 'invalid' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(0) end + + context "when public level is restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it "returns authorization error when the `username` parameter refers to an inaccessible user" do + get api("/users"), username: user.username + + expect(response).to have_gitlab_http_status(403) + end + + it "returns authorization error when the `username` parameter is not passed" do + get api("/users") + + expect(response).to have_gitlab_http_status(403) + end + end end context "when authenticated" do @@ -58,10 +64,10 @@ describe API::Users do end context 'when authenticate as a regular user' do - it "renders 403" do + it "renders 200" do get api("/users", user) - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(200) end end From 20bfc4f679bd63f71af716d4910c5c22e33180c0 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 1 Aug 2017 08:49:03 +0100 Subject: [PATCH 049/108] added mouseleave timeout with JS --- app/assets/javascripts/fly_out_nav.js | 86 ++++++++++++--- app/assets/stylesheets/new_sidebar.scss | 3 +- spec/javascripts/fly_out_nav_spec.js | 133 +++++++++++++++++++++++- 3 files changed, 204 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index f2151396d43..93101f123b5 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -1,3 +1,16 @@ +let hideTimeoutInterval = 0; +let hideTimeout; +let subitems; + +export const getHideTimeoutInterval = () => hideTimeoutInterval; + +export const hideAllSubItems = () => { + subitems.forEach((el) => { + el.parentNode.classList.remove('is-over'); + el.style.display = 'none'; // eslint-disable-line no-param-reassign + }); +}; + export const calculateTop = (boundingRect, outerHeight) => { const windowHeight = window.innerHeight; const bottomOverflow = windowHeight - (boundingRect.top + outerHeight); @@ -6,23 +19,64 @@ export const calculateTop = (boundingRect, outerHeight) => { boundingRect.top; }; -export default () => { - $('.sidebar-top-level-items > li:not(.active)').on('mouseover', (e) => { - const $this = e.currentTarget; - const $subitems = $('.sidebar-sub-level-items', $this).show(); +export const showSubLevelItems = (el) => { + const $subitems = el.querySelector('.sidebar-sub-level-items'); - if ($subitems.length) { - const boundingRect = $this.getBoundingClientRect(); - const top = calculateTop(boundingRect, $subitems.outerHeight()); - const isAbove = top < boundingRect.top; + if (!$subitems) return; - $subitems.css({ - transform: `translate3d(0, ${top}px, 0)`, - }); + hideAllSubItems(); - if (isAbove) { - $subitems.addClass('is-above'); - } - } - }).on('mouseout', e => $('.sidebar-sub-level-items', e.currentTarget).hide().removeClass('is-above')); + if (el.classList.contains('is-over')) { + clearTimeout(hideTimeout); + } else { + $subitems.style.display = 'block'; + el.classList.add('is-over'); + } + + const boundingRect = el.getBoundingClientRect(); + const top = calculateTop(boundingRect, $subitems.offsetHeight); + const isAbove = top < boundingRect.top; + + $subitems.style.transform = `translate3d(0, ${top}px, 0)`; + + if (isAbove) { + $subitems.classList.add('is-above'); + } +}; + +export const hideSubLevelItems = (el) => { + const $subitems = el.querySelector('.sidebar-sub-level-items'); + const hideFn = () => { + el.classList.remove('is-over'); + $subitems.style.display = 'none'; + $subitems.classList.remove('is-above'); + + hideTimeoutInterval = 0; + }; + + if ($subitems && hideTimeoutInterval) { + hideTimeout = setTimeout(hideFn, hideTimeoutInterval); + } else if ($subitems) { + hideFn(); + } +}; + +export const setMouseOutTimeout = (el) => { + if (el.closest('.sidebar-sub-level-items')) { + hideTimeoutInterval = 250; + } else { + hideTimeoutInterval = 0; + } +}; + +export default () => { + const items = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active)')]; + subitems = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active) .sidebar-sub-level-items')]; + + items.forEach((el) => { + el.addEventListener('mouseenter', e => showSubLevelItems(e.currentTarget)); + el.addEventListener('mouseleave', e => hideSubLevelItems(e.currentTarget)); + }); + + subitems.forEach(el => el.addEventListener('mouseleave', e => setMouseOutTimeout(e.target))); }; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 05b72e9f425..72c12413aba 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -292,7 +292,8 @@ $new-sidebar-width: 220px; } &:not(.active):hover > a, - > a:hover { + > a:hover, + &.is-over > a { background-color: $white-light; } } diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index d3c6dafe460..0fdaa2d8663 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -1,6 +1,22 @@ -import { calculateTop } from '~/fly_out_nav'; +import { + calculateTop, + setMouseOutTimeout, + getHideTimeoutInterval, + hideSubLevelItems, + showSubLevelItems, +} from '~/fly_out_nav'; describe('Fly out sidebar navigation', () => { + let el; + beforeEach(() => { + el = document.createElement('div'); + document.body.appendChild(el); + }); + + afterEach(() => { + el.remove(); + }); + describe('calculateTop', () => { it('returns boundingRect top', () => { const boundingRect = { @@ -24,4 +40,119 @@ describe('Fly out sidebar navigation', () => { ).toBe(window.innerHeight - 50); }); }); + + describe('setMouseOutTimeout', () => { + it('sets hideTimeoutInterval to 150 when inside sub items', () => { + el.innerHTML = ''; + + setMouseOutTimeout(el.querySelector('.js-test')); + + expect( + getHideTimeoutInterval(), + ).toBe(150); + }); + + it('resets hideTimeoutInterval when not inside sub items', () => { + setMouseOutTimeout(el); + + expect( + getHideTimeoutInterval(), + ).toBe(0); + }); + }); + + describe('hideSubLevelItems', () => { + beforeEach(() => { + el.innerHTML = ''; + }); + + it('hides subitems', () => { + hideSubLevelItems(el); + + expect( + el.querySelector('.sidebar-sub-level-items').style.display, + ).toBe('none'); + }); + + it('removes is-over class', () => { + spyOn(el.classList, 'remove'); + + hideSubLevelItems(el); + + expect( + el.classList.remove, + ).toHaveBeenCalledWith('is-over'); + }); + + it('removes is-above class from sub-items', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); + + spyOn(subItems.classList, 'remove'); + + hideSubLevelItems(el); + + expect( + subItems.classList.remove, + ).toHaveBeenCalledWith('is-above'); + }); + + it('does nothing if el has no sub-items', () => { + el.innerHTML = ''; + + spyOn(el.classList, 'remove'); + + hideSubLevelItems(el); + + expect( + el.classList.remove, + ).not.toHaveBeenCalledWith(); + }); + }); + + describe('showSubLevelItems', () => { + beforeEach(() => { + el.innerHTML = ''; + }); + + it('adds is-over class to el', () => { + spyOn(el.classList, 'add'); + + showSubLevelItems(el); + + expect( + el.classList.add, + ).toHaveBeenCalledWith('is-over'); + }); + + it('shows sub-items', () => { + showSubLevelItems(el); + + expect( + el.querySelector('.sidebar-sub-level-items').style.display, + ).toBe('block'); + }); + + it('sets transform of sub-items', () => { + showSubLevelItems(el); + + expect( + el.querySelector('.sidebar-sub-level-items').style.transform, + ).toBe(`translate3d(0px, ${el.offsetTop}px, 0px)`); + }); + + it('sets is-above when element is above', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); + subItems.style.height = '5000px'; + el.style.position = 'relative'; + el.style.top = '1000px'; + + spyOn(el.classList, 'add'); + + showSubLevelItems(el); + + expect( + el.classList.add, + ).toHaveBeenCalledWith('is-above'); + }); + }); }); From d608aa5846a65edc133deeef886af111a9f580e1 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 1 Aug 2017 17:42:05 +0900 Subject: [PATCH 050/108] fix --- spec/services/ci/pipeline_trigger_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb index 945a2fe1a6b..e9188a83b89 100644 --- a/spec/services/ci/pipeline_trigger_service_spec.rb +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::PipelineTriggerService, services: true do +describe Ci::PipelineTriggerService do let(:project) { create(:project, :repository) } before do From e99564568b2fefab8973ce571594aaa888cf8494 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 31 Jul 2017 15:17:14 +0200 Subject: [PATCH 051/108] CI fixes for gitaly-ruby --- .gitlab-ci.yml | 3 +++ lib/tasks/gitlab/gitaly.rake | 12 +++++++++--- scripts/gitaly-test-build | 10 ++++++++++ scripts/gitaly-test-spawn | 7 +++++++ spec/support/test_env.rb | 11 +++++++---- spec/tasks/gitlab/gitaly_rake_spec.rb | 18 ++++++++++++++---- 6 files changed, 50 insertions(+), 11 deletions(-) create mode 100755 scripts/gitaly-test-build create mode 100755 scripts/gitaly-test-spawn diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index adde3400107..27fdf6ca0b5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,6 +96,7 @@ stages: - export KNAPSACK_GENERATE_REPORT=true - export CACHE_CLASSES=true - cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH} + - scripts/gitaly-test-spawn - knapsack rspec "--color --format documentation" artifacts: expire_in: 31d @@ -221,6 +222,7 @@ setup-test-env: - bundle exec rake gettext:po_to_json - bundle exec rake gitlab:assets:compile - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' + - scripts/gitaly-test-build # Do not use 'bundle exec' here artifacts: expire_in: 7d paths: @@ -486,6 +488,7 @@ karma: BABEL_ENV: "coverage" CHROME_LOG_FILE: "chrome_debug.log" script: + - scripts/gitaly-test-spawn - bundle exec rake gettext:po_to_json - bundle exec rake karma coverage: '/^Statements *: (\d+\.\d+%)/' diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake index 9df07ea8d83..680e76af471 100644 --- a/lib/tasks/gitlab/gitaly.rake +++ b/lib/tasks/gitlab/gitaly.rake @@ -19,7 +19,10 @@ namespace :gitlab do Dir.chdir(args.dir) do create_gitaly_configuration - Bundler.with_original_env { run_command!([command]) } + # In CI we run scripts/gitaly-test-build instead of this command + unless ENV['CI'].present? + Bundler.with_original_env { run_command!(%w[/usr/bin/env -u BUNDLE_GEMFILE] + [command]) } + end end end @@ -30,7 +33,9 @@ namespace :gitlab do puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}" puts "# This is in TOML format suitable for use in Gitaly's config.toml file." - puts gitaly_configuration_toml + # Exclude gitaly-ruby configuration because that depends on the gitaly + # installation directory. + puts gitaly_configuration_toml(gitaly_ruby: false) end private @@ -41,7 +46,7 @@ namespace :gitlab do # only generate a configuration for the most common and simplest case: when # we have exactly one Gitaly process and we are sure it is running locally # because it uses a Unix socket. - def gitaly_configuration_toml + def gitaly_configuration_toml(gitaly_ruby: true) storages = [] address = nil @@ -60,6 +65,7 @@ namespace :gitlab do end config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages } config[:auth] = { token: 'secret' } if Rails.env.test? + config[:'gitaly-ruby'] = { dir: File.join(Dir.pwd, 'ruby') } if gitaly_ruby TOML.dump(config) end diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build new file mode 100755 index 00000000000..44d314009e2 --- /dev/null +++ b/scripts/gitaly-test-build @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +# This script assumes tmp/tests/gitaly already contains the correct +# Gitaly version. We just have to compile it and run its 'bundle +# install'. We have this separate script for that because weird things +# were happening in CI when we have a 'bundle exec' process that later +# called 'bundle install' using a different Gemfile, as happens with +# gitlab-ce and gitaly. + +abort 'gitaly build failed' unless system('make', chdir: 'tmp/tests/gitaly') diff --git a/scripts/gitaly-test-spawn b/scripts/gitaly-test-spawn new file mode 100755 index 00000000000..dd603eec7f6 --- /dev/null +++ b/scripts/gitaly-test-spawn @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +gitaly_dir = 'tmp/tests/gitaly' +args = %W[#{gitaly_dir}/gitaly #{gitaly_dir}/config.toml] + +# Print the PID of the spawned process +puts spawn(*args, [:out, :err] => 'log/gitaly-test.log') diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 86f9568c12e..f0603dfadde 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -144,10 +144,13 @@ module TestEnv end def start_gitaly(gitaly_dir) - gitaly_exec = File.join(gitaly_dir, 'gitaly') - gitaly_config = File.join(gitaly_dir, 'config.toml') - log_file = Rails.root.join('log/gitaly-test.log').to_s - @gitaly_pid = Bundler.with_original_env { spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file) } + if ENV['CI'].present? + # Gitaly has been spawned outside this process already + return + end + + spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s + @gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i } end def stop_gitaly diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index d42d2423f15..695231c7d15 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -41,6 +41,16 @@ describe 'gitlab:gitaly namespace rake task' do end describe 'gmake/make' do + let(:command_preamble) { %w[/usr/bin/env -u BUNDLE_GEMFILE] } + + before(:all) do + @old_env_ci = ENV.delete('CI') + end + + after(:all) do + ENV['CI'] = @old_env_ci if @old_env_ci + end + before do FileUtils.mkdir_p(clone_path) expect(Dir).to receive(:chdir).with(clone_path).and_call_original @@ -49,12 +59,12 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is available' do before do expect_any_instance_of(Object).to receive(:checkout_or_clone_version) - allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true) + allow_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true) end it 'calls gmake in the gitaly directory' do expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0]) - expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true) + expect_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true) run_rake_task('gitlab:gitaly:install', clone_path) end @@ -63,12 +73,12 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is not available' do before do expect_any_instance_of(Object).to receive(:checkout_or_clone_version) - allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true) + allow_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) end it 'calls make in the gitaly directory' do expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42]) - expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true) + expect_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) run_rake_task('gitlab:gitaly:install', clone_path) end From 67de82cf5fa5a6408621cbf955c730e2825d9c39 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 31 Jul 2017 15:23:05 +0200 Subject: [PATCH 052/108] Add option to use CommitLanguages RPC --- GITALY_SERVER_VERSION | 2 +- app/controllers/projects/graphs_controller.rb | 18 +-------- lib/gitlab/git/repository.rb | 27 +++++++++++++ lib/gitlab/gitaly_client/commit_service.rb | 7 ++++ .../projects/graphs_controller_spec.rb | 33 ---------------- spec/lib/gitlab/git/repository_spec.rb | 39 +++++++++++++++++++ 6 files changed, 75 insertions(+), 51 deletions(-) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index d21d277be51..4e8f395fa5e 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.25.0 +0.26.0 diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 57372f9e79d..475d4c86294 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -43,23 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController end def get_languages - @languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages - total = @languages.map(&:last).sum - - @languages = @languages.map do |language| - name, share = language - color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}" - { - value: (share.to_f * 100 / total).round(2), - label: name, - color: color, - highlight: color - } - end - - @languages.sort! do |x, y| - y[:value] <=> x[:value] - end + @languages = @project.repository.languages end def fetch_graph diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index a3bc79109f8..88529ba2c47 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -636,6 +636,33 @@ module Gitlab @attributes.attributes(path) end + def languages(ref = nil) + Gitlab::GitalyClient.migrate(:commit_languages) do |is_enabled| + if is_enabled + gitaly_commit_client.languages(ref) + else + ref ||= rugged.head.target_id + languages = Linguist::Repository.new(rugged, ref).languages + total = languages.map(&:last).sum + + languages = languages.map do |language| + name, share = language + color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}" + { + value: (share.to_f * 100 / total).round(2), + label: name, + color: color, + highlight: color + } + end + + languages.sort do |x, y| + y[:value] <=> x[:value] + end + end + end + end + def gitaly_repository Gitlab::GitalyClient::Util.repository(@storage, @relative_path) end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index c6e52b530b3..a834781b1f1 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -118,6 +118,13 @@ module Gitlab consume_commits_response(response) end + def languages(ref = nil) + request = Gitaly::CommitLanguagesRequest.new(repository: @gitaly_repo, revision: ref || '') + response = GitalyClient.call(@repository.storage, :commit_service, :commit_languages, request) + + response.languages.map { |l| { value: l.share.round(2), label: l.name, color: l.color, highlight: l.color } } + end + private def commit_diff_request_params(commit, options = {}) diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index e0de62e4454..5af03ae118c 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -24,37 +24,4 @@ describe Projects::GraphsController do expect(response).to redirect_to action: :charts end end - - describe 'GET charts' do - let(:linguist_repository) do - double(languages: { - 'Ruby' => 1000, - 'CoffeeScript' => 350, - 'NSIS' => 15 - }) - end - - let(:expected_values) do - nsis_color = "##{Digest::SHA256.hexdigest('NSIS')[0...6]}" - [ - # colors from Linguist: - { label: "Ruby", color: "#701516", highlight: "#701516" }, - { label: "CoffeeScript", color: "#244776", highlight: "#244776" }, - # colors from SHA256 fallback: - { label: "NSIS", color: nsis_color, highlight: nsis_color } - ] - end - - before do - allow(Linguist::Repository).to receive(:new).and_return(linguist_repository) - end - - it 'sets the correct colour according to language' do - get(:charts, namespace_id: project.namespace, project_id: project, id: 'master') - - expected_values.each do |val| - expect(assigns(:languages)).to include(a_hash_including(val)) - end - end - end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 50736d353ad..8e4a1f31ced 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1127,6 +1127,45 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#languages' do + shared_examples 'languages' do + it 'returns exactly the expected results' do + languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6') + expected_languages = [ + { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" }, + { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" }, + { value: 7.9, label: "HTML", color: "#e44b23", highlight: "#e44b23" }, + { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" } + ] + + expect(languages.size).to eq(expected_languages.size) + + expected_languages.size.times do |i| + a = expected_languages[i] + b = languages[i] + + expect(a.keys.sort).to eq(b.keys.sort) + expect(a[:value]).to be_within(0.1).of(b[:value]) + + non_float_keys = a.keys - [:value] + expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys)) + end + end + + it "uses the repository's HEAD when no ref is passed" do + lang = repository.languages.first + + expect(lang[:label]).to eq('Ruby') + end + end + + it_behaves_like 'languages' + + context 'with rugged', skip_gitaly_mock: true do + it_behaves_like 'languages' + end + end + def create_remote_branch(repository, remote_name, branch_name, source_branch_name) source_branch = repository.branches.find { |branch| branch.name == source_branch_name } rugged = repository.rugged From 423d31a300370c9c0acd9e8faae4d7bda2ac7d61 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Tue, 1 Aug 2017 08:50:59 +0000 Subject: [PATCH 053/108] Inline script cleanup globals and easy --- .../javascripts/blob_edit/blob_bundle.js | 5 + app/assets/javascripts/dispatcher.js | 35 +++- .../javascripts/graphs/graphs_bundle.js | 2 - .../javascripts/graphs/graphs_charts.js | 63 +++++++ app/assets/javascripts/graphs/graphs_show.js | 21 +++ app/assets/javascripts/two_factor_auth.js | 13 ++ app/assets/javascripts/ui_development_kit.js | 22 +++ app/views/help/ui.html.haml | 25 +-- app/views/layouts/_google_analytics.html.haml | 1 + .../layouts/_init_auto_complete.html.haml | 1 + app/views/layouts/_piwik.html.haml | 1 + app/views/layouts/project.html.haml | 1 + app/views/layouts/snippets.html.haml | 1 + .../personal_access_tokens/index.html.haml | 8 +- .../profiles/two_factor_auths/show.html.haml | 163 +++++++++--------- app/views/projects/_activity.html.haml | 6 - app/views/projects/blob/_new_dir.html.haml | 3 - app/views/projects/blob/_remove.html.haml | 3 - app/views/projects/branches/new.html.haml | 6 +- .../projects/commit/_commit_box.html.haml | 5 +- app/views/projects/commits/show.html.haml | 52 +++--- app/views/projects/find_file/show.html.haml | 10 +- app/views/projects/graphs/charts.html.haml | 64 ++----- app/views/projects/graphs/show.html.haml | 28 +-- app/views/projects/imports/show.html.haml | 2 - app/views/u2f/_register.html.haml | 4 - config/webpack.config.js | 5 + spec/features/commits_spec.rb | 4 +- 28 files changed, 291 insertions(+), 263 deletions(-) create mode 100644 app/assets/javascripts/graphs/graphs_charts.js create mode 100644 app/assets/javascripts/graphs/graphs_show.js create mode 100644 app/assets/javascripts/two_factor_auth.js create mode 100644 app/assets/javascripts/ui_development_kit.js diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 1c64ccf536f..b5500ac116f 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -8,6 +8,7 @@ import BlobFileDropzone from '../blob/blob_file_dropzone'; $(() => { const editBlobForm = $('.js-edit-blob-form'); const uploadBlobForm = $('.js-upload-blob-form'); + const deleteBlobForm = $('.js-delete-blob-form'); if (editBlobForm.length) { const urlRoot = editBlobForm.data('relative-url-root'); @@ -30,4 +31,8 @@ $(() => { '.btn-upload-file', ); } + + if (deleteBlobForm.length) { + new NewCommitForm(deleteBlobForm); + } }); diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index d5923266c60..a2664c0301e 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -8,6 +8,7 @@ /* global LabelsSelect */ /* global MilestoneSelect */ /* global Commit */ +/* global CommitsList */ /* global NewBranchForm */ /* global NotificationsForm */ /* global NotificationsDropdown */ @@ -19,15 +20,20 @@ /* global Search */ /* global Admin */ /* global NamespaceSelects */ +/* global NewCommitForm */ +/* global NewBranchForm */ /* global Project */ /* global ProjectAvatar */ /* global MergeRequest */ /* global Compare */ /* global CompareAutocomplete */ +/* global ProjectFindFile */ /* global ProjectNew */ /* global ProjectShow */ +/* global ProjectImport */ /* global Labels */ /* global Shortcuts */ +/* global ShortcutsFindFile */ /* global Sidebar */ /* global ShortcutsWiki */ @@ -195,7 +201,6 @@ import GpgBadges from './gpg_badges'; break; case 'explore:groups:index': new GroupsList(); - const landingElement = document.querySelector('.js-explore-groups-landing'); if (!landingElement) break; const exploreGroupsLanding = new Landing( @@ -218,6 +223,10 @@ import GpgBadges from './gpg_badges'; case 'projects:compare:show': new gl.Diff(); break; + case 'projects:branches:new': + case 'projects:branches:create': + new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); + break; case 'projects:branches:index': gl.AjaxLoadingSpinner.init(); new DeleteModal(); @@ -305,19 +314,24 @@ import GpgBadges from './gpg_badges'; container: '.js-commit-pipeline-graph', }).bindEvents(); initNotes(); + $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); break; case 'projects:commit:pipelines': new MiniPipelineGraph({ container: '.js-commit-pipeline-graph', }).bindEvents(); - break; - case 'projects:commits:show': - shortcut_handler = new ShortcutsNavigation(); - GpgBadges.fetch(); + $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); break; case 'projects:activity': + new gl.Activities(); shortcut_handler = new ShortcutsNavigation(); break; + case 'projects:commits:show': + CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); + new gl.Activities(); + shortcut_handler = new ShortcutsNavigation(); + GpgBadges.fetch(); + break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); new NotificationsForm(); @@ -331,6 +345,9 @@ import GpgBadges from './gpg_badges'; case 'projects:edit': setupProjectEdit(); break; + case 'projects:imports:show': + new ProjectImport(); + break; case 'projects:pipelines:new': new NewBranchForm($('.js-new-pipeline-form')); break; @@ -387,11 +404,19 @@ import GpgBadges from './gpg_badges'; shortcut_handler = new ShortcutsNavigation(); new TreeView(); new BlobViewer(); + new NewCommitForm($('.js-create-dir-form')); $('#tree-slider').waitForImages(function() { gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); break; case 'projects:find_file:show': + const findElement = document.querySelector('.js-file-finder'); + const projectFindFile = new ProjectFindFile($(".file-finder-holder"), { + url: findElement.dataset.fileFindUrl, + treeUrl: findElement.dataset.findTreeUrl, + blobUrlTemplate: findElement.dataset.blobUrlTemplate, + }); + new ShortcutsFindFile(projectFindFile); shortcut_handler = true; break; case 'projects:blob:show': diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js index a433c7ba8f0..534bc535bb6 100644 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ b/app/assets/javascripts/graphs/graphs_bundle.js @@ -1,6 +1,4 @@ import Chart from 'vendor/Chart'; -import ContributorsStatGraph from './stat_graph_contributors'; // export to global scope window.Chart = Chart; -window.ContributorsStatGraph = ContributorsStatGraph; diff --git a/app/assets/javascripts/graphs/graphs_charts.js b/app/assets/javascripts/graphs/graphs_charts.js new file mode 100644 index 00000000000..279ffef770f --- /dev/null +++ b/app/assets/javascripts/graphs/graphs_charts.js @@ -0,0 +1,63 @@ +import Chart from 'vendor/Chart'; + +document.addEventListener('DOMContentLoaded', () => { + const projectChartData = JSON.parse(document.getElementById('projectChartData').innerHTML); + + const responsiveChart = (selector, data) => { + const options = { + scaleOverlay: true, + responsive: true, + pointHitDetectionRadius: 2, + maintainAspectRatio: false, + }; + // get selector by context + const ctx = selector.get(0).getContext('2d'); + // pointing parent container to make chart.js inherit its width + const container = $(selector).parent(); + const generateChart = () => { + selector.attr('width', $(container).width()); + if (window.innerWidth < 768) { + // Scale fonts if window width lower than 768px (iPad portrait) + options.scaleFontSize = 8; + } + return new Chart(ctx).Bar(data, options); + }; + // enabling auto-resizing + $(window).resize(generateChart); + return generateChart(); + }; + + const chartData = (keys, values) => { + const data = { + labels: keys, + datasets: [{ + fillColor: 'rgba(220,220,220,0.5)', + strokeColor: 'rgba(220,220,220,1)', + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data: values, + }], + }; + return data; + }; + + const hourData = chartData(projectChartData.hour.keys, projectChartData.hour.values); + responsiveChart($('#hour-chart'), hourData); + + const dayData = chartData(projectChartData.weekDays.keys, projectChartData.weekDays.values); + responsiveChart($('#weekday-chart'), dayData); + + const monthData = chartData(projectChartData.month.keys, projectChartData.month.values); + responsiveChart($('#month-chart'), monthData); + + const data = projectChartData.languages; + const ctx = $('#languages-chart').get(0).getContext('2d'); + const options = { + scaleOverlay: true, + responsive: true, + maintainAspectRatio: false, + }; + + new Chart(ctx).Pie(data, options); +}); diff --git a/app/assets/javascripts/graphs/graphs_show.js b/app/assets/javascripts/graphs/graphs_show.js new file mode 100644 index 00000000000..36bad6db3e1 --- /dev/null +++ b/app/assets/javascripts/graphs/graphs_show.js @@ -0,0 +1,21 @@ +import ContributorsStatGraph from './stat_graph_contributors'; + +document.addEventListener('DOMContentLoaded', () => { + $.ajax({ + type: 'GET', + url: document.querySelector('.js-graphs-show').dataset.projectGraphPath, + dataType: 'json', + success(data) { + const graph = new ContributorsStatGraph(); + graph.init(data); + + $('#brush_change').change(() => { + graph.change_date_header(); + graph.redraw_authors(); + }); + + $('.stat-graph').fadeIn(); + $('.loading-graph').hide(); + }, + }); +}); diff --git a/app/assets/javascripts/two_factor_auth.js b/app/assets/javascripts/two_factor_auth.js new file mode 100644 index 00000000000..d26f61562a5 --- /dev/null +++ b/app/assets/javascripts/two_factor_auth.js @@ -0,0 +1,13 @@ +/* global U2FRegister */ +document.addEventListener('DOMContentLoaded', () => { + const twoFactorNode = document.querySelector('.js-two-factor-auth'); + const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true'; + if (skippable) { + const button = `Configure it later`; + const flashAlert = document.querySelector('.flash-alert .container-fluid'); + if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button); + } + + const u2fRegister = new U2FRegister($('#js-register-u2f'), gon.u2f); + u2fRegister.start(); +}); diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js new file mode 100644 index 00000000000..f503076715c --- /dev/null +++ b/app/assets/javascripts/ui_development_kit.js @@ -0,0 +1,22 @@ +import Api from './api'; + +document.addEventListener('DOMContentLoaded', () => { + $('#js-project-dropdown').glDropdown({ + data: (term, callback) => { + Api.projects(term, { + order_by: 'last_activity_at', + }, (data) => { + callback(data); + }); + }, + text: project => (project.name_with_namespace || project.name), + selectable: true, + fieldName: 'author_id', + filterable: true, + search: { + fields: ['name_with_namespace'], + }, + id: data => data.id, + isSelected: data => (data.id === 2), + }); +}); diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 48edbb8c16f..f18c3a74120 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -1,5 +1,7 @@ - page_title "UI Development Kit", "Help" - lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare." +- content_for :page_specific_javascripts do + = webpack_bundle_tag('ui_development_kit') .gitlab-ui-dev-kit %h1 GitLab UI development kit @@ -407,29 +409,6 @@ .dropdown-content .dropdown-loading = icon('spinner spin') - :javascript - $('#js-project-dropdown').glDropdown({ - data: function (term, callback) { - Api.projects(term, { order_by: 'last_activity_at' }, function (data) { - callback(data); - }); - }, - text: function (project) { - return project.name_with_namespace || project.name; - }, - selectable: true, - fieldName: "author_id", - filterable: true, - search: { - fields: ['name_with_namespace'] - }, - id: function (data) { - return data.id; - }, - isSelected: function (data) { - return data.id === 2; - } - }) .example %div diff --git a/app/views/layouts/_google_analytics.html.haml b/app/views/layouts/_google_analytics.html.haml index 81e03c7eff2..98ea96b0b77 100644 --- a/app/views/layouts/_google_analytics.html.haml +++ b/app/views/layouts/_google_analytics.html.haml @@ -1,3 +1,4 @@ +-# haml-lint:disable InlineJavaScript :javascript var _gaq = _gaq || []; _gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']); diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 4bb0dfc73fd..9704c9ec624 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -2,6 +2,7 @@ - noteable_type = @noteable.class if @noteable.present? - if project + -# haml-lint:disable InlineJavaScript :javascript gl.GfmAutoComplete = gl.GfmAutoComplete || {}; gl.GfmAutoComplete.dataSources = { diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml index 259b4f7cdfc..a888e8ae187 100644 --- a/app/views/layouts/_piwik.html.haml +++ b/app/views/layouts/_piwik.html.haml @@ -1,4 +1,5 @@ +-# haml-lint:disable InlineJavaScript :javascript var _paq = _paq || []; _paq.push(['trackPageView']); diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 99adb83cd1f..54d56e9b873 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -10,6 +10,7 @@ - content_for :project_javascripts do - project = @target_project || @project - if current_user + -# haml-lint:disable InlineJavaScript :javascript window.uploads_path = "#{project_uploads_path(project)}"; diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index 57971205e0e..849075a0ba5 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -2,6 +2,7 @@ - content_for :page_specific_javascripts do - if @snippet && current_user + -# haml-lint:disable InlineJavaScript :javascript window.uploads_path = "#{upload_path('personal_snippet', id: @snippet.id)}"; diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index cf750378e25..2216708d354 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -1,5 +1,6 @@ - page_title "Personal Access Tokens" - @content_class = "limit-container-width" unless fluid_layout + = render 'profiles/head' .row.prepend-top-default @@ -19,7 +20,7 @@ %h5.prepend-top-0 Your New Personal Access Token .form-group - = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block" + = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block" = clipboard_button(text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left") %span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again. @@ -28,8 +29,3 @@ = render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes = render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens - -:javascript - $("#created-personal-access-token").click(function() { - this.select(); - }); diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 037cb30efb9..33e062c1c9c 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -7,97 +7,92 @@ = render 'profiles/head' -- if inject_u2f_api? - - content_for :page_specific_javascripts do +- content_for :page_specific_javascripts do + - if inject_u2f_api? = page_specific_javascript_bundle_tag('u2f') + = page_specific_javascript_bundle_tag('two_factor_auth') -.row.prepend-top-default - .col-lg-4 - %h4.prepend-top-0 - Register Two-Factor Authentication App - %p - Use an app on your mobile device to enable two-factor authentication (2FA). - .col-lg-8 - - if current_user.two_factor_otp_enabled? - = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page." - - else +.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path } + .row.prepend-top-default + .col-lg-4 + %h4.prepend-top-0 + Register Two-Factor Authentication App %p - Download the Google Authenticator application from App Store or Google Play Store and scan this code. - More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}. - .row.append-bottom-10 - .col-md-4 - = raw @qr_code - .col-md-8 - .account-well - %p.prepend-top-0.append-bottom-0 - Can't scan the code? - %p.prepend-top-0.append-bottom-0 - To add the entry manually, provide the following details to the application on your phone. - %p.prepend-top-0.append-bottom-0 - Account: - = @account_string - %p.prepend-top-0.append-bottom-0 - Key: - = current_user.otp_secret.scan(/.{4}/).join(' ') - %p.two-factor-new-manual-content - Time based: Yes - = form_tag profile_two_factor_auth_path, method: :post do |f| - - if @error - .alert.alert-danger - = @error - .form-group - = label_tag :pin_code, nil, class: "label-light" - = text_field_tag :pin_code, nil, class: "form-control", required: true - .prepend-top-default - = submit_tag 'Register with two-factor app', class: 'btn btn-success' + Use an app on your mobile device to enable two-factor authentication (2FA). + .col-lg-8 + - if current_user.two_factor_otp_enabled? + = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page." + - else + %p + Download the Google Authenticator application from App Store or Google Play Store and scan this code. + More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}. + .row.append-bottom-10 + .col-md-4 + = raw @qr_code + .col-md-8 + .account-well + %p.prepend-top-0.append-bottom-0 + Can't scan the code? + %p.prepend-top-0.append-bottom-0 + To add the entry manually, provide the following details to the application on your phone. + %p.prepend-top-0.append-bottom-0 + Account: + = @account_string + %p.prepend-top-0.append-bottom-0 + Key: + = current_user.otp_secret.scan(/.{4}/).join(' ') + %p.two-factor-new-manual-content + Time based: Yes + = form_tag profile_two_factor_auth_path, method: :post do |f| + - if @error + .alert.alert-danger + = @error + .form-group + = label_tag :pin_code, nil, class: "label-light" + = text_field_tag :pin_code, nil, class: "form-control", required: true + .prepend-top-default + = submit_tag 'Register with two-factor app', class: 'btn btn-success' -%hr + %hr -.row.prepend-top-default + .row.prepend-top-default + .col-lg-4 + %h4.prepend-top-0 + Register Universal Two-Factor (U2F) Device + %p + Use a hardware device to add the second factor of authentication. + %p + As U2F devices are only supported by a few browsers, we require that you set up a + two-factor authentication app before a U2F device. That way you'll always be able to + log in - even when you're using an unsupported browser. + .col-lg-8 + - if @u2f_registration.errors.present? + = form_errors(@u2f_registration) + = render "u2f/register" - .col-lg-4 - %h4.prepend-top-0 - Register Universal Two-Factor (U2F) Device - %p - Use a hardware device to add the second factor of authentication. - %p - As U2F devices are only supported by a few browsers, we require that you set up a - two-factor authentication app before a U2F device. That way you'll always be able to - log in - even when you're using an unsupported browser. - .col-lg-8 - - if @u2f_registration.errors.present? - = form_errors(@u2f_registration) - = render "u2f/register" + %hr - %hr + %h5 U2F Devices (#{@u2f_registrations.length}) - %h5 U2F Devices (#{@u2f_registrations.length}) - - - if @u2f_registrations.present? - .table-responsive - %table.table.table-bordered.u2f-registrations - %colgroup - %col{ width: "50%" } - %col{ width: "30%" } - %col{ width: "20%" } - %thead - %tr - %th Name - %th Registered On - %th - %tbody - - @u2f_registrations.each do |registration| + - if @u2f_registrations.present? + .table-responsive + %table.table.table-bordered.u2f-registrations + %colgroup + %col{ width: "50%" } + %col{ width: "30%" } + %col{ width: "20%" } + %thead %tr - %td= registration.name.presence || "" - %td= registration.created_at.to_date.to_s(:medium) - %td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." } + %th Name + %th Registered On + %th + %tbody + - @u2f_registrations.each do |registration| + %tr + %td= registration.name.presence || "" + %td= registration.created_at.to_date.to_s(:medium) + %td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." } - - else - .settings-message.text-center - You don't have any U2F devices registered yet. - - -- if two_factor_skippable? - :javascript - var button = "Configure it later"; - $(".flash-alert").append(button); + - else + .settings-message.text-center + You don't have any U2F devices registered yet. diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index ecc966ed453..ad63f5e73ae 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -8,9 +8,3 @@ .content_list.project-activity{ :"data-href" => activity_project_path(@project) } = spinner - -:javascript - var activity = new gl.Activities(); - $(document).on('page:restore', function (event) { - activity.reloadActivities() - }) diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index b2959ef6d31..03ab1bb59e4 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -20,6 +20,3 @@ - unless can?(current_user, :push_code, @project) .inline.prepend-left-10 = commit_in_fork_help - -:javascript - new NewCommitForm($('.js-create-dir-form')) diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index 6a4a657fa8c..750bdef3308 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -13,6 +13,3 @@ .col-sm-offset-2.col-sm-10 = button_tag 'Delete file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" - -:javascript - new NewCommitForm($('.js-delete-blob-form')) diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 03eefcc2b4d..2baaaf6ac5b 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -28,8 +28,4 @@ .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' - -:javascript - var availableRefs = #{@project.repository.ref_names.to_json}; - - new NewBranchForm($('.js-create-branch-form'), availableRefs) +%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 419fbe99af8..09bcd187e59 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,4 +1,4 @@ -.page-content-header +.page-content-header.js-commit-box{ 'data-commit-path' => branches_project_commit_path(@project, @commit.id) } .header-main-content = render partial: 'signature', object: @commit.signature %strong @@ -79,6 +79,3 @@ = render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph' in = time_interval_in_words last_pipeline.duration - -:javascript - $(".commit-info.branches").load("#{branches_project_commit_path(@project, @commit.id)}"); diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index bd2d900997e..7ae56086177 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -11,34 +11,32 @@ = content_for :sub_nav do = render "head" -%div{ class: container_class } - .tree-holder - .nav-block - .tree-ref-container - .tree-ref-holder - = render 'shared/ref_switcher', destination: 'commits' +.js-project-commits-show{ 'data-commits-limit' => @limit } + %div{ class: container_class } + .tree-holder + .nav-block + .tree-ref-container + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'commits' + + %ul.breadcrumb.repo-breadcrumb + = commits_breadcrumbs + .tree-controls.hidden-xs.hidden-sm + - if @merge_request.present? + .control + = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' + - elsif create_mr_button?(@repository.root_ref, @ref) + .control + = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' - %ul.breadcrumb.repo-breadcrumb - = commits_breadcrumbs - .tree-controls.hidden-xs.hidden-sm - - if @merge_request.present? .control - = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' - - elsif create_mr_button?(@repository.root_ref, @ref) + = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do + = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } .control - = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' + = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do + = icon("rss") - .control - = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do - = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } - .control - = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do - = icon("rss") - - %div{ id: dom_id(@project) } - %ol#commits-list.list-unstyled.content_list - = render 'commits', project: @project, ref: @ref - = spinner - -:javascript - CommitsList.init(#{@limit}); + %div{ id: dom_id(@project) } + %ol#commits-list.list-unstyled.content_list + = render 'commits', project: @project, ref: @ref + = spinner diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index e3bf48ee47f..021575160ea 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -1,7 +1,7 @@ - page_title "Find File", @ref = render "projects/commits/head" -.file-finder-holder.tree-holder.clearfix +.file-finder-holder.tree-holder.clearfix.js-file-finder{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @id || @commit.id)) } .nav-block .tree-ref-holder = render 'shared/ref_switcher', destination: 'find_file', path: @path @@ -17,11 +17,3 @@ %table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" } %tbody = spinner nil, true - -:javascript - var projectFindFile = new ProjectFindFile($(".file-finder-holder"), { - url: "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", - treeUrl: "#{escape_javascript(project_tree_path(@project, @ref))}", - blobUrlTemplate: "#{escape_javascript(project_blob_path(@project, @id || @commit.id))}" - }); - new ShortcutsFindFile(projectFindFile); diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index 249b9d82ad9..228c8c84792 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -3,8 +3,9 @@ - if show_new_nav? - add_to_breadcrumbs("Repository", project_tree_path(@project)) - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('graphs') + = webpack_bundle_tag('common_d3') + = webpack_bundle_tag('graphs') + = webpack_bundle_tag('graphs_charts') = render "projects/commits/head" .repo-charts{ class: container_class } @@ -75,55 +76,10 @@ Commits per day hour (UTC) %canvas#hour-chart -:javascript - var responsiveChart = function (selector, data) { - var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }; - // get selector by context - var ctx = selector.get(0).getContext("2d"); - // pointing parent container to make chart.js inherit its width - var container = $(selector).parent(); - var generateChart = function() { - selector.attr('width', $(container).width()); - if (window.innerWidth < 768) { - // Scale fonts if window width lower than 768px (iPad portrait) - options.scaleFontSize = 8 - } - return new Chart(ctx).Bar(data, options); - }; - // enabling auto-resizing - $(window).resize(generateChart); - return generateChart(); - }; - - var chartData = function (keys, values) { - var data = { - labels : keys, - datasets : [{ - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data : values - }] - }; - return data; - }; - - var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json}); - responsiveChart($('#hour-chart'), hourData); - - var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json}); - responsiveChart($('#weekday-chart'), dayData); - - var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json}); - responsiveChart($('#month-chart'), monthData); - - var data = #{@languages.to_json}; - var ctx = $("#languages-chart").get(0).getContext("2d"); - var options = { - scaleOverlay: true, - responsive: true, - maintainAspectRatio: false - } - var myPieChart = new Chart(ctx).Pie(data, options); +%script#projectChartData{ type: "application/json" } + - projectChartData = {}; + - projectChartData['hour'] = { 'keys' => @commits_per_time.keys, 'values' => @commits_per_time.values } + - projectChartData['weekDays'] = { 'keys' => @commits_per_week_days.keys, 'values' => @commits_per_week_days.values } + - projectChartData['month'] = { 'keys' => @commits_per_month.keys, 'values' => @commits_per_month.values } + - projectChartData['languages'] = @languages + = projectChartData.to_json.html_safe diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 4256a8c4d7e..f41a0d8293b 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,15 +1,16 @@ - @no_container = true - page_title "Contributors" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('graphs') + = webpack_bundle_tag('common_d3') + = webpack_bundle_tag('graphs') + = webpack_bundle_tag('graphs_show') - if show_new_nav? - add_to_breadcrumbs("Repository", project_tree_path(@project)) = render 'projects/commits/head' -%div{ class: container_class } +.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } .sub-header-block .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs' @@ -33,24 +34,3 @@ #contributors-master #contributors.clearfix %ol.contributors-list.clearfix - - - -:javascript - $.ajax({ - type: "GET", - url: "#{project_graph_path(@project, current_ref, format: :json)}", - dataType: "json", - success: function (data) { - var graph = new ContributorsStatGraph(); - graph.init(data); - - $("#brush_change").change(function(){ - graph.change_date_header(); - graph.redraw_authors(); - }); - - $(".stat-graph").fadeIn(); - $(".loading-graph").hide(); - } - }); diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index c52b3860636..8c490773a56 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -10,5 +10,3 @@ - if @project.external_import? %p.monospace git clone --bare #{@project.safe_import_url} %p Please wait while we import the repository for you. Refresh at will. - :javascript - new ProjectImport(); diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index 00788e77b6b..093b2d82813 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -37,7 +37,3 @@ .col-md-3 = hidden_field_tag 'u2f_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response" = submit_tag "Register U2F device", class: "btn btn-success" - -:javascript - var u2fRegister = new U2FRegister($("#js-register-u2f"), gon.u2f); - u2fRegister.start(); diff --git a/config/webpack.config.js b/config/webpack.config.js index 2f85b89d523..8b0c64f9289 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -39,6 +39,8 @@ var config = { environments_folder: './environments/folder/environments_folder_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js', graphs: './graphs/graphs_bundle.js', + graphs_charts: './graphs/graphs_charts.js', + graphs_show: './graphs/graphs_show.js', group: './group.js', groups: './groups/index.js', groups_list: './groups_list.js', @@ -70,9 +72,12 @@ var config = { stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', u2f: ['vendor/u2f'], + ui_development_kit: './ui_development_kit.js', + users: './users/index.js', raven: './raven/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js', + two_factor_auth: './two_factor_auth.js', performance_bar: './performance_bar.js', webpack_runtime: './webpack.js', }, diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 15ec6f20763..0c9fcc60d30 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -282,7 +282,7 @@ describe 'Commits' do end # verified and the gpg user has a gitlab profile - click_on 'Verified' + click_on 'Verified', match: :first within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature.' expect(page).to have_content 'Nannie Bernhard' @@ -295,7 +295,7 @@ describe 'Commits' do visit project_commits_path(project, :'signed-commits') - click_on 'Verified' + click_on 'Verified', match: :first within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature.' expect(page).to have_content 'Nannie Bernhard' From 3f2f8916dcda0988f6640200a0ed9c8827fd454d Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 1 Aug 2017 08:58:13 +0000 Subject: [PATCH 054/108] Docs add blog articles --- doc/articles/index.md | 126 ++++++++++++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 30 deletions(-) diff --git a/doc/articles/index.md b/doc/articles/index.md index a4e41517d83..d7e662d737f 100644 --- a/doc/articles/index.md +++ b/doc/articles/index.md @@ -7,40 +7,106 @@ to provide the community with guidance on specific processes to achieve certain They are written by members of the GitLab Team and by [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/). +Part of the articles listed below link to the [GitLab Blog](https://about.gitlab.com/blog/), +where they were originally published. + ## Authentication -- **LDAP** - - [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md) - - [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) +Learn how to explore GitLab's supported [authentications methods](../topics/authentication/index.md): -## Git - -- [How to install Git](how_to_install_git/index.md) - -## GitLab Pages - -- **GitLab Pages from A to Z** - - [Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md) - - [Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md) - - [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md) - - [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md) -- [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) -- [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) - -## Sofware development - -- [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) -- [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) -- [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) -- [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) -- [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| **LDAP** | +| [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md)| Admin guide | 2017/05/03 | +| [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) | Admin guide | 2017/05/03 | ## Build, test, and deploy with GitLab CI/CD -**Build, test, and deploy** the software you develop with **[GitLab CI/CD](../ci/README.md)** +Build, test, and deploy the software you develop with [GitLab CI/CD](../ci/README.md): + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 | +| [Dockerizing GitLab Review Apps](https://about.gitlab.com/2017/07/11/dockerizing-review-apps/) | Concepts | 2017/07/11 | +| [Continuous Integration: From Jenkins to GitLab Using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/) | Concepts | 2017/07/27 | +| [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) | Tutorial | 2016/12/14 | +| [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) | Tutorial | 2016/11/30 | +| [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) | Tutorial | 2016/10/12 | +| [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) | Tutorial | 2016/08/11 | +| [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) | Technical overview | 2016/06/09 | +| [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) | Technical overview | 2017/05/15 | +| [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) | Tutorial | 2016/03/10 | + +## Git + +Learn how to use [Git with GitLab](../topics/git/index.md): + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Why Git is Worth the Learning Curve](https://about.gitlab.com/2017/05/17/learning-curve-is-the-biggest-challenge-developers-face-with-git/) | Concepts | 2017/05/17 | +| [How to install Git](how_to_install_git/index.md) | Tutorial | 2017/05/15 | +| [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/) | Tutorial | 2017/01/30 | +| [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/) | Technical overview | 2016/12/08 | + +## GitLab Pages + +Learn how to deploy a static website with [GitLab Pages](../user/project/pages/index.md#getting-started): + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| **Series: GitLab Pages from A to Z:** | +| [- Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md)| User guide | 2017/02/22 | +| [- Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md)| User guide | 2017/02/22 | +| [- Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md)| User guide | 2017/02/22 | +| [- Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md)| User guide | 2017/02/22 | +| [Setting up GitLab Pages with CloudFlare Certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Tutorial | 2017/02/07 | +| [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) | Tutorial | 2016/12/07 | +| [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) | Tutorial | 2016/11/03 | +| [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) | Tutorial | 2016/08/26 | +| [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) | Tutorial | 2016/08/19 | +| **Series: Static Site Generator:** | +| [- Part 1: Dynamic vs Static Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | Tutorial | 2016/06/03 | +| [- Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | Tutorial | 2016/06/10 | +| [- Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) | Tutorial | 2016/06/17 | +| [Securing your GitLab Pages with TLS and Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) | Tutorial | 2016/04/11 | +| [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) | Tutorial | 2016/04/07 | + +## Install and maintain GitLab + +Install, upgrade, integrate, migrate to GitLab: + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017/01/23 | +| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016/07/13 | +| [Get started with OpenShift Origin 3 and GitLab](https://about.gitlab.com/2016/06/28/get-started-with-openshift-origin-3-and-gitlab/) | Tutorial | 2016/06/28 | +| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016/04/27 | + +## Software development + +Learn how to explore the best of GitLab's software development's capabilities: + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 | +| [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/)| Concepts | 2017/06/29 | +| [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) | Concepts | 2017/05/22 | +| [Demo: GitLab Service Desk](https://about.gitlab.com/2017/05/09/demo-service-desk/) | Feature highlight | 2017/05/09 | +| [Demo - Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017/04/25 | +| [Demo – Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017/04/18 | +| [Demo - Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017/03/17 | +| [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) | Technical overview | 2016/11/14 | +| [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) | Technical overview | 2016/10/25 | +| [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/) | Concepts | 2016/08/16 | +| [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) | Concepts | 2016/08/05 | +| [Trends in Version Control Land: Innersourcing](https://about.gitlab.com/2016/07/07/trends-version-control-innersourcing/) | Concepts | 2016/07/07 | +| [Tutorial: It's all connected in GitLab](https://about.gitlab.com/2016/03/08/gitlab-tutorial-its-all-connected/) | Technical overview | 2016/03/08 | + +## Technologies + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Why we are not leaving the cloud](https://about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/) | Concepts | 2017/03/02 | +| [Why We Chose Vue.js](https://about.gitlab.com/2016/10/20/why-we-chose-vue/) | Concepts | 2016/10/20 | +| [Markdown Kramdown Tips & Tricks](https://about.gitlab.com/2016/07/19/markdown-kramdown-tips-and-tricks/) | Technical overview | 2016/07/19 | -- [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) -- [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) -- [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) -- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) -- [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) From d03ea6d40a923f233c71000808e333809791bc26 Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Tue, 1 Aug 2017 14:36:17 +0530 Subject: [PATCH 055/108] Change ID type to number --- spec/javascripts/groups/mock_data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js index 9e1f414514a..5bb84b591f4 100644 --- a/spec/javascripts/groups/mock_data.js +++ b/spec/javascripts/groups/mock_data.js @@ -1,5 +1,5 @@ const group1 = { - id: '12', + id: 12, name: 'level1', path: 'level1', description: 'foo', From 53fb22bcc3458598de9cebd60ae64a5f96937d5e Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Tue, 1 Aug 2017 14:36:30 +0530 Subject: [PATCH 056/108] Remove unnecessary imports --- spec/javascripts/groups/groups_spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/javascripts/groups/groups_spec.js b/spec/javascripts/groups/groups_spec.js index 7e38b49c792..b14153dbbfa 100644 --- a/spec/javascripts/groups/groups_spec.js +++ b/spec/javascripts/groups/groups_spec.js @@ -2,7 +2,6 @@ import Vue from 'vue'; import eventHub from '~/groups/event_hub'; import groupFolderComponent from '~/groups/components/group_folder.vue'; import groupItemComponent from '~/groups/components/group_item.vue'; -import groupIdenticonComponent from '~/groups/components/group_identicon.vue'; import groupsComponent from '~/groups/components/groups.vue'; import GroupsStore from '~/groups/stores/groups_store'; import { groupsData } from './mock_data'; @@ -15,7 +14,6 @@ describe('Groups Component', () => { beforeEach((done) => { Vue.component('group-folder', groupFolderComponent); - Vue.component('group-identicon', groupIdenticonComponent); Vue.component('group-item', groupItemComponent); store = new GroupsStore(); From 59377d54e91ba9acb309fe3cb1d4f942dbf43e54 Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Tue, 1 Aug 2017 14:38:09 +0530 Subject: [PATCH 057/108] Tests for `group_identicon` component --- .../groups/group_identicon_spec.js | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 spec/javascripts/groups/group_identicon_spec.js diff --git a/spec/javascripts/groups/group_identicon_spec.js b/spec/javascripts/groups/group_identicon_spec.js new file mode 100644 index 00000000000..d9615646982 --- /dev/null +++ b/spec/javascripts/groups/group_identicon_spec.js @@ -0,0 +1,82 @@ +import Vue from 'vue'; +import groupIdenticonComponent from '~/groups/components/group_identicon.vue'; +import GroupsStore from '~/groups/stores/groups_store'; +import { group1 } from './mock_data'; + +const createComponent = () => { + const Component = Vue.extend(groupIdenticonComponent); + const store = new GroupsStore(); + const group = store.decorateGroup(group1); + + return new Component({ + el: document.createElement('div'), + propsData: { + entityId: group.id, + entityName: group.name, + }, + }); +}; + +describe('GroupIdenticonComponent', () => { + let vm; + let el; + + beforeEach(() => { + vm = createComponent(); + el = vm.$el; + }); + + describe('props', () => { + it('should have props with defined data types', (done) => { + const identiconProps = groupIdenticonComponent.props; + const EntityIdTypeClass = identiconProps.entityId.type; + const EntityNameTypeClass = identiconProps.entityName.type; + + Vue.nextTick(() => { + expect(identiconProps.entityId).toBeDefined(); + expect(new EntityIdTypeClass() instanceof Number).toBeTruthy(); + expect(identiconProps.entityId.required).toBeTruthy(); + + expect(identiconProps.entityName).toBeDefined(); + expect(new EntityNameTypeClass() instanceof String).toBeTruthy(); + expect(identiconProps.entityName.required).toBeTruthy(); + done(); + }); + }); + }); + + describe('computed', () => { + describe('identiconStyles', () => { + it('should return styles attribute value with `background-color` property', () => { + vm.entityId = 4; + + expect(vm.identiconStyles).toBeDefined(); + expect(vm.identiconStyles.indexOf('background-color: #E0F2F1;') > -1).toBeTruthy(); + }); + + it('should return styles attribute value with `color` property', () => { + vm.entityId = 4; + + expect(vm.identiconStyles).toBeDefined(); + expect(vm.identiconStyles.indexOf('color: #555;') > -1).toBeTruthy(); + }); + }); + + describe('identiconTitle', () => { + it('should return first letter of entity title in uppercase', () => { + vm.entityName = 'dummy-group'; + + expect(vm.identiconTitle).toBeDefined(); + expect(vm.identiconTitle).toBe('D'); + }); + }); + }); + + describe('template', () => { + it('should render identicon', () => { + expect(el.nodeName).toBe('DIV'); + expect(el.classList.contains('identicon')).toBeTruthy(); + expect(el.getAttribute('style').indexOf('background-color') > -1).toBeTruthy(); + }); + }); +}); From 00226a9404d8e453a06d09ec54628a4eb66d98ec Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Tue, 1 Aug 2017 14:38:40 +0530 Subject: [PATCH 058/108] Import `group_identicon` minor clean up and prop updates --- .../javascripts/groups/components/group_item.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index c704aa65df2..41c9e5fc8b0 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -1,7 +1,11 @@ From 9f8152d84b1778c9d5c693d2cbc12b26cad596c4 Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Tue, 1 Aug 2017 14:40:55 +0530 Subject: [PATCH 060/108] Add changelog entry --- changelogs/unreleased/35408-group-auto-avatars.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/35408-group-auto-avatars.yml diff --git a/changelogs/unreleased/35408-group-auto-avatars.yml b/changelogs/unreleased/35408-group-auto-avatars.yml new file mode 100644 index 00000000000..77b644a7f94 --- /dev/null +++ b/changelogs/unreleased/35408-group-auto-avatars.yml @@ -0,0 +1,4 @@ +--- +title: Show auto-generated avatars for Groups without avatars +merge_request: 13188 +author: From fe555e86fb54a0e21b49020b52a5e7f43beba73c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 1 Aug 2017 10:29:55 +0100 Subject: [PATCH 061/108] Fixes the search losing focus This was caused by the blur & then the focus after transition end Closes #35515 --- app/assets/javascripts/gl_dropdown.js | 6 +++--- changelogs/unreleased/search-flickering.yml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/search-flickering.yml diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 3babe273100..9475498e176 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -730,10 +730,10 @@ GitLabDropdown = (function() { GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) { if (this.options.filterable) { - $(':focus').blur(); - this.dropdown.one('transitionend', () => { - this.filterInput.focus(); + if (this.dropdown.is('.open')) { + this.filterInput.focus(); + } }); if (triggerFocus) { diff --git a/changelogs/unreleased/search-flickering.yml b/changelogs/unreleased/search-flickering.yml new file mode 100644 index 00000000000..951a5a0292a --- /dev/null +++ b/changelogs/unreleased/search-flickering.yml @@ -0,0 +1,4 @@ +--- +title: Fix search box losing focus when typing +merge_request: +author: From 48ec70250cd63f5558f30698723555656eceff64 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 1 Aug 2017 11:55:37 +0100 Subject: [PATCH 062/108] reverted the JS timeout code improved specs so that they pass --- app/assets/javascripts/fly_out_nav.js | 52 ++++--------------------- app/assets/stylesheets/new_sidebar.scss | 6 +-- spec/javascripts/fly_out_nav_spec.js | 31 +++------------ 3 files changed, 16 insertions(+), 73 deletions(-) diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 93101f123b5..67a58493fae 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -1,16 +1,3 @@ -let hideTimeoutInterval = 0; -let hideTimeout; -let subitems; - -export const getHideTimeoutInterval = () => hideTimeoutInterval; - -export const hideAllSubItems = () => { - subitems.forEach((el) => { - el.parentNode.classList.remove('is-over'); - el.style.display = 'none'; // eslint-disable-line no-param-reassign - }); -}; - export const calculateTop = (boundingRect, outerHeight) => { const windowHeight = window.innerHeight; const bottomOverflow = windowHeight - (boundingRect.top + outerHeight); @@ -24,14 +11,8 @@ export const showSubLevelItems = (el) => { if (!$subitems) return; - hideAllSubItems(); - - if (el.classList.contains('is-over')) { - clearTimeout(hideTimeout); - } else { - $subitems.style.display = 'block'; - el.classList.add('is-over'); - } + $subitems.style.display = 'block'; + el.classList.add('is-over'); const boundingRect = el.getBoundingClientRect(); const top = calculateTop(boundingRect, $subitems.offsetHeight); @@ -46,37 +27,20 @@ export const showSubLevelItems = (el) => { export const hideSubLevelItems = (el) => { const $subitems = el.querySelector('.sidebar-sub-level-items'); - const hideFn = () => { - el.classList.remove('is-over'); - $subitems.style.display = 'none'; - $subitems.classList.remove('is-above'); - hideTimeoutInterval = 0; - }; + if (!$subitems) return; - if ($subitems && hideTimeoutInterval) { - hideTimeout = setTimeout(hideFn, hideTimeoutInterval); - } else if ($subitems) { - hideFn(); - } -}; - -export const setMouseOutTimeout = (el) => { - if (el.closest('.sidebar-sub-level-items')) { - hideTimeoutInterval = 250; - } else { - hideTimeoutInterval = 0; - } + el.classList.remove('is-over'); + $subitems.style.display = 'none'; + $subitems.classList.remove('is-above'); }; export default () => { - const items = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active)')]; - subitems = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active) .sidebar-sub-level-items')]; + const items = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active)')] + .filter(el => el.querySelector('.sidebar-sub-level-items')); items.forEach((el) => { el.addEventListener('mouseenter', e => showSubLevelItems(e.currentTarget)); el.addEventListener('mouseleave', e => hideSubLevelItems(e.currentTarget)); }); - - subitems.forEach(el => el.addEventListener('mouseleave', e => setMouseOutTimeout(e.target))); }; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 72c12413aba..88486a12379 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -226,10 +226,10 @@ $new-sidebar-width: 220px; &::before { content: ""; position: absolute; - top: -20px; - bottom: -20px; + top: -30px; + bottom: -30px; left: 0; - right: -20px; + right: -30px; z-index: -1; } diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index 0fdaa2d8663..61e6c9f1fdb 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -1,7 +1,5 @@ import { calculateTop, - setMouseOutTimeout, - getHideTimeoutInterval, hideSubLevelItems, showSubLevelItems, } from '~/fly_out_nav'; @@ -41,26 +39,6 @@ describe('Fly out sidebar navigation', () => { }); }); - describe('setMouseOutTimeout', () => { - it('sets hideTimeoutInterval to 150 when inside sub items', () => { - el.innerHTML = ''; - - setMouseOutTimeout(el.querySelector('.js-test')); - - expect( - getHideTimeoutInterval(), - ).toBe(150); - }); - - it('resets hideTimeoutInterval when not inside sub items', () => { - setMouseOutTimeout(el); - - expect( - getHideTimeoutInterval(), - ).toBe(0); - }); - }); - describe('hideSubLevelItems', () => { beforeEach(() => { el.innerHTML = ''; @@ -142,16 +120,17 @@ describe('Fly out sidebar navigation', () => { it('sets is-above when element is above', () => { const subItems = el.querySelector('.sidebar-sub-level-items'); - subItems.style.height = '5000px'; + subItems.style.height = `${window.innerHeight + el.offsetHeight}px`; + subItems.style.position = 'absolute'; el.style.position = 'relative'; - el.style.top = '1000px'; + el.style.top = `${window.innerHeight - el.offsetHeight}px`; - spyOn(el.classList, 'add'); + spyOn(subItems.classList, 'add'); showSubLevelItems(el); expect( - el.classList.add, + subItems.classList.add, ).toHaveBeenCalledWith('is-above'); }); }); From 8de14be21e4ee9ddf5d5c699a6299387318fd0bd Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 1 Aug 2017 12:41:55 +0100 Subject: [PATCH 063/108] fixed translate3d test --- app/assets/javascripts/fly_out_nav.js | 2 +- spec/javascripts/fly_out_nav_spec.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 67a58493fae..ae04826896b 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -18,7 +18,7 @@ export const showSubLevelItems = (el) => { const top = calculateTop(boundingRect, $subitems.offsetHeight); const isAbove = top < boundingRect.top; - $subitems.style.transform = `translate3d(0, ${top}px, 0)`; + $subitems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; if (isAbove) { $subitems.classList.add('is-above'); diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index 61e6c9f1fdb..7c530606d61 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -8,6 +8,7 @@ describe('Fly out sidebar navigation', () => { let el; beforeEach(() => { el = document.createElement('div'); + el.style.position = 'relative'; document.body.appendChild(el); }); @@ -89,7 +90,7 @@ describe('Fly out sidebar navigation', () => { describe('showSubLevelItems', () => { beforeEach(() => { - el.innerHTML = ''; + el.innerHTML = ''; }); it('adds is-over class to el', () => { @@ -111,18 +112,17 @@ describe('Fly out sidebar navigation', () => { }); it('sets transform of sub-items', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); showSubLevelItems(el); expect( - el.querySelector('.sidebar-sub-level-items').style.transform, - ).toBe(`translate3d(0px, ${el.offsetTop}px, 0px)`); + subItems.style.transform, + ).toBe(`translate3d(0px, ${el.getBoundingClientRect().top}px, 0px)`); }); it('sets is-above when element is above', () => { const subItems = el.querySelector('.sidebar-sub-level-items'); subItems.style.height = `${window.innerHeight + el.offsetHeight}px`; - subItems.style.position = 'absolute'; - el.style.position = 'relative'; el.style.top = `${window.innerHeight - el.offsetHeight}px`; spyOn(subItems.classList, 'add'); From 97ef19d0660da6953fe201019a07de970d443d2c Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 1 Aug 2017 11:43:45 +0000 Subject: [PATCH 064/108] Fixes dropdown margin in sidebar --- app/assets/stylesheets/pages/builds.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index acf3719e9d2..28c99d8e57c 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -311,9 +311,7 @@ } .dropdown-menu { - right: $gl-padding; - left: $gl-padding; - width: auto; + margin-top: -$gl-padding; } svg { From 4fa83bbe90ea1f3d2c2d0d75f50ca732e299651f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 1 Aug 2017 13:52:44 +0200 Subject: [PATCH 065/108] Always fetch branches before finding the merge base, otherwise we could find an outdated merge base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/ee_compat_check.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index 85e6db0a689..72d7d4f84d1 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -181,8 +181,6 @@ module Gitlab end def find_merge_base_with_master(branch:) - return if merge_base_found? - # Start with (Math.exp(3).to_i = 20) until (Math.exp(6).to_i = 403) # In total we go (20 + 54 + 148 + 403 = 625) commits deeper depth = 20 From f3569a25e493bf6b3eb9bb4f6227cc087fd179a6 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 1 Aug 2017 13:17:11 +0200 Subject: [PATCH 066/108] Fix bug in blob test --- spec/lib/gitlab/git/blob_spec.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 3c784eda4f8..18320bb23b9 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -78,12 +78,18 @@ describe Gitlab::Git::Blob, seed_helper: true do context 'large file' do let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') } let(:blob_size) { 111803 } + let(:stub_limit) { 1000 } + + before do + stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', stub_limit) + end it { expect(blob.size).to eq(blob_size) } - it { expect(blob.data.length).to eq(blob_size) } + it { expect(blob.data.length).to eq(stub_limit) } it 'check that this test is sane' do - expect(blob.size).to be <= Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + # It only makes sense to test limiting if the blob is larger than the limit. + expect(blob.size).to be > Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE end it 'can load all data' do From 913aca1db9b1bbf90f3490262c4429d95fbc517c Mon Sep 17 00:00:00 2001 From: "Lin Jen-Shin (godfat)" Date: Tue, 1 Aug 2017 12:06:56 +0000 Subject: [PATCH 067/108] Make sure we didn't commit conflicts --- scripts/lint-conflicts.sh | 5 +++++ scripts/static-analysis | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100755 scripts/lint-conflicts.sh diff --git a/scripts/lint-conflicts.sh b/scripts/lint-conflicts.sh new file mode 100755 index 00000000000..f3877600c8c --- /dev/null +++ b/scripts/lint-conflicts.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +output=`git ls-files -z | grep -zvE '\.(rb|js|haml)$' | xargs -0n1 grep -HEn '^<<<<<<< '` +echo $output +test -z "$output" diff --git a/scripts/static-analysis b/scripts/static-analysis index 6d35684b97f..e4f80e8fc6f 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -11,7 +11,8 @@ tasks = [ %w[bundle exec rake brakeman], %w[bundle exec license_finder], %w[yarn run eslint], - %w[bundle exec rubocop --require rubocop-rspec] + %w[bundle exec rubocop --require rubocop-rspec], + %w[scripts/lint-conflicts.sh] ] failed_tasks = tasks.reduce({}) do |failures, task| From b808fdf16b4799e0d2e5b41d70d04f6dd10c040d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 1 Aug 2017 13:13:27 +0100 Subject: [PATCH 068/108] Math.floor the value in the tests --- spec/javascripts/fly_out_nav_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index 7c530606d61..74775911505 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -117,7 +117,7 @@ describe('Fly out sidebar navigation', () => { expect( subItems.style.transform, - ).toBe(`translate3d(0px, ${el.getBoundingClientRect().top}px, 0px)`); + ).toBe(`translate3d(0px, ${Math.floor(el.getBoundingClientRect().top)}px, 0px)`); }); it('sets is-above when element is above', () => { From 0c5fbaa72b8e4f032cf58758f8aa75925d57b06f Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 1 Aug 2017 21:17:46 +0900 Subject: [PATCH 069/108] Add 204. Remove duplicated method. --- lib/api/group_variables.rb | 1 + lib/api/helpers.rb | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb index 0dd418887e0..f64da4ab77b 100644 --- a/lib/api/group_variables.rb +++ b/lib/api/group_variables.rb @@ -88,6 +88,7 @@ module API variable = user_group.variables.find_by(key: params[:key]) not_found!('GroupVariable') unless variable + status 204 variable.destroy end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index de58e9779a9..234825480f2 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -33,10 +33,6 @@ module API @project ||= find_project!(params[:id]) end - def user_group - @group ||= find_group!(params[:id]) - end - def available_labels @available_labels ||= LabelsFinder.new(current_user, project_id: user_project.id).execute end From 5fc985776b10ff3f601a569428d916a7a5a99793 Mon Sep 17 00:00:00 2001 From: winh Date: Thu, 27 Jul 2017 11:18:11 +0200 Subject: [PATCH 070/108] Make Markdown autocomplete dropdown style consistent --- .../stylesheets/framework/markdown_area.scss | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index a2de4598167..fcd4c72b430 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -185,3 +185,28 @@ text-overflow: ellipsis; } } + +// TODO: fallback to global style +.atwho-view { + .atwho-view-ul { + padding: 8px 1px; + + li { + padding: 8px 16px; + border: 0; + + &.cur { + background-color: $gray-darker; + color: $gl-text-color; + + small { + color: inherit; + } + } + + strong { + color: $gl-text-color; + } + } + } +} From 7fa3dce30cd89ba152a410d51238fd1b8701bfda Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 1 Aug 2017 10:36:30 -0300 Subject: [PATCH 071/108] copyedit, add article to the list --- doc/articles/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/articles/index.md b/doc/articles/index.md index d7e662d737f..260940e129b 100644 --- a/doc/articles/index.md +++ b/doc/articles/index.md @@ -12,7 +12,7 @@ where they were originally published. ## Authentication -Learn how to explore GitLab's supported [authentications methods](../topics/authentication/index.md): +Explore GitLab's supported [authentications methods](../topics/authentication/index.md): | Article title | Category | Publishing date | | :------------ | :------: | --------------: | @@ -34,6 +34,7 @@ Build, test, and deploy the software you develop with [GitLab CI/CD](../ci/READM | [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) | Tutorial | 2016/10/12 | | [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) | Tutorial | 2016/08/11 | | [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) | Technical overview | 2016/06/09 | +| [GitLab Container Registry](https://about.gitlab.com/2016/05/23/gitlab-container-registry/) | Technical overview | 2016/05/23 | | [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) | Technical overview | 2017/05/15 | | [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) | Tutorial | 2016/03/10 | @@ -84,7 +85,7 @@ Install, upgrade, integrate, migrate to GitLab: ## Software development -Learn how to explore the best of GitLab's software development's capabilities: +Explore the best of GitLab's software development's capabilities: | Article title | Category | Publishing date | | :------------ | :------: | --------------: | @@ -109,4 +110,3 @@ Learn how to explore the best of GitLab's software development's capabilities: | [Why we are not leaving the cloud](https://about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/) | Concepts | 2017/03/02 | | [Why We Chose Vue.js](https://about.gitlab.com/2016/10/20/why-we-chose-vue/) | Concepts | 2016/10/20 | | [Markdown Kramdown Tips & Tricks](https://about.gitlab.com/2016/07/19/markdown-kramdown-tips-and-tricks/) | Technical overview | 2016/07/19 | - From 08ea9572d61f8fee19b3c013d2e4e943ce054580 Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Tue, 1 Aug 2017 13:38:16 +0000 Subject: [PATCH 072/108] Make time span dropdown style on cycle analytics page consistent --- .../stylesheets/pages/cycle_analytics.scss | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index eeb90759f10..87b50c7687e 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -110,6 +110,10 @@ .js-ca-dropdown { top: $gl-padding-top; + + .dropdown-menu-align-right { + margin-top: 2px; + } } .content-list { @@ -442,6 +446,24 @@ margin-bottom: 20px; } } + + // TODO: fallback to global style + .dropdown-menu { + li { + padding: 0 1px; + + a { + border-radius: 0; + padding: 8px 16px; + + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .cycle-analytics-overview { From 0430007ec86b7761cdaac2a0ba5d735264d63148 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 1 Aug 2017 14:01:48 +0000 Subject: [PATCH 073/108] Add code review guidelines related to Build [CI skip]. --- doc/development/code_review.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/development/code_review.md b/doc/development/code_review.md index e3f37616757..64a89976300 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -11,6 +11,8 @@ There are a few rules to get your merge request accepted: **approved by a [frontend maintainer][projects]**. 1. If your merge request includes frontend and backend changes [^1], it must be **approved by a [frontend and a backend maintainer][projects]**. + 1. If your merge request includes a new dependency or a filesystem change, it must + be **approved by a [Build team member][team]**. See [how to work with the Build team][build handbook] for more details. 1. To lower the amount of merge requests maintainers need to review, you can ask or assign any [reviewers][projects] for a first review. 1. If you need some guidance (e.g. it's your first merge request), feel free @@ -194,3 +196,4 @@ Largely based on the [thoughtbot code review guide]. [projects]: https://about.gitlab.com/handbook/engineering/projects/ [team]: https://about.gitlab.com/team/ +[build handbook]: https://about.gitlab.com/handbook/build/handbook/build#how-to-work-with-build From f67c7a4da6eb7e363f5433df74016f234999c467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 1 Aug 2017 10:40:41 +0200 Subject: [PATCH 074/108] Fix Issue board when using Ruby 2.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Ruby 2.4, Hash#compact exists and returns a Hash, while in Ruby 2.3, Hash#compact is implemented by Rails and returns a new `ActionController::Parameters` instance in this case. Also, `ActionController::Parameters#compact` is deprecated in Rails 5.1 so we're using `reject { |_, value| value.nil? }` instead. Signed-off-by: Rémy Coutable --- app/controllers/projects/boards/issues_controller.rb | 3 ++- changelogs/unreleased/35769-fix-ruby-2-4-compatibility.yml | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/35769-fix-ruby-2-4-compatibility.yml diff --git a/app/controllers/projects/boards/issues_controller.rb b/app/controllers/projects/boards/issues_controller.rb index da9b789d617..653e7bc7e40 100644 --- a/app/controllers/projects/boards/issues_controller.rb +++ b/app/controllers/projects/boards/issues_controller.rb @@ -66,7 +66,8 @@ module Projects end def filter_params - params.merge(board_id: params[:board_id], id: params[:list_id]).compact + params.merge(board_id: params[:board_id], id: params[:list_id]) + .reject { |_, value| value.nil? } end def move_params diff --git a/changelogs/unreleased/35769-fix-ruby-2-4-compatibility.yml b/changelogs/unreleased/35769-fix-ruby-2-4-compatibility.yml new file mode 100644 index 00000000000..ac480993d85 --- /dev/null +++ b/changelogs/unreleased/35769-fix-ruby-2-4-compatibility.yml @@ -0,0 +1,4 @@ +--- +title: Fix Issue board when using Ruby 2.4 +merge_request: 13220 +author: From e7b5324b76f2cc7fe913298cde784ce6ae3d4671 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 1 Aug 2017 09:31:43 -0500 Subject: [PATCH 075/108] include cropper jQuery plugin as an npm module --- app/assets/javascripts/profile/gl_crop.js | 2 +- package.json | 1 + vendor/assets/javascripts/cropper.js | 2993 --------------------- yarn.lock | 8 +- 4 files changed, 9 insertions(+), 2995 deletions(-) delete mode 100644 vendor/assets/javascripts/cropper.js diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js index cf1566eeb87..f32b2413725 100644 --- a/app/assets/javascripts/profile/gl_crop.js +++ b/app/assets/javascripts/profile/gl_crop.js @@ -1,6 +1,6 @@ /* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */ -import 'vendor/cropper'; +import 'cropper'; ((global) => { // Matches everything but the file name diff --git a/package.json b/package.json index fd944531a6a..6b6577ec06e 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "bootstrap-sass": "^3.3.6", "compression-webpack-plugin": "^0.3.2", "core-js": "^2.4.1", + "cropper": "^2.3.0", "css-loader": "^0.28.0", "d3": "^3.5.11", "deckar01-task_list": "^2.0.0", diff --git a/vendor/assets/javascripts/cropper.js b/vendor/assets/javascripts/cropper.js deleted file mode 100644 index 805485904a5..00000000000 --- a/vendor/assets/javascripts/cropper.js +++ /dev/null @@ -1,2993 +0,0 @@ -/*! - * Cropper v2.3.0 - * https://github.com/fengyuanchen/cropper - * - * Copyright (c) 2014-2016 Fengyuan Chen and contributors - * Released under the MIT license - * - * Date: 2016-02-22T02:13:13.332Z - */ - -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as anonymous module. - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // Node / CommonJS - factory(require('jquery')); - } else { - // Browser globals. - factory(jQuery); - } -})(function ($) { - - 'use strict'; - - // Globals - var $window = $(window); - var $document = $(document); - var location = window.location; - var navigator = window.navigator; - var ArrayBuffer = window.ArrayBuffer; - var Uint8Array = window.Uint8Array; - var DataView = window.DataView; - var btoa = window.btoa; - - // Constants - var NAMESPACE = 'cropper'; - - // Classes - var CLASS_MODAL = 'cropper-modal'; - var CLASS_HIDE = 'cropper-hide'; - var CLASS_HIDDEN = 'cropper-hidden'; - var CLASS_INVISIBLE = 'cropper-invisible'; - var CLASS_MOVE = 'cropper-move'; - var CLASS_CROP = 'cropper-crop'; - var CLASS_DISABLED = 'cropper-disabled'; - var CLASS_BG = 'cropper-bg'; - - // Events - var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown'; - var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove'; - var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel'; - var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll'; - var EVENT_DBLCLICK = 'dblclick'; - var EVENT_LOAD = 'load.' + NAMESPACE; - var EVENT_ERROR = 'error.' + NAMESPACE; - var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace - var EVENT_BUILD = 'build.' + NAMESPACE; - var EVENT_BUILT = 'built.' + NAMESPACE; - var EVENT_CROP_START = 'cropstart.' + NAMESPACE; - var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE; - var EVENT_CROP_END = 'cropend.' + NAMESPACE; - var EVENT_CROP = 'crop.' + NAMESPACE; - var EVENT_ZOOM = 'zoom.' + NAMESPACE; - - // RegExps - var REGEXP_ACTIONS = /e|w|s|n|se|sw|ne|nw|all|crop|move|zoom/; - var REGEXP_DATA_URL = /^data\:/; - var REGEXP_DATA_URL_HEAD = /^data\:([^\;]+)\;base64,/; - var REGEXP_DATA_URL_JPEG = /^data\:image\/jpeg.*;base64,/; - - // Data keys - var DATA_PREVIEW = 'preview'; - var DATA_ACTION = 'action'; - - // Actions - var ACTION_EAST = 'e'; - var ACTION_WEST = 'w'; - var ACTION_SOUTH = 's'; - var ACTION_NORTH = 'n'; - var ACTION_SOUTH_EAST = 'se'; - var ACTION_SOUTH_WEST = 'sw'; - var ACTION_NORTH_EAST = 'ne'; - var ACTION_NORTH_WEST = 'nw'; - var ACTION_ALL = 'all'; - var ACTION_CROP = 'crop'; - var ACTION_MOVE = 'move'; - var ACTION_ZOOM = 'zoom'; - var ACTION_NONE = 'none'; - - // Supports - var SUPPORT_CANVAS = $.isFunction($('')[0].getContext); - var IS_SAFARI = navigator && /safari/i.test(navigator.userAgent) && /apple computer/i.test(navigator.vendor); - - // Maths - var num = Number; - var min = Math.min; - var max = Math.max; - var abs = Math.abs; - var sin = Math.sin; - var cos = Math.cos; - var sqrt = Math.sqrt; - var round = Math.round; - var floor = Math.floor; - - // Utilities - var fromCharCode = String.fromCharCode; - - function isNumber(n) { - return typeof n === 'number' && !isNaN(n); - } - - function isUndefined(n) { - return typeof n === 'undefined'; - } - - function toArray(obj, offset) { - var args = []; - - // This is necessary for IE8 - if (isNumber(offset)) { - args.push(offset); - } - - return args.slice.apply(obj, args); - } - - // Custom proxy to avoid jQuery's guid - function proxy(fn, context) { - var args = toArray(arguments, 2); - - return function () { - return fn.apply(context, args.concat(toArray(arguments))); - }; - } - - function isCrossOriginURL(url) { - var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i); - - return parts && ( - parts[1] !== location.protocol || - parts[2] !== location.hostname || - parts[3] !== location.port - ); - } - - function addTimestamp(url) { - var timestamp = 'timestamp=' + (new Date()).getTime(); - - return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp); - } - - function getCrossOrigin(crossOrigin) { - return crossOrigin ? ' crossOrigin="' + crossOrigin + '"' : ''; - } - - function getImageSize(image, callback) { - var newImage; - - // Modern browsers (ignore Safari, #120 & #509) - if (image.naturalWidth && !IS_SAFARI) { - return callback(image.naturalWidth, image.naturalHeight); - } - - // IE8: Don't use `new Image()` here (#319) - newImage = document.createElement('img'); - - newImage.onload = function () { - callback(this.width, this.height); - }; - - newImage.src = image.src; - } - - function getTransform(options) { - var transforms = []; - var rotate = options.rotate; - var scaleX = options.scaleX; - var scaleY = options.scaleY; - - if (isNumber(rotate)) { - transforms.push('rotate(' + rotate + 'deg)'); - } - - if (isNumber(scaleX) && isNumber(scaleY)) { - transforms.push('scale(' + scaleX + ',' + scaleY + ')'); - } - - return transforms.length ? transforms.join(' ') : 'none'; - } - - function getRotatedSizes(data, isReversed) { - var deg = abs(data.degree) % 180; - var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180; - var sinArc = sin(arc); - var cosArc = cos(arc); - var width = data.width; - var height = data.height; - var aspectRatio = data.aspectRatio; - var newWidth; - var newHeight; - - if (!isReversed) { - newWidth = width * cosArc + height * sinArc; - newHeight = width * sinArc + height * cosArc; - } else { - newWidth = width / (cosArc + sinArc / aspectRatio); - newHeight = newWidth / aspectRatio; - } - - return { - width: newWidth, - height: newHeight - }; - } - - function getSourceCanvas(image, data) { - var canvas = $('')[0]; - var context = canvas.getContext('2d'); - var dstX = 0; - var dstY = 0; - var dstWidth = data.naturalWidth; - var dstHeight = data.naturalHeight; - var rotate = data.rotate; - var scaleX = data.scaleX; - var scaleY = data.scaleY; - var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1); - var rotatable = isNumber(rotate) && rotate !== 0; - var advanced = rotatable || scalable; - var canvasWidth = dstWidth * abs(scaleX || 1); - var canvasHeight = dstHeight * abs(scaleY || 1); - var translateX; - var translateY; - var rotated; - - if (scalable) { - translateX = canvasWidth / 2; - translateY = canvasHeight / 2; - } - - if (rotatable) { - rotated = getRotatedSizes({ - width: canvasWidth, - height: canvasHeight, - degree: rotate - }); - - canvasWidth = rotated.width; - canvasHeight = rotated.height; - translateX = canvasWidth / 2; - translateY = canvasHeight / 2; - } - - canvas.width = canvasWidth; - canvas.height = canvasHeight; - - if (advanced) { - dstX = -dstWidth / 2; - dstY = -dstHeight / 2; - - context.save(); - context.translate(translateX, translateY); - } - - if (rotatable) { - context.rotate(rotate * Math.PI / 180); - } - - // Should call `scale` after rotated - if (scalable) { - context.scale(scaleX, scaleY); - } - - context.drawImage(image, floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight)); - - if (advanced) { - context.restore(); - } - - return canvas; - } - - function getTouchesCenter(touches) { - var length = touches.length; - var pageX = 0; - var pageY = 0; - - if (length) { - $.each(touches, function (i, touch) { - pageX += touch.pageX; - pageY += touch.pageY; - }); - - pageX /= length; - pageY /= length; - } - - return { - pageX: pageX, - pageY: pageY - }; - } - - function getStringFromCharCode(dataView, start, length) { - var str = ''; - var i; - - for (i = start, length += start; i < length; i++) { - str += fromCharCode(dataView.getUint8(i)); - } - - return str; - } - - function getOrientation(arrayBuffer) { - var dataView = new DataView(arrayBuffer); - var length = dataView.byteLength; - var orientation; - var exifIDCode; - var tiffOffset; - var firstIFDOffset; - var littleEndian; - var endianness; - var app1Start; - var ifdStart; - var offset; - var i; - - // Only handle JPEG image (start by 0xFFD8) - if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) { - offset = 2; - - while (offset < length) { - if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) { - app1Start = offset; - break; - } - - offset++; - } - } - - if (app1Start) { - exifIDCode = app1Start + 4; - tiffOffset = app1Start + 10; - - if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') { - endianness = dataView.getUint16(tiffOffset); - littleEndian = endianness === 0x4949; - - if (littleEndian || endianness === 0x4D4D /* bigEndian */) { - if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) { - firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian); - - if (firstIFDOffset >= 0x00000008) { - ifdStart = tiffOffset + firstIFDOffset; - } - } - } - } - } - - if (ifdStart) { - length = dataView.getUint16(ifdStart, littleEndian); - - for (i = 0; i < length; i++) { - offset = ifdStart + i * 12 + 2; - - if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) { - - // 8 is the offset of the current tag's value - offset += 8; - - // Get the original orientation value - orientation = dataView.getUint16(offset, littleEndian); - - // Override the orientation with its default value for Safari (#120) - if (IS_SAFARI) { - dataView.setUint16(offset, 1, littleEndian); - } - - break; - } - } - } - - return orientation; - } - - function dataURLToArrayBuffer(dataURL) { - var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, ''); - var binary = atob(base64); - var length = binary.length; - var arrayBuffer = new ArrayBuffer(length); - var dataView = new Uint8Array(arrayBuffer); - var i; - - for (i = 0; i < length; i++) { - dataView[i] = binary.charCodeAt(i); - } - - return arrayBuffer; - } - - // Only available for JPEG image - function arrayBufferToDataURL(arrayBuffer) { - var dataView = new Uint8Array(arrayBuffer); - var length = dataView.length; - var base64 = ''; - var i; - - for (i = 0; i < length; i++) { - base64 += fromCharCode(dataView[i]); - } - - return 'data:image/jpeg;base64,' + btoa(base64); - } - - function Cropper(element, options) { - this.$element = $(element); - this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options); - this.isLoaded = false; - this.isBuilt = false; - this.isCompleted = false; - this.isRotated = false; - this.isCropped = false; - this.isDisabled = false; - this.isReplaced = false; - this.isLimited = false; - this.wheeling = false; - this.isImg = false; - this.originalUrl = ''; - this.canvas = null; - this.cropBox = null; - this.init(); - } - - Cropper.prototype = { - constructor: Cropper, - - init: function () { - var $this = this.$element; - var url; - - if ($this.is('img')) { - this.isImg = true; - - // Should use `$.fn.attr` here. e.g.: "img/picture.jpg" - this.originalUrl = url = $this.attr('src'); - - // Stop when it's a blank image - if (!url) { - return; - } - - // Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg" - url = $this.prop('src'); - } else if ($this.is('canvas') && SUPPORT_CANVAS) { - url = $this[0].toDataURL(); - } - - this.load(url); - }, - - // A shortcut for triggering custom events - trigger: function (type, data) { - var e = $.Event(type, data); - - this.$element.trigger(e); - - return e; - }, - - load: function (url) { - var options = this.options; - var $this = this.$element; - var read; - var xhr; - - if (!url) { - return; - } - - // Trigger build event first - $this.one(EVENT_BUILD, options.build); - - if (this.trigger(EVENT_BUILD).isDefaultPrevented()) { - return; - } - - this.url = url; - this.image = {}; - - if (!options.checkOrientation || !ArrayBuffer) { - return this.clone(); - } - - read = $.proxy(this.read, this); - - // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari - if (REGEXP_DATA_URL.test(url)) { - return REGEXP_DATA_URL_JPEG.test(url) ? - read(dataURLToArrayBuffer(url)) : - this.clone(); - } - - xhr = new XMLHttpRequest(); - - xhr.onerror = xhr.onabort = $.proxy(function () { - this.clone(); - }, this); - - xhr.onload = function () { - read(this.response); - }; - - xhr.open('get', url); - xhr.responseType = 'arraybuffer'; - xhr.send(); - }, - - read: function (arrayBuffer) { - var options = this.options; - var orientation = getOrientation(arrayBuffer); - var image = this.image; - var rotate; - var scaleX; - var scaleY; - - if (orientation > 1) { - this.url = arrayBufferToDataURL(arrayBuffer); - - switch (orientation) { - - // flip horizontal - case 2: - scaleX = -1; - break; - - // rotate left 180° - case 3: - rotate = -180; - break; - - // flip vertical - case 4: - scaleY = -1; - break; - - // flip vertical + rotate right 90° - case 5: - rotate = 90; - scaleY = -1; - break; - - // rotate right 90° - case 6: - rotate = 90; - break; - - // flip horizontal + rotate right 90° - case 7: - rotate = 90; - scaleX = -1; - break; - - // rotate left 90° - case 8: - rotate = -90; - break; - } - } - - if (options.rotatable) { - image.rotate = rotate; - } - - if (options.scalable) { - image.scaleX = scaleX; - image.scaleY = scaleY; - } - - this.clone(); - }, - - clone: function () { - var options = this.options; - var $this = this.$element; - var url = this.url; - var crossOrigin = ''; - var crossOriginUrl; - var $clone; - - if (options.checkCrossOrigin && isCrossOriginURL(url)) { - crossOrigin = $this.prop('crossOrigin'); - - if (crossOrigin) { - crossOriginUrl = url; - } else { - crossOrigin = 'anonymous'; - - // Bust cache (#148) when there is not a "crossOrigin" property - crossOriginUrl = addTimestamp(url); - } - } - - this.crossOrigin = crossOrigin; - this.crossOriginUrl = crossOriginUrl; - this.$clone = $clone = $(''); - - if (this.isImg) { - if ($this[0].complete) { - this.start(); - } else { - $this.one(EVENT_LOAD, $.proxy(this.start, this)); - } - } else { - $clone. - one(EVENT_LOAD, $.proxy(this.start, this)). - one(EVENT_ERROR, $.proxy(this.stop, this)). - addClass(CLASS_HIDE). - insertAfter($this); - } - }, - - start: function () { - var $image = this.$element; - var $clone = this.$clone; - - if (!this.isImg) { - $clone.off(EVENT_ERROR, this.stop); - $image = $clone; - } - - getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) { - $.extend(this.image, { - naturalWidth: naturalWidth, - naturalHeight: naturalHeight, - aspectRatio: naturalWidth / naturalHeight - }); - - this.isLoaded = true; - this.build(); - }, this)); - }, - - stop: function () { - this.$clone.remove(); - this.$clone = null; - }, - - build: function () { - var options = this.options; - var $this = this.$element; - var $clone = this.$clone; - var $cropper; - var $cropBox; - var $face; - - if (!this.isLoaded) { - return; - } - - // Unbuild first when replace - if (this.isBuilt) { - this.unbuild(); - } - - // Create cropper elements - this.$container = $this.parent(); - this.$cropper = $cropper = $(Cropper.TEMPLATE); - this.$canvas = $cropper.find('.cropper-canvas').append($clone); - this.$dragBox = $cropper.find('.cropper-drag-box'); - this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box'); - this.$viewBox = $cropper.find('.cropper-view-box'); - this.$face = $face = $cropBox.find('.cropper-face'); - - // Hide the original image - $this.addClass(CLASS_HIDDEN).after($cropper); - - // Show the clone image if is hidden - if (!this.isImg) { - $clone.removeClass(CLASS_HIDE); - } - - this.initPreview(); - this.bind(); - - options.aspectRatio = max(0, options.aspectRatio) || NaN; - options.viewMode = max(0, min(3, round(options.viewMode))) || 0; - - if (options.autoCrop) { - this.isCropped = true; - - if (options.modal) { - this.$dragBox.addClass(CLASS_MODAL); - } - } else { - $cropBox.addClass(CLASS_HIDDEN); - } - - if (!options.guides) { - $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN); - } - - if (!options.center) { - $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN); - } - - if (options.cropBoxMovable) { - $face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL); - } - - if (!options.highlight) { - $face.addClass(CLASS_INVISIBLE); - } - - if (options.background) { - $cropper.addClass(CLASS_BG); - } - - if (!options.cropBoxResizable) { - $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN); - } - - this.setDragMode(options.dragMode); - this.render(); - this.isBuilt = true; - this.setData(options.data); - $this.one(EVENT_BUILT, options.built); - - // Trigger the built event asynchronously to keep `data('cropper')` is defined - setTimeout($.proxy(function () { - this.trigger(EVENT_BUILT); - this.isCompleted = true; - }, this), 0); - }, - - unbuild: function () { - if (!this.isBuilt) { - return; - } - - this.isBuilt = false; - this.isCompleted = false; - this.initialImage = null; - - // Clear `initialCanvas` is necessary when replace - this.initialCanvas = null; - this.initialCropBox = null; - this.container = null; - this.canvas = null; - - // Clear `cropBox` is necessary when replace - this.cropBox = null; - this.unbind(); - - this.resetPreview(); - this.$preview = null; - - this.$viewBox = null; - this.$cropBox = null; - this.$dragBox = null; - this.$canvas = null; - this.$container = null; - - this.$cropper.remove(); - this.$cropper = null; - }, - - render: function () { - this.initContainer(); - this.initCanvas(); - this.initCropBox(); - - this.renderCanvas(); - - if (this.isCropped) { - this.renderCropBox(); - } - }, - - initContainer: function () { - var options = this.options; - var $this = this.$element; - var $container = this.$container; - var $cropper = this.$cropper; - - $cropper.addClass(CLASS_HIDDEN); - $this.removeClass(CLASS_HIDDEN); - - $cropper.css((this.container = { - width: max($container.width(), num(options.minContainerWidth) || 200), - height: max($container.height(), num(options.minContainerHeight) || 100) - })); - - $this.addClass(CLASS_HIDDEN); - $cropper.removeClass(CLASS_HIDDEN); - }, - - // Canvas (image wrapper) - initCanvas: function () { - var viewMode = this.options.viewMode; - var container = this.container; - var containerWidth = container.width; - var containerHeight = container.height; - var image = this.image; - var imageNaturalWidth = image.naturalWidth; - var imageNaturalHeight = image.naturalHeight; - var is90Degree = abs(image.rotate) === 90; - var naturalWidth = is90Degree ? imageNaturalHeight : imageNaturalWidth; - var naturalHeight = is90Degree ? imageNaturalWidth : imageNaturalHeight; - var aspectRatio = naturalWidth / naturalHeight; - var canvasWidth = containerWidth; - var canvasHeight = containerHeight; - var canvas; - - if (containerHeight * aspectRatio > containerWidth) { - if (viewMode === 3) { - canvasWidth = containerHeight * aspectRatio; - } else { - canvasHeight = containerWidth / aspectRatio; - } - } else { - if (viewMode === 3) { - canvasHeight = containerWidth / aspectRatio; - } else { - canvasWidth = containerHeight * aspectRatio; - } - } - - canvas = { - naturalWidth: naturalWidth, - naturalHeight: naturalHeight, - aspectRatio: aspectRatio, - width: canvasWidth, - height: canvasHeight - }; - - canvas.oldLeft = canvas.left = (containerWidth - canvasWidth) / 2; - canvas.oldTop = canvas.top = (containerHeight - canvasHeight) / 2; - - this.canvas = canvas; - this.isLimited = (viewMode === 1 || viewMode === 2); - this.limitCanvas(true, true); - this.initialImage = $.extend({}, image); - this.initialCanvas = $.extend({}, canvas); - }, - - limitCanvas: function (isSizeLimited, isPositionLimited) { - var options = this.options; - var viewMode = options.viewMode; - var container = this.container; - var containerWidth = container.width; - var containerHeight = container.height; - var canvas = this.canvas; - var aspectRatio = canvas.aspectRatio; - var cropBox = this.cropBox; - var isCropped = this.isCropped && cropBox; - var minCanvasWidth; - var minCanvasHeight; - var newCanvasLeft; - var newCanvasTop; - - if (isSizeLimited) { - minCanvasWidth = num(options.minCanvasWidth) || 0; - minCanvasHeight = num(options.minCanvasHeight) || 0; - - if (viewMode) { - if (viewMode > 1) { - minCanvasWidth = max(minCanvasWidth, containerWidth); - minCanvasHeight = max(minCanvasHeight, containerHeight); - - if (viewMode === 3) { - if (minCanvasHeight * aspectRatio > minCanvasWidth) { - minCanvasWidth = minCanvasHeight * aspectRatio; - } else { - minCanvasHeight = minCanvasWidth / aspectRatio; - } - } - } else { - if (minCanvasWidth) { - minCanvasWidth = max(minCanvasWidth, isCropped ? cropBox.width : 0); - } else if (minCanvasHeight) { - minCanvasHeight = max(minCanvasHeight, isCropped ? cropBox.height : 0); - } else if (isCropped) { - minCanvasWidth = cropBox.width; - minCanvasHeight = cropBox.height; - - if (minCanvasHeight * aspectRatio > minCanvasWidth) { - minCanvasWidth = minCanvasHeight * aspectRatio; - } else { - minCanvasHeight = minCanvasWidth / aspectRatio; - } - } - } - } - - if (minCanvasWidth && minCanvasHeight) { - if (minCanvasHeight * aspectRatio > minCanvasWidth) { - minCanvasHeight = minCanvasWidth / aspectRatio; - } else { - minCanvasWidth = minCanvasHeight * aspectRatio; - } - } else if (minCanvasWidth) { - minCanvasHeight = minCanvasWidth / aspectRatio; - } else if (minCanvasHeight) { - minCanvasWidth = minCanvasHeight * aspectRatio; - } - - canvas.minWidth = minCanvasWidth; - canvas.minHeight = minCanvasHeight; - canvas.maxWidth = Infinity; - canvas.maxHeight = Infinity; - } - - if (isPositionLimited) { - if (viewMode) { - newCanvasLeft = containerWidth - canvas.width; - newCanvasTop = containerHeight - canvas.height; - - canvas.minLeft = min(0, newCanvasLeft); - canvas.minTop = min(0, newCanvasTop); - canvas.maxLeft = max(0, newCanvasLeft); - canvas.maxTop = max(0, newCanvasTop); - - if (isCropped && this.isLimited) { - canvas.minLeft = min( - cropBox.left, - cropBox.left + cropBox.width - canvas.width - ); - canvas.minTop = min( - cropBox.top, - cropBox.top + cropBox.height - canvas.height - ); - canvas.maxLeft = cropBox.left; - canvas.maxTop = cropBox.top; - - if (viewMode === 2) { - if (canvas.width >= containerWidth) { - canvas.minLeft = min(0, newCanvasLeft); - canvas.maxLeft = max(0, newCanvasLeft); - } - - if (canvas.height >= containerHeight) { - canvas.minTop = min(0, newCanvasTop); - canvas.maxTop = max(0, newCanvasTop); - } - } - } - } else { - canvas.minLeft = -canvas.width; - canvas.minTop = -canvas.height; - canvas.maxLeft = containerWidth; - canvas.maxTop = containerHeight; - } - } - }, - - renderCanvas: function (isChanged) { - var canvas = this.canvas; - var image = this.image; - var rotate = image.rotate; - var naturalWidth = image.naturalWidth; - var naturalHeight = image.naturalHeight; - var aspectRatio; - var rotated; - - if (this.isRotated) { - this.isRotated = false; - - // Computes rotated sizes with image sizes - rotated = getRotatedSizes({ - width: image.width, - height: image.height, - degree: rotate - }); - - aspectRatio = rotated.width / rotated.height; - - if (aspectRatio !== canvas.aspectRatio) { - canvas.left -= (rotated.width - canvas.width) / 2; - canvas.top -= (rotated.height - canvas.height) / 2; - canvas.width = rotated.width; - canvas.height = rotated.height; - canvas.aspectRatio = aspectRatio; - canvas.naturalWidth = naturalWidth; - canvas.naturalHeight = naturalHeight; - - // Computes rotated sizes with natural image sizes - if (rotate % 180) { - rotated = getRotatedSizes({ - width: naturalWidth, - height: naturalHeight, - degree: rotate - }); - - canvas.naturalWidth = rotated.width; - canvas.naturalHeight = rotated.height; - } - - this.limitCanvas(true, false); - } - } - - if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) { - canvas.left = canvas.oldLeft; - } - - if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) { - canvas.top = canvas.oldTop; - } - - canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth); - canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight); - - this.limitCanvas(false, true); - - canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft); - canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop); - - this.$canvas.css({ - width: canvas.width, - height: canvas.height, - left: canvas.left, - top: canvas.top - }); - - this.renderImage(); - - if (this.isCropped && this.isLimited) { - this.limitCropBox(true, true); - } - - if (isChanged) { - this.output(); - } - }, - - renderImage: function (isChanged) { - var canvas = this.canvas; - var image = this.image; - var reversed; - - if (image.rotate) { - reversed = getRotatedSizes({ - width: canvas.width, - height: canvas.height, - degree: image.rotate, - aspectRatio: image.aspectRatio - }, true); - } - - $.extend(image, reversed ? { - width: reversed.width, - height: reversed.height, - left: (canvas.width - reversed.width) / 2, - top: (canvas.height - reversed.height) / 2 - } : { - width: canvas.width, - height: canvas.height, - left: 0, - top: 0 - }); - - this.$clone.css({ - width: image.width, - height: image.height, - marginLeft: image.left, - marginTop: image.top, - transform: getTransform(image) - }); - - if (isChanged) { - this.output(); - } - }, - - initCropBox: function () { - var options = this.options; - var canvas = this.canvas; - var aspectRatio = options.aspectRatio; - var autoCropArea = num(options.autoCropArea) || 0.8; - var cropBox = { - width: canvas.width, - height: canvas.height - }; - - if (aspectRatio) { - if (canvas.height * aspectRatio > canvas.width) { - cropBox.height = cropBox.width / aspectRatio; - } else { - cropBox.width = cropBox.height * aspectRatio; - } - } - - this.cropBox = cropBox; - this.limitCropBox(true, true); - - // Initialize auto crop area - cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth); - cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight); - - // The width of auto crop area must large than "minWidth", and the height too. (#164) - cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea); - cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea); - cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2; - cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2; - - this.initialCropBox = $.extend({}, cropBox); - }, - - limitCropBox: function (isSizeLimited, isPositionLimited) { - var options = this.options; - var aspectRatio = options.aspectRatio; - var container = this.container; - var containerWidth = container.width; - var containerHeight = container.height; - var canvas = this.canvas; - var cropBox = this.cropBox; - var isLimited = this.isLimited; - var minCropBoxWidth; - var minCropBoxHeight; - var maxCropBoxWidth; - var maxCropBoxHeight; - - if (isSizeLimited) { - minCropBoxWidth = num(options.minCropBoxWidth) || 0; - minCropBoxHeight = num(options.minCropBoxHeight) || 0; - - // The min/maxCropBoxWidth/Height must be less than containerWidth/Height - minCropBoxWidth = min(minCropBoxWidth, containerWidth); - minCropBoxHeight = min(minCropBoxHeight, containerHeight); - maxCropBoxWidth = min(containerWidth, isLimited ? canvas.width : containerWidth); - maxCropBoxHeight = min(containerHeight, isLimited ? canvas.height : containerHeight); - - if (aspectRatio) { - if (minCropBoxWidth && minCropBoxHeight) { - if (minCropBoxHeight * aspectRatio > minCropBoxWidth) { - minCropBoxHeight = minCropBoxWidth / aspectRatio; - } else { - minCropBoxWidth = minCropBoxHeight * aspectRatio; - } - } else if (minCropBoxWidth) { - minCropBoxHeight = minCropBoxWidth / aspectRatio; - } else if (minCropBoxHeight) { - minCropBoxWidth = minCropBoxHeight * aspectRatio; - } - - if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) { - maxCropBoxHeight = maxCropBoxWidth / aspectRatio; - } else { - maxCropBoxWidth = maxCropBoxHeight * aspectRatio; - } - } - - // The minWidth/Height must be less than maxWidth/Height - cropBox.minWidth = min(minCropBoxWidth, maxCropBoxWidth); - cropBox.minHeight = min(minCropBoxHeight, maxCropBoxHeight); - cropBox.maxWidth = maxCropBoxWidth; - cropBox.maxHeight = maxCropBoxHeight; - } - - if (isPositionLimited) { - if (isLimited) { - cropBox.minLeft = max(0, canvas.left); - cropBox.minTop = max(0, canvas.top); - cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width; - cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height; - } else { - cropBox.minLeft = 0; - cropBox.minTop = 0; - cropBox.maxLeft = containerWidth - cropBox.width; - cropBox.maxTop = containerHeight - cropBox.height; - } - } - }, - - renderCropBox: function () { - var options = this.options; - var container = this.container; - var containerWidth = container.width; - var containerHeight = container.height; - var cropBox = this.cropBox; - - if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) { - cropBox.left = cropBox.oldLeft; - } - - if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) { - cropBox.top = cropBox.oldTop; - } - - cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth); - cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight); - - this.limitCropBox(false, true); - - cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft); - cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop); - - if (options.movable && options.cropBoxMovable) { - - // Turn to move the canvas when the crop box is equal to the container - this.$face.data(DATA_ACTION, (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL); - } - - this.$cropBox.css({ - width: cropBox.width, - height: cropBox.height, - left: cropBox.left, - top: cropBox.top - }); - - if (this.isCropped && this.isLimited) { - this.limitCanvas(true, true); - } - - if (!this.isDisabled) { - this.output(); - } - }, - - output: function () { - this.preview(); - - if (this.isCompleted) { - this.trigger(EVENT_CROP, this.getData()); - } else if (!this.isBuilt) { - - // Only trigger one crop event before complete - this.$element.one(EVENT_BUILT, $.proxy(function () { - this.trigger(EVENT_CROP, this.getData()); - }, this)); - } - }, - - initPreview: function () { - var crossOrigin = getCrossOrigin(this.crossOrigin); - var url = crossOrigin ? this.crossOriginUrl : this.url; - var $clone2; - - this.$preview = $(this.options.preview); - this.$clone2 = $clone2 = $(''); - this.$viewBox.html($clone2); - this.$preview.each(function () { - var $this = $(this); - - // Save the original size for recover - $this.data(DATA_PREVIEW, { - width: $this.width(), - height: $this.height(), - html: $this.html() - }); - - /** - * Override img element styles - * Add `display:block` to avoid margin top issue - * (Occur only when margin-top <= -height) - */ - $this.html( - '' - ); - }); - }, - - resetPreview: function () { - this.$preview.each(function () { - var $this = $(this); - var data = $this.data(DATA_PREVIEW); - - $this.css({ - width: data.width, - height: data.height - }).html(data.html).removeData(DATA_PREVIEW); - }); - }, - - preview: function () { - var image = this.image; - var canvas = this.canvas; - var cropBox = this.cropBox; - var cropBoxWidth = cropBox.width; - var cropBoxHeight = cropBox.height; - var width = image.width; - var height = image.height; - var left = cropBox.left - canvas.left - image.left; - var top = cropBox.top - canvas.top - image.top; - - if (!this.isCropped || this.isDisabled) { - return; - } - - this.$clone2.css({ - width: width, - height: height, - marginLeft: -left, - marginTop: -top, - transform: getTransform(image) - }); - - this.$preview.each(function () { - var $this = $(this); - var data = $this.data(DATA_PREVIEW); - var originalWidth = data.width; - var originalHeight = data.height; - var newWidth = originalWidth; - var newHeight = originalHeight; - var ratio = 1; - - if (cropBoxWidth) { - ratio = originalWidth / cropBoxWidth; - newHeight = cropBoxHeight * ratio; - } - - if (cropBoxHeight && newHeight > originalHeight) { - ratio = originalHeight / cropBoxHeight; - newWidth = cropBoxWidth * ratio; - newHeight = originalHeight; - } - - $this.css({ - width: newWidth, - height: newHeight - }).find('img').css({ - width: width * ratio, - height: height * ratio, - marginLeft: -left * ratio, - marginTop: -top * ratio, - transform: getTransform(image) - }); - }); - }, - - bind: function () { - var options = this.options; - var $this = this.$element; - var $cropper = this.$cropper; - - if ($.isFunction(options.cropstart)) { - $this.on(EVENT_CROP_START, options.cropstart); - } - - if ($.isFunction(options.cropmove)) { - $this.on(EVENT_CROP_MOVE, options.cropmove); - } - - if ($.isFunction(options.cropend)) { - $this.on(EVENT_CROP_END, options.cropend); - } - - if ($.isFunction(options.crop)) { - $this.on(EVENT_CROP, options.crop); - } - - if ($.isFunction(options.zoom)) { - $this.on(EVENT_ZOOM, options.zoom); - } - - $cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this)); - - if (options.zoomable && options.zoomOnWheel) { - $cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this)); - } - - if (options.toggleDragModeOnDblclick) { - $cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this)); - } - - $document. - on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))). - on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this))); - - if (options.responsive) { - $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this))); - } - }, - - unbind: function () { - var options = this.options; - var $this = this.$element; - var $cropper = this.$cropper; - - if ($.isFunction(options.cropstart)) { - $this.off(EVENT_CROP_START, options.cropstart); - } - - if ($.isFunction(options.cropmove)) { - $this.off(EVENT_CROP_MOVE, options.cropmove); - } - - if ($.isFunction(options.cropend)) { - $this.off(EVENT_CROP_END, options.cropend); - } - - if ($.isFunction(options.crop)) { - $this.off(EVENT_CROP, options.crop); - } - - if ($.isFunction(options.zoom)) { - $this.off(EVENT_ZOOM, options.zoom); - } - - $cropper.off(EVENT_MOUSE_DOWN, this.cropStart); - - if (options.zoomable && options.zoomOnWheel) { - $cropper.off(EVENT_WHEEL, this.wheel); - } - - if (options.toggleDragModeOnDblclick) { - $cropper.off(EVENT_DBLCLICK, this.dblclick); - } - - $document. - off(EVENT_MOUSE_MOVE, this._cropMove). - off(EVENT_MOUSE_UP, this._cropEnd); - - if (options.responsive) { - $window.off(EVENT_RESIZE, this._resize); - } - }, - - resize: function () { - var restore = this.options.restore; - var $container = this.$container; - var container = this.container; - var canvasData; - var cropBoxData; - var ratio; - - // Check `container` is necessary for IE8 - if (this.isDisabled || !container) { - return; - } - - ratio = $container.width() / container.width; - - // Resize when width changed or height changed - if (ratio !== 1 || $container.height() !== container.height) { - if (restore) { - canvasData = this.getCanvasData(); - cropBoxData = this.getCropBoxData(); - } - - this.render(); - - if (restore) { - this.setCanvasData($.each(canvasData, function (i, n) { - canvasData[i] = n * ratio; - })); - this.setCropBoxData($.each(cropBoxData, function (i, n) { - cropBoxData[i] = n * ratio; - })); - } - } - }, - - dblclick: function () { - if (this.isDisabled) { - return; - } - - if (this.$dragBox.hasClass(CLASS_CROP)) { - this.setDragMode(ACTION_MOVE); - } else { - this.setDragMode(ACTION_CROP); - } - }, - - wheel: function (event) { - var e = event.originalEvent || event; - var ratio = num(this.options.wheelZoomRatio) || 0.1; - var delta = 1; - - if (this.isDisabled) { - return; - } - - event.preventDefault(); - - // Limit wheel speed to prevent zoom too fast - if (this.wheeling) { - return; - } - - this.wheeling = true; - - setTimeout($.proxy(function () { - this.wheeling = false; - }, this), 50); - - if (e.deltaY) { - delta = e.deltaY > 0 ? 1 : -1; - } else if (e.wheelDelta) { - delta = -e.wheelDelta / 120; - } else if (e.detail) { - delta = e.detail > 0 ? 1 : -1; - } - - this.zoom(-delta * ratio, event); - }, - - cropStart: function (event) { - var options = this.options; - var originalEvent = event.originalEvent; - var touches = originalEvent && originalEvent.touches; - var e = event; - var touchesLength; - var action; - - if (this.isDisabled) { - return; - } - - if (touches) { - touchesLength = touches.length; - - if (touchesLength > 1) { - if (options.zoomable && options.zoomOnTouch && touchesLength === 2) { - e = touches[1]; - this.startX2 = e.pageX; - this.startY2 = e.pageY; - action = ACTION_ZOOM; - } else { - return; - } - } - - e = touches[0]; - } - - action = action || $(e.target).data(DATA_ACTION); - - if (REGEXP_ACTIONS.test(action)) { - if (this.trigger(EVENT_CROP_START, { - originalEvent: originalEvent, - action: action - }).isDefaultPrevented()) { - return; - } - - event.preventDefault(); - - this.action = action; - this.cropping = false; - - // IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y` - // IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y` - this.startX = e.pageX || originalEvent && originalEvent.pageX; - this.startY = e.pageY || originalEvent && originalEvent.pageY; - - if (action === ACTION_CROP) { - this.cropping = true; - this.$dragBox.addClass(CLASS_MODAL); - } - } - }, - - cropMove: function (event) { - var options = this.options; - var originalEvent = event.originalEvent; - var touches = originalEvent && originalEvent.touches; - var e = event; - var action = this.action; - var touchesLength; - - if (this.isDisabled) { - return; - } - - if (touches) { - touchesLength = touches.length; - - if (touchesLength > 1) { - if (options.zoomable && options.zoomOnTouch && touchesLength === 2) { - e = touches[1]; - this.endX2 = e.pageX; - this.endY2 = e.pageY; - } else { - return; - } - } - - e = touches[0]; - } - - if (action) { - if (this.trigger(EVENT_CROP_MOVE, { - originalEvent: originalEvent, - action: action - }).isDefaultPrevented()) { - return; - } - - event.preventDefault(); - - this.endX = e.pageX || originalEvent && originalEvent.pageX; - this.endY = e.pageY || originalEvent && originalEvent.pageY; - - this.change(e.shiftKey, action === ACTION_ZOOM ? event : null); - } - }, - - cropEnd: function (event) { - var originalEvent = event.originalEvent; - var action = this.action; - - if (this.isDisabled) { - return; - } - - if (action) { - event.preventDefault(); - - if (this.cropping) { - this.cropping = false; - this.$dragBox.toggleClass(CLASS_MODAL, this.isCropped && this.options.modal); - } - - this.action = ''; - - this.trigger(EVENT_CROP_END, { - originalEvent: originalEvent, - action: action - }); - } - }, - - change: function (shiftKey, event) { - var options = this.options; - var aspectRatio = options.aspectRatio; - var action = this.action; - var container = this.container; - var canvas = this.canvas; - var cropBox = this.cropBox; - var width = cropBox.width; - var height = cropBox.height; - var left = cropBox.left; - var top = cropBox.top; - var right = left + width; - var bottom = top + height; - var minLeft = 0; - var minTop = 0; - var maxWidth = container.width; - var maxHeight = container.height; - var renderable = true; - var offset; - var range; - - // Locking aspect ratio in "free mode" by holding shift key (#259) - if (!aspectRatio && shiftKey) { - aspectRatio = width && height ? width / height : 1; - } - - if (this.limited) { - minLeft = cropBox.minLeft; - minTop = cropBox.minTop; - maxWidth = minLeft + min(container.width, canvas.left + canvas.width); - maxHeight = minTop + min(container.height, canvas.top + canvas.height); - } - - range = { - x: this.endX - this.startX, - y: this.endY - this.startY - }; - - if (aspectRatio) { - range.X = range.y * aspectRatio; - range.Y = range.x / aspectRatio; - } - - switch (action) { - // Move crop box - case ACTION_ALL: - left += range.x; - top += range.y; - break; - - // Resize crop box - case ACTION_EAST: - if (range.x >= 0 && (right >= maxWidth || aspectRatio && - (top <= minTop || bottom >= maxHeight))) { - - renderable = false; - break; - } - - width += range.x; - - if (aspectRatio) { - height = width / aspectRatio; - top -= range.Y / 2; - } - - if (width < 0) { - action = ACTION_WEST; - width = 0; - } - - break; - - case ACTION_NORTH: - if (range.y <= 0 && (top <= minTop || aspectRatio && - (left <= minLeft || right >= maxWidth))) { - - renderable = false; - break; - } - - height -= range.y; - top += range.y; - - if (aspectRatio) { - width = height * aspectRatio; - left += range.X / 2; - } - - if (height < 0) { - action = ACTION_SOUTH; - height = 0; - } - - break; - - case ACTION_WEST: - if (range.x <= 0 && (left <= minLeft || aspectRatio && - (top <= minTop || bottom >= maxHeight))) { - - renderable = false; - break; - } - - width -= range.x; - left += range.x; - - if (aspectRatio) { - height = width / aspectRatio; - top += range.Y / 2; - } - - if (width < 0) { - action = ACTION_EAST; - width = 0; - } - - break; - - case ACTION_SOUTH: - if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && - (left <= minLeft || right >= maxWidth))) { - - renderable = false; - break; - } - - height += range.y; - - if (aspectRatio) { - width = height * aspectRatio; - left -= range.X / 2; - } - - if (height < 0) { - action = ACTION_NORTH; - height = 0; - } - - break; - - case ACTION_NORTH_EAST: - if (aspectRatio) { - if (range.y <= 0 && (top <= minTop || right >= maxWidth)) { - renderable = false; - break; - } - - height -= range.y; - top += range.y; - width = height * aspectRatio; - } else { - if (range.x >= 0) { - if (right < maxWidth) { - width += range.x; - } else if (range.y <= 0 && top <= minTop) { - renderable = false; - } - } else { - width += range.x; - } - - if (range.y <= 0) { - if (top > minTop) { - height -= range.y; - top += range.y; - } - } else { - height -= range.y; - top += range.y; - } - } - - if (width < 0 && height < 0) { - action = ACTION_SOUTH_WEST; - height = 0; - width = 0; - } else if (width < 0) { - action = ACTION_NORTH_WEST; - width = 0; - } else if (height < 0) { - action = ACTION_SOUTH_EAST; - height = 0; - } - - break; - - case ACTION_NORTH_WEST: - if (aspectRatio) { - if (range.y <= 0 && (top <= minTop || left <= minLeft)) { - renderable = false; - break; - } - - height -= range.y; - top += range.y; - width = height * aspectRatio; - left += range.X; - } else { - if (range.x <= 0) { - if (left > minLeft) { - width -= range.x; - left += range.x; - } else if (range.y <= 0 && top <= minTop) { - renderable = false; - } - } else { - width -= range.x; - left += range.x; - } - - if (range.y <= 0) { - if (top > minTop) { - height -= range.y; - top += range.y; - } - } else { - height -= range.y; - top += range.y; - } - } - - if (width < 0 && height < 0) { - action = ACTION_SOUTH_EAST; - height = 0; - width = 0; - } else if (width < 0) { - action = ACTION_NORTH_EAST; - width = 0; - } else if (height < 0) { - action = ACTION_SOUTH_WEST; - height = 0; - } - - break; - - case ACTION_SOUTH_WEST: - if (aspectRatio) { - if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) { - renderable = false; - break; - } - - width -= range.x; - left += range.x; - height = width / aspectRatio; - } else { - if (range.x <= 0) { - if (left > minLeft) { - width -= range.x; - left += range.x; - } else if (range.y >= 0 && bottom >= maxHeight) { - renderable = false; - } - } else { - width -= range.x; - left += range.x; - } - - if (range.y >= 0) { - if (bottom < maxHeight) { - height += range.y; - } - } else { - height += range.y; - } - } - - if (width < 0 && height < 0) { - action = ACTION_NORTH_EAST; - height = 0; - width = 0; - } else if (width < 0) { - action = ACTION_SOUTH_EAST; - width = 0; - } else if (height < 0) { - action = ACTION_NORTH_WEST; - height = 0; - } - - break; - - case ACTION_SOUTH_EAST: - if (aspectRatio) { - if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) { - renderable = false; - break; - } - - width += range.x; - height = width / aspectRatio; - } else { - if (range.x >= 0) { - if (right < maxWidth) { - width += range.x; - } else if (range.y >= 0 && bottom >= maxHeight) { - renderable = false; - } - } else { - width += range.x; - } - - if (range.y >= 0) { - if (bottom < maxHeight) { - height += range.y; - } - } else { - height += range.y; - } - } - - if (width < 0 && height < 0) { - action = ACTION_NORTH_WEST; - height = 0; - width = 0; - } else if (width < 0) { - action = ACTION_SOUTH_WEST; - width = 0; - } else if (height < 0) { - action = ACTION_NORTH_EAST; - height = 0; - } - - break; - - // Move canvas - case ACTION_MOVE: - this.move(range.x, range.y); - renderable = false; - break; - - // Zoom canvas - case ACTION_ZOOM: - this.zoom((function (x1, y1, x2, y2) { - var z1 = sqrt(x1 * x1 + y1 * y1); - var z2 = sqrt(x2 * x2 + y2 * y2); - - return (z2 - z1) / z1; - })( - abs(this.startX - this.startX2), - abs(this.startY - this.startY2), - abs(this.endX - this.endX2), - abs(this.endY - this.endY2) - ), event); - this.startX2 = this.endX2; - this.startY2 = this.endY2; - renderable = false; - break; - - // Create crop box - case ACTION_CROP: - if (!range.x || !range.y) { - renderable = false; - break; - } - - offset = this.$cropper.offset(); - left = this.startX - offset.left; - top = this.startY - offset.top; - width = cropBox.minWidth; - height = cropBox.minHeight; - - if (range.x > 0) { - action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST; - } else if (range.x < 0) { - left -= width; - action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST; - } - - if (range.y < 0) { - top -= height; - } - - // Show the crop box if is hidden - if (!this.isCropped) { - this.$cropBox.removeClass(CLASS_HIDDEN); - this.isCropped = true; - - if (this.limited) { - this.limitCropBox(true, true); - } - } - - break; - - // No default - } - - if (renderable) { - cropBox.width = width; - cropBox.height = height; - cropBox.left = left; - cropBox.top = top; - this.action = action; - - this.renderCropBox(); - } - - // Override - this.startX = this.endX; - this.startY = this.endY; - }, - - // Show the crop box manually - crop: function () { - if (!this.isBuilt || this.isDisabled) { - return; - } - - if (!this.isCropped) { - this.isCropped = true; - this.limitCropBox(true, true); - - if (this.options.modal) { - this.$dragBox.addClass(CLASS_MODAL); - } - - this.$cropBox.removeClass(CLASS_HIDDEN); - } - - this.setCropBoxData(this.initialCropBox); - }, - - // Reset the image and crop box to their initial states - reset: function () { - if (!this.isBuilt || this.isDisabled) { - return; - } - - this.image = $.extend({}, this.initialImage); - this.canvas = $.extend({}, this.initialCanvas); - this.cropBox = $.extend({}, this.initialCropBox); - - this.renderCanvas(); - - if (this.isCropped) { - this.renderCropBox(); - } - }, - - // Clear the crop box - clear: function () { - if (!this.isCropped || this.isDisabled) { - return; - } - - $.extend(this.cropBox, { - left: 0, - top: 0, - width: 0, - height: 0 - }); - - this.isCropped = false; - this.renderCropBox(); - - this.limitCanvas(true, true); - - // Render canvas after crop box rendered - this.renderCanvas(); - - this.$dragBox.removeClass(CLASS_MODAL); - this.$cropBox.addClass(CLASS_HIDDEN); - }, - - /** - * Replace the image's src and rebuild the cropper - * - * @param {String} url - * @param {Boolean} onlyColorChanged (optional) - */ - replace: function (url, onlyColorChanged) { - if (!this.isDisabled && url) { - if (this.isImg) { - this.$element.attr('src', url); - } - - if (onlyColorChanged) { - this.url = url; - this.$clone.attr('src', url); - - if (this.isBuilt) { - this.$preview.find('img').add(this.$clone2).attr('src', url); - } - } else { - if (this.isImg) { - this.isReplaced = true; - } - - // Clear previous data - this.options.data = null; - this.load(url); - } - } - }, - - // Enable (unfreeze) the cropper - enable: function () { - if (this.isBuilt) { - this.isDisabled = false; - this.$cropper.removeClass(CLASS_DISABLED); - } - }, - - // Disable (freeze) the cropper - disable: function () { - if (this.isBuilt) { - this.isDisabled = true; - this.$cropper.addClass(CLASS_DISABLED); - } - }, - - // Destroy the cropper and remove the instance from the image - destroy: function () { - var $this = this.$element; - - if (this.isLoaded) { - if (this.isImg && this.isReplaced) { - $this.attr('src', this.originalUrl); - } - - this.unbuild(); - $this.removeClass(CLASS_HIDDEN); - } else { - if (this.isImg) { - $this.off(EVENT_LOAD, this.start); - } else if (this.$clone) { - this.$clone.remove(); - } - } - - $this.removeData(NAMESPACE); - }, - - /** - * Move the canvas with relative offsets - * - * @param {Number} offsetX - * @param {Number} offsetY (optional) - */ - move: function (offsetX, offsetY) { - var canvas = this.canvas; - - this.moveTo( - isUndefined(offsetX) ? offsetX : canvas.left + num(offsetX), - isUndefined(offsetY) ? offsetY : canvas.top + num(offsetY) - ); - }, - - /** - * Move the canvas to an absolute point - * - * @param {Number} x - * @param {Number} y (optional) - */ - moveTo: function (x, y) { - var canvas = this.canvas; - var isChanged = false; - - // If "y" is not present, its default value is "x" - if (isUndefined(y)) { - y = x; - } - - x = num(x); - y = num(y); - - if (this.isBuilt && !this.isDisabled && this.options.movable) { - if (isNumber(x)) { - canvas.left = x; - isChanged = true; - } - - if (isNumber(y)) { - canvas.top = y; - isChanged = true; - } - - if (isChanged) { - this.renderCanvas(true); - } - } - }, - - /** - * Zoom the canvas with a relative ratio - * - * @param {Number} ratio - * @param {jQuery Event} _event (private) - */ - zoom: function (ratio, _event) { - var canvas = this.canvas; - - ratio = num(ratio); - - if (ratio < 0) { - ratio = 1 / (1 - ratio); - } else { - ratio = 1 + ratio; - } - - this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _event); - }, - - /** - * Zoom the canvas to an absolute ratio - * - * @param {Number} ratio - * @param {jQuery Event} _event (private) - */ - zoomTo: function (ratio, _event) { - var options = this.options; - var canvas = this.canvas; - var width = canvas.width; - var height = canvas.height; - var naturalWidth = canvas.naturalWidth; - var naturalHeight = canvas.naturalHeight; - var originalEvent; - var newWidth; - var newHeight; - var offset; - var center; - - ratio = num(ratio); - - if (ratio >= 0 && this.isBuilt && !this.isDisabled && options.zoomable) { - newWidth = naturalWidth * ratio; - newHeight = naturalHeight * ratio; - - if (_event) { - originalEvent = _event.originalEvent; - } - - if (this.trigger(EVENT_ZOOM, { - originalEvent: originalEvent, - oldRatio: width / naturalWidth, - ratio: newWidth / naturalWidth - }).isDefaultPrevented()) { - return; - } - - if (originalEvent) { - offset = this.$cropper.offset(); - center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : { - pageX: _event.pageX || originalEvent.pageX || 0, - pageY: _event.pageY || originalEvent.pageY || 0 - }; - - // Zoom from the triggering point of the event - canvas.left -= (newWidth - width) * ( - ((center.pageX - offset.left) - canvas.left) / width - ); - canvas.top -= (newHeight - height) * ( - ((center.pageY - offset.top) - canvas.top) / height - ); - } else { - - // Zoom from the center of the canvas - canvas.left -= (newWidth - width) / 2; - canvas.top -= (newHeight - height) / 2; - } - - canvas.width = newWidth; - canvas.height = newHeight; - this.renderCanvas(true); - } - }, - - /** - * Rotate the canvas with a relative degree - * - * @param {Number} degree - */ - rotate: function (degree) { - this.rotateTo((this.image.rotate || 0) + num(degree)); - }, - - /** - * Rotate the canvas to an absolute degree - * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate() - * - * @param {Number} degree - */ - rotateTo: function (degree) { - degree = num(degree); - - if (isNumber(degree) && this.isBuilt && !this.isDisabled && this.options.rotatable) { - this.image.rotate = degree % 360; - this.isRotated = true; - this.renderCanvas(true); - } - }, - - /** - * Scale the image - * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale() - * - * @param {Number} scaleX - * @param {Number} scaleY (optional) - */ - scale: function (scaleX, scaleY) { - var image = this.image; - var isChanged = false; - - // If "scaleY" is not present, its default value is "scaleX" - if (isUndefined(scaleY)) { - scaleY = scaleX; - } - - scaleX = num(scaleX); - scaleY = num(scaleY); - - if (this.isBuilt && !this.isDisabled && this.options.scalable) { - if (isNumber(scaleX)) { - image.scaleX = scaleX; - isChanged = true; - } - - if (isNumber(scaleY)) { - image.scaleY = scaleY; - isChanged = true; - } - - if (isChanged) { - this.renderImage(true); - } - } - }, - - /** - * Scale the abscissa of the image - * - * @param {Number} scaleX - */ - scaleX: function (scaleX) { - var scaleY = this.image.scaleY; - - this.scale(scaleX, isNumber(scaleY) ? scaleY : 1); - }, - - /** - * Scale the ordinate of the image - * - * @param {Number} scaleY - */ - scaleY: function (scaleY) { - var scaleX = this.image.scaleX; - - this.scale(isNumber(scaleX) ? scaleX : 1, scaleY); - }, - - /** - * Get the cropped area position and size data (base on the original image) - * - * @param {Boolean} isRounded (optional) - * @return {Object} data - */ - getData: function (isRounded) { - var options = this.options; - var image = this.image; - var canvas = this.canvas; - var cropBox = this.cropBox; - var ratio; - var data; - - if (this.isBuilt && this.isCropped) { - data = { - x: cropBox.left - canvas.left, - y: cropBox.top - canvas.top, - width: cropBox.width, - height: cropBox.height - }; - - ratio = image.width / image.naturalWidth; - - $.each(data, function (i, n) { - n = n / ratio; - data[i] = isRounded ? round(n) : n; - }); - - } else { - data = { - x: 0, - y: 0, - width: 0, - height: 0 - }; - } - - if (options.rotatable) { - data.rotate = image.rotate || 0; - } - - if (options.scalable) { - data.scaleX = image.scaleX || 1; - data.scaleY = image.scaleY || 1; - } - - return data; - }, - - /** - * Set the cropped area position and size with new data - * - * @param {Object} data - */ - setData: function (data) { - var options = this.options; - var image = this.image; - var canvas = this.canvas; - var cropBoxData = {}; - var isRotated; - var isScaled; - var ratio; - - if ($.isFunction(data)) { - data = data.call(this.element); - } - - if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) { - if (options.rotatable) { - if (isNumber(data.rotate) && data.rotate !== image.rotate) { - image.rotate = data.rotate; - this.isRotated = isRotated = true; - } - } - - if (options.scalable) { - if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) { - image.scaleX = data.scaleX; - isScaled = true; - } - - if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) { - image.scaleY = data.scaleY; - isScaled = true; - } - } - - if (isRotated) { - this.renderCanvas(); - } else if (isScaled) { - this.renderImage(); - } - - ratio = image.width / image.naturalWidth; - - if (isNumber(data.x)) { - cropBoxData.left = data.x * ratio + canvas.left; - } - - if (isNumber(data.y)) { - cropBoxData.top = data.y * ratio + canvas.top; - } - - if (isNumber(data.width)) { - cropBoxData.width = data.width * ratio; - } - - if (isNumber(data.height)) { - cropBoxData.height = data.height * ratio; - } - - this.setCropBoxData(cropBoxData); - } - }, - - /** - * Get the container size data - * - * @return {Object} data - */ - getContainerData: function () { - return this.isBuilt ? this.container : {}; - }, - - /** - * Get the image position and size data - * - * @return {Object} data - */ - getImageData: function () { - return this.isLoaded ? this.image : {}; - }, - - /** - * Get the canvas position and size data - * - * @return {Object} data - */ - getCanvasData: function () { - var canvas = this.canvas; - var data = {}; - - if (this.isBuilt) { - $.each([ - 'left', - 'top', - 'width', - 'height', - 'naturalWidth', - 'naturalHeight' - ], function (i, n) { - data[n] = canvas[n]; - }); - } - - return data; - }, - - /** - * Set the canvas position and size with new data - * - * @param {Object} data - */ - setCanvasData: function (data) { - var canvas = this.canvas; - var aspectRatio = canvas.aspectRatio; - - if ($.isFunction(data)) { - data = data.call(this.$element); - } - - if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) { - if (isNumber(data.left)) { - canvas.left = data.left; - } - - if (isNumber(data.top)) { - canvas.top = data.top; - } - - if (isNumber(data.width)) { - canvas.width = data.width; - canvas.height = data.width / aspectRatio; - } else if (isNumber(data.height)) { - canvas.height = data.height; - canvas.width = data.height * aspectRatio; - } - - this.renderCanvas(true); - } - }, - - /** - * Get the crop box position and size data - * - * @return {Object} data - */ - getCropBoxData: function () { - var cropBox = this.cropBox; - var data; - - if (this.isBuilt && this.isCropped) { - data = { - left: cropBox.left, - top: cropBox.top, - width: cropBox.width, - height: cropBox.height - }; - } - - return data || {}; - }, - - /** - * Set the crop box position and size with new data - * - * @param {Object} data - */ - setCropBoxData: function (data) { - var cropBox = this.cropBox; - var aspectRatio = this.options.aspectRatio; - var isWidthChanged; - var isHeightChanged; - - if ($.isFunction(data)) { - data = data.call(this.$element); - } - - if (this.isBuilt && this.isCropped && !this.isDisabled && $.isPlainObject(data)) { - - if (isNumber(data.left)) { - cropBox.left = data.left; - } - - if (isNumber(data.top)) { - cropBox.top = data.top; - } - - if (isNumber(data.width)) { - isWidthChanged = true; - cropBox.width = data.width; - } - - if (isNumber(data.height)) { - isHeightChanged = true; - cropBox.height = data.height; - } - - if (aspectRatio) { - if (isWidthChanged) { - cropBox.height = cropBox.width / aspectRatio; - } else if (isHeightChanged) { - cropBox.width = cropBox.height * aspectRatio; - } - } - - this.renderCropBox(); - } - }, - - /** - * Get a canvas drawn the cropped image - * - * @param {Object} options (optional) - * @return {HTMLCanvasElement} canvas - */ - getCroppedCanvas: function (options) { - var originalWidth; - var originalHeight; - var canvasWidth; - var canvasHeight; - var scaledWidth; - var scaledHeight; - var scaledRatio; - var aspectRatio; - var canvas; - var context; - var data; - - if (!this.isBuilt || !this.isCropped || !SUPPORT_CANVAS) { - return; - } - - if (!$.isPlainObject(options)) { - options = {}; - } - - data = this.getData(); - originalWidth = data.width; - originalHeight = data.height; - aspectRatio = originalWidth / originalHeight; - - if ($.isPlainObject(options)) { - scaledWidth = options.width; - scaledHeight = options.height; - - if (scaledWidth) { - scaledHeight = scaledWidth / aspectRatio; - scaledRatio = scaledWidth / originalWidth; - } else if (scaledHeight) { - scaledWidth = scaledHeight * aspectRatio; - scaledRatio = scaledHeight / originalHeight; - } - } - - // The canvas element will use `Math.floor` on a float number, so floor first - canvasWidth = floor(scaledWidth || originalWidth); - canvasHeight = floor(scaledHeight || originalHeight); - - canvas = $('')[0]; - canvas.width = canvasWidth; - canvas.height = canvasHeight; - context = canvas.getContext('2d'); - - if (options.fillColor) { - context.fillStyle = options.fillColor; - context.fillRect(0, 0, canvasWidth, canvasHeight); - } - - // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage - context.drawImage.apply(context, (function () { - var source = getSourceCanvas(this.$clone[0], this.image); - var sourceWidth = source.width; - var sourceHeight = source.height; - var canvas = this.canvas; - var params = [source]; - - // Source canvas - var srcX = data.x + canvas.naturalWidth * (abs(data.scaleX || 1) - 1) / 2; - var srcY = data.y + canvas.naturalHeight * (abs(data.scaleY || 1) - 1) / 2; - var srcWidth; - var srcHeight; - - // Destination canvas - var dstX; - var dstY; - var dstWidth; - var dstHeight; - - if (srcX <= -originalWidth || srcX > sourceWidth) { - srcX = srcWidth = dstX = dstWidth = 0; - } else if (srcX <= 0) { - dstX = -srcX; - srcX = 0; - srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX); - } else if (srcX <= sourceWidth) { - dstX = 0; - srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX); - } - - if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) { - srcY = srcHeight = dstY = dstHeight = 0; - } else if (srcY <= 0) { - dstY = -srcY; - srcY = 0; - srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY); - } else if (srcY <= sourceHeight) { - dstY = 0; - srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY); - } - - // All the numerical parameters should be integer for `drawImage` (#476) - params.push(floor(srcX), floor(srcY), floor(srcWidth), floor(srcHeight)); - - // Scale destination sizes - if (scaledRatio) { - dstX *= scaledRatio; - dstY *= scaledRatio; - dstWidth *= scaledRatio; - dstHeight *= scaledRatio; - } - - // Avoid "IndexSizeError" in IE and Firefox - if (dstWidth > 0 && dstHeight > 0) { - params.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight)); - } - - return params; - }).call(this)); - - return canvas; - }, - - /** - * Change the aspect ratio of the crop box - * - * @param {Number} aspectRatio - */ - setAspectRatio: function (aspectRatio) { - var options = this.options; - - if (!this.isDisabled && !isUndefined(aspectRatio)) { - - // 0 -> NaN - options.aspectRatio = max(0, aspectRatio) || NaN; - - if (this.isBuilt) { - this.initCropBox(); - - if (this.isCropped) { - this.renderCropBox(); - } - } - } - }, - - /** - * Change the drag mode - * - * @param {String} mode (optional) - */ - setDragMode: function (mode) { - var options = this.options; - var croppable; - var movable; - - if (this.isLoaded && !this.isDisabled) { - croppable = mode === ACTION_CROP; - movable = options.movable && mode === ACTION_MOVE; - mode = (croppable || movable) ? mode : ACTION_NONE; - - this.$dragBox. - data(DATA_ACTION, mode). - toggleClass(CLASS_CROP, croppable). - toggleClass(CLASS_MOVE, movable); - - if (!options.cropBoxMovable) { - - // Sync drag mode to crop box when it is not movable(#300) - this.$face. - data(DATA_ACTION, mode). - toggleClass(CLASS_CROP, croppable). - toggleClass(CLASS_MOVE, movable); - } - } - } - }; - - Cropper.DEFAULTS = { - - // Define the view mode of the cropper - viewMode: 0, // 0, 1, 2, 3 - - // Define the dragging mode of the cropper - dragMode: 'crop', // 'crop', 'move' or 'none' - - // Define the aspect ratio of the crop box - aspectRatio: NaN, - - // An object with the previous cropping result data - data: null, - - // A jQuery selector for adding extra containers to preview - preview: '', - - // Re-render the cropper when resize the window - responsive: true, - - // Restore the cropped area after resize the window - restore: true, - - // Check if the current image is a cross-origin image - checkCrossOrigin: true, - - // Check the current image's Exif Orientation information - checkOrientation: true, - - // Show the black modal - modal: true, - - // Show the dashed lines for guiding - guides: true, - - // Show the center indicator for guiding - center: true, - - // Show the white modal to highlight the crop box - highlight: true, - - // Show the grid background - background: true, - - // Enable to crop the image automatically when initialize - autoCrop: true, - - // Define the percentage of automatic cropping area when initializes - autoCropArea: 0.8, - - // Enable to move the image - movable: true, - - // Enable to rotate the image - rotatable: true, - - // Enable to scale the image - scalable: true, - - // Enable to zoom the image - zoomable: true, - - // Enable to zoom the image by dragging touch - zoomOnTouch: true, - - // Enable to zoom the image by wheeling mouse - zoomOnWheel: true, - - // Define zoom ratio when zoom the image by wheeling mouse - wheelZoomRatio: 0.1, - - // Enable to move the crop box - cropBoxMovable: true, - - // Enable to resize the crop box - cropBoxResizable: true, - - // Toggle drag mode between "crop" and "move" when click twice on the cropper - toggleDragModeOnDblclick: true, - - // Size limitation - minCanvasWidth: 0, - minCanvasHeight: 0, - minCropBoxWidth: 0, - minCropBoxHeight: 0, - minContainerWidth: 200, - minContainerHeight: 100, - - // Shortcuts of events - build: null, - built: null, - cropstart: null, - cropmove: null, - cropend: null, - crop: null, - zoom: null - }; - - Cropper.setDefaults = function (options) { - $.extend(Cropper.DEFAULTS, options); - }; - - Cropper.TEMPLATE = ( - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
' + - '
' - ); - - // Save the other cropper - Cropper.other = $.fn.cropper; - - // Register as jQuery plugin - $.fn.cropper = function (option) { - var args = toArray(arguments, 1); - var result; - - this.each(function () { - var $this = $(this); - var data = $this.data(NAMESPACE); - var options; - var fn; - - if (!data) { - if (/destroy/.test(option)) { - return; - } - - options = $.extend({}, $this.data(), $.isPlainObject(option) && option); - $this.data(NAMESPACE, (data = new Cropper(this, options))); - } - - if (typeof option === 'string' && $.isFunction(fn = data[option])) { - result = fn.apply(data, args); - } - }); - - return isUndefined(result) ? this : result; - }; - - $.fn.cropper.Constructor = Cropper; - $.fn.cropper.setDefaults = Cropper.setDefaults; - - // No conflict - $.fn.cropper.noConflict = function () { - $.fn.cropper = Cropper.other; - return this; - }; - -}); diff --git a/yarn.lock b/yarn.lock index 98da6a984d1..d56d7272fe2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1409,6 +1409,12 @@ create-hmac@^1.1.0, create-hmac@^1.1.2: create-hash "^1.1.0" inherits "^2.0.1" +cropper@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cropper/-/cropper-2.3.0.tgz#607461d4e7aa7a7fe15a26834b14b7f0c2801562" + dependencies: + jquery ">= 1.9.1" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -3169,7 +3175,7 @@ jquery-ujs@^1.2.1: dependencies: jquery ">=1.8.0" -jquery@>=1.8.0, jquery@^2.2.1: +"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" From caeaa704207fa475fb3050aecddc72af4ff75390 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 1 Aug 2017 11:45:39 -0300 Subject: [PATCH 076/108] link to CI/CD Deep Dive demo for #5800 --- doc/ci/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ci/README.md b/doc/ci/README.md index ca7266ac68f..10ea9467942 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -23,6 +23,7 @@ The first steps towards your GitLab CI journey. - [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/) - [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) - **Videos:** + - [Demo (Streamed live on Jul 17, 2017): GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195) - [Demo (March, 2017): how to get started using CI/CD with GitLab](https://about.gitlab.com/2017/03/13/ci-cd-demo/) - [Webcast (April, 2016): getting started with CI in GitLab](https://about.gitlab.com/2016/04/20/webcast-recording-and-slides-introduction-to-ci-in-gitlab/) - **Third-party videos:** From 51332ca02f5696ed1876c5ff1affeda67ba2d25c Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 1 Aug 2017 11:57:23 -0300 Subject: [PATCH 077/108] add article to list also, trigger build --- doc/articles/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/articles/index.md b/doc/articles/index.md index 260940e129b..9d2e5956029 100644 --- a/doc/articles/index.md +++ b/doc/articles/index.md @@ -92,10 +92,11 @@ Explore the best of GitLab's software development's capabilities: | [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 | | [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/)| Concepts | 2017/06/29 | | [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) | Concepts | 2017/05/22 | +| [Demo: Auto-Deploy from GitLab to an OpenShift Container Cluster](https://about.gitlab.com/2017/05/16/devops-containers-gitlab-openshift/) | Technical overview | 2017/05/16 | | [Demo: GitLab Service Desk](https://about.gitlab.com/2017/05/09/demo-service-desk/) | Feature highlight | 2017/05/09 | -| [Demo - Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017/04/25 | -| [Demo – Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017/04/18 | -| [Demo - Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017/03/17 | +| [Demo: Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017/04/25 | +| [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017/04/18 | +| [Demo: Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017/03/17 | | [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) | Technical overview | 2016/11/14 | | [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) | Technical overview | 2016/10/25 | | [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/) | Concepts | 2016/08/16 | From d3ddc69b54a70fbd2332a21d18babd55f240e5a6 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 1 Aug 2017 17:24:37 +0200 Subject: [PATCH 078/108] Add rugged_is_ancestor method --- app/models/repository.rb | 2 +- lib/gitlab/git/repository.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 7ea9f1459a0..4e9fe759fdc 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -944,7 +944,7 @@ class Repository if is_enabled raw_repository.is_ancestor?(ancestor_id, descendant_id) else - merge_base_commit(ancestor_id, descendant_id) == ancestor_id + rugged_is_ancestor?(ancestor_id, descendant_id) end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 88529ba2c47..70d793f8e4a 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -353,6 +353,13 @@ module Gitlab rugged.merge_base(from, to) end + # Gitaly note: JV: check gitlab-ee before removing this method. + def rugged_is_ancestor?(ancestor_id, descendant_id) + return false if ancestor_id.nil? || descendant_id.nil? + + merge_base_commit(ancestor_id, descendant_id) == ancestor_id + end + # Returns true is +from+ is direct ancestor to +to+, otherwise false def is_ancestor?(from, to) gitaly_commit_client.is_ancestor(from, to) From 939e9bdde144849cbc11091985bca0a27f6e75ac Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Aug 2017 12:55:57 -0400 Subject: [PATCH 079/108] Make the 'issuables list meta-data' shared example less dangerous This shared example would take an object's database ID and create a number of objects based on it. If for some reason the ID were a high number, like 20, this would create `20 + 21 + 22` objects. Not only was this dangerous from a performance perspective, it was entirely unnecessary, as the behavior it was testing is already well-tested in the unit test for the underlying object. For a controller test, which is what's including this shared example, all we need to do is verify that the assigned object contains the correct `id => object` Hash, which is what we now test for. --- .../projects/merge_requests_controller_spec.rb | 4 +++- .../issuables_list_metadata_shared_examples.rb | 11 +++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 0bfe83f1d98..216c59d3ae9 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -106,7 +106,7 @@ describe Projects::MergeRequestsController do end describe 'GET index' do - let!(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } def get_merge_requests(page = nil) get :index, @@ -150,6 +150,8 @@ describe Projects::MergeRequestsController do context 'when filtering by opened state' do context 'with opened merge requests' do it 'lists those merge requests' do + expect(merge_request).to be_persisted + get_merge_requests expect(assigns(:merge_requests)).to include(merge_request) diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb index 3406e4c3161..1004c895bb4 100644 --- a/spec/support/issuables_list_metadata_shared_examples.rb +++ b/spec/support/issuables_list_metadata_shared_examples.rb @@ -11,10 +11,6 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| end @issuable_ids << issuable.id - - issuable.id.times { create(:note, noteable: issuable, project: issuable.project) } - (issuable.id + 1).times { create(:award_emoji, :downvote, awardable: issuable) } - (issuable.id + 2).times { create(:award_emoji, :upvote, awardable: issuable) } end end @@ -27,10 +23,9 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| meta_data = assigns(:issuable_meta_data) - @issuable_ids.each do |id| - expect(meta_data[id].notes_count).to eq(id) - expect(meta_data[id].downvotes).to eq(id + 1) - expect(meta_data[id].upvotes).to eq(id + 2) + aggregate_failures do + expect(meta_data.keys).to match_array(@issuable_ids) + expect(meta_data.values).to all(be_kind_of(Issuable::IssuableMeta)) end end From 0135d57b017e4b044511249d534bd26ab77c063d Mon Sep 17 00:00:00 2001 From: Alexander Randa Date: Tue, 1 Aug 2017 17:30:29 +0300 Subject: [PATCH 080/108] Fix encoding error for WebHook logging --- app/services/web_hook_service.rb | 8 +++++++- .../35815-webhook-log-encoding-error.yml | 4 ++++ spec/services/web_hook_service_spec.rb | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/35815-webhook-log-encoding-error.yml diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb index 27c3ba197ac..2825478926a 100644 --- a/app/services/web_hook_service.rb +++ b/app/services/web_hook_service.rb @@ -101,7 +101,7 @@ class WebHookService request_headers: build_headers(hook_name), request_data: request_data, response_headers: format_response_headers(response), - response_body: response.body, + response_body: safe_response_body(response), response_status: response.code, internal_error_message: error_message ) @@ -124,4 +124,10 @@ class WebHookService def format_response_headers(response) response.headers.each_capitalized.to_h end + + def safe_response_body(response) + return '' unless response.body + + response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + end end diff --git a/changelogs/unreleased/35815-webhook-log-encoding-error.yml b/changelogs/unreleased/35815-webhook-log-encoding-error.yml new file mode 100644 index 00000000000..76ec235086c --- /dev/null +++ b/changelogs/unreleased/35815-webhook-log-encoding-error.yml @@ -0,0 +1,4 @@ +--- +title: Fix encoding error for WebHook logging +merge_request: 13230 +author: Alexander Randa (@randaalex) diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index e79c12daa1c..0d710db812c 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -112,6 +112,23 @@ describe WebHookService do end end + context 'with unsafe response body' do + before do + WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: "\xBB") + service_instance.execute + end + + it 'log successful execution' do + expect(hook_log.trigger).to eq('push_hooks') + expect(hook_log.url).to eq(project_hook.url) + expect(hook_log.request_headers).to eq(headers) + expect(hook_log.response_body).to eq('') + expect(hook_log.response_status).to eq('200') + expect(hook_log.execution_duration).to be > 0 + expect(hook_log.internal_error_message).to be_nil + end + end + context 'should not log ServiceHooks' do let(:service_hook) { create(:service_hook) } let(:service_instance) { described_class.new(service_hook, data, 'service_hook') } From 4546ecd67ab2497af51144120667737e773e2dcc Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Tue, 1 Aug 2017 18:06:06 +0000 Subject: [PATCH 081/108] Derive project path from import URL --- .../javascripts/projects/project_new.js | 64 +++++++-- .../unreleased/winh-derive-project-name.yml | 4 + spec/javascripts/projects/project_new_spec.js | 127 ++++++++++++++++++ 3 files changed, 184 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/winh-derive-project-name.yml create mode 100644 spec/javascripts/projects/project_new_spec.js diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 2091b275c3d..1dc1dbf356d 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -1,6 +1,40 @@ -document.addEventListener('DOMContentLoaded', () => { +let hasUserDefinedProjectPath = false; + +const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => { + if ($projectImportUrl.attr('disabled') || hasUserDefinedProjectPath) { + return; + } + + let importUrl = $projectImportUrl.val().trim(); + if (importUrl.length === 0) { + return; + } + + /* + \/?: remove trailing slash + (\.git\/?)?: remove trailing .git (with optional trailing slash) + (\?.*)?: remove query string + (#.*)?: remove fragment identifier + */ + importUrl = importUrl.replace(/\/?(\.git\/?)?(\?.*)?(#.*)?$/, ''); + + // extract everything after the last slash + const pathMatch = /\/([^/]+)$/.exec(importUrl); + if (pathMatch) { + $projectPath.val(pathMatch[1]); + } +}; + +const bindEvents = () => { + const $newProjectForm = $('#new_project'); const importBtnTooltip = 'Please enter a valid project name.'; const $importBtnWrapper = $('.import_gitlab_project'); + const $projectImportUrl = $('#project_import_url'); + const $projectPath = $('#project_path'); + + if ($newProjectForm.length !== 1) { + return; + } $('.how_to_import_link').on('click', (e) => { e.preventDefault(); @@ -13,19 +47,19 @@ document.addEventListener('DOMContentLoaded', () => { $('.btn_import_gitlab_project').on('click', () => { const importHref = $('a.btn_import_gitlab_project').attr('href'); - $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`); + $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`); }); - $('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length); + $('.btn_import_gitlab_project').attr('disabled', !$projectPath.val().trim().length); $importBtnWrapper.attr('title', importBtnTooltip); - $('#new_project').on('submit', () => { - const $path = $('#project_path'); - $path.val($path.val().trim()); + $newProjectForm.on('submit', () => { + $projectPath.val($projectPath.val().trim()); }); - $('#project_path').on('keyup', () => { - if ($('#project_path').val().trim().length) { + $projectPath.on('keyup', () => { + hasUserDefinedProjectPath = $projectPath.val().trim().length > 0; + if (hasUserDefinedProjectPath) { $('.btn_import_gitlab_project').attr('disabled', false); $importBtnWrapper.attr('title', ''); $importBtnWrapper.removeClass('has-tooltip'); @@ -35,9 +69,17 @@ document.addEventListener('DOMContentLoaded', () => { } }); - $('#project_import_url').disable(); + $projectImportUrl.disable(); + $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath)); + $('.import_git').on('click', () => { - const $projectImportUrl = $('#project_import_url'); $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled')); }); -}); +}; + +document.addEventListener('DOMContentLoaded', bindEvents); + +export default { + bindEvents, + deriveProjectPathFromUrl, +}; diff --git a/changelogs/unreleased/winh-derive-project-name.yml b/changelogs/unreleased/winh-derive-project-name.yml new file mode 100644 index 00000000000..2244d21d768 --- /dev/null +++ b/changelogs/unreleased/winh-derive-project-name.yml @@ -0,0 +1,4 @@ +--- +title: Derive project path from import URL +merge_request: 13131 +author: diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js new file mode 100644 index 00000000000..850768f0e4f --- /dev/null +++ b/spec/javascripts/projects/project_new_spec.js @@ -0,0 +1,127 @@ +import projectNew from '~/projects/project_new'; + +describe('New Project', () => { + let $projectImportUrl; + let $projectPath; + + beforeEach(() => { + setFixtures(` + + + `); + + $projectImportUrl = $('#project_import_url'); + $projectPath = $('#project_path'); + }); + + describe('deriveProjectPathFromUrl', () => { + const dummyImportUrl = `${gl.TEST_HOST}/dummy/import/url.git`; + + beforeEach(() => { + projectNew.bindEvents(); + $projectPath.val('').keyup().val(dummyImportUrl); + }); + + it('does not change project path for disabled $projectImportUrl', () => { + $projectImportUrl.attr('disabled', true); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + describe('for enabled $projectImportUrl', () => { + beforeEach(() => { + $projectImportUrl.attr('disabled', false); + }); + + it('does not change project path if it is set by user', () => { + $projectPath.keyup(); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('does not change project path for empty $projectImportUrl', () => { + $projectImportUrl.val(''); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('does not change project path for whitespace $projectImportUrl', () => { + $projectImportUrl.val(' '); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('does not change project path for $projectImportUrl without slashes', () => { + $projectImportUrl.val('has-no-slash'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual(dummyImportUrl); + }); + + it('changes project path to last $projectImportUrl component', () => { + $projectImportUrl.val('/this/is/last'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('last'); + }); + + it('ignores trailing slashes in $projectImportUrl', () => { + $projectImportUrl.val('/has/trailing/slash/'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('slash'); + }); + + it('ignores fragment identifier in $projectImportUrl', () => { + $projectImportUrl.val('/this/has/a#fragment-identifier/'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('a'); + }); + + it('ignores query string in $projectImportUrl', () => { + $projectImportUrl.val('/url/with?query=string'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('with'); + }); + + it('ignores trailing .git in $projectImportUrl', () => { + $projectImportUrl.val('/repository.git'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('repository'); + }); + + it('changes project path for HTTPS URL in $projectImportUrl', () => { + $projectImportUrl.val('https://username:password@gitlab.company.com/group/project.git'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('project'); + }); + + it('changes project path for SSH URL in $projectImportUrl', () => { + $projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git'); + + projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath); + + expect($projectPath.val()).toEqual('gitlab-ce'); + }); + }); + }); +}); From b243c3ea783df5070f889abbefc918f6599a916e Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 1 Aug 2017 20:28:10 +0200 Subject: [PATCH 082/108] Give metric query context rspec examples correctly sounding names. + missing whitespace --- lib/gitlab/prometheus/queries/query_additional_metrics.rb | 1 + spec/support/prometheus/additional_metrics_shared_examples.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb index 5827c117556..d96921a9ee7 100644 --- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb +++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb @@ -51,6 +51,7 @@ module Gitlab query[:query] %= context client_query(query[:query], time: context[:timeframe_end]) end + query[:result] = result&.map(&:deep_symbolize_keys) query end diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index 18b02920104..620fa37d455 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -23,13 +23,13 @@ RSpec.shared_examples 'additional metrics query' do subject! { described_class.new(client) } shared_examples 'query context containing environment slug and filter' do - it 'query context contains ci_environment_slug' do + it 'contains ci_environment_slug' do expect(subject).to receive(:query_metrics).with(hash_including(ci_environment_slug: environment.slug)) subject.query(*query_params) end - it 'query context contains environment filter' do + it 'contains environment filter' do expect(subject).to receive(:query_metrics).with( hash_including( environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\"" From 9513bd18c43b749c8856d6ad89635d97c8fb73b5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Aug 2017 14:51:52 -0400 Subject: [PATCH 083/108] Ensure all project factories use `:repository` trait or `:empty_project` --- .../application_settings_controller_spec.rb | 2 +- .../admin/dashboard_controller_spec.rb | 4 +- .../autocomplete_controller_spec.rb | 16 +++---- spec/controllers/dashboard_controller_spec.rb | 2 +- .../conflicts_controller_spec.rb | 2 +- .../creations_controller_spec.rb | 4 +- .../merge_requests/diffs_controller_spec.rb | 4 +- .../merge_requests_controller_spec.rb | 8 ++-- .../projects/notes_controller_spec.rb | 4 +- .../projects/pipelines_controller_spec.rb | 2 +- .../projects/snippets_controller_spec.rb | 2 +- spec/controllers/projects_controller_spec.rb | 14 +++---- spec/controllers/snippets_controller_spec.rb | 2 +- spec/factories/uploads.rb | 2 +- spec/features/dashboard/projects_spec.rb | 4 +- .../discussion_comments/commit_spec.rb | 2 +- .../discussion_comments/merge_request_spec.rb | 2 +- .../issuables/markdown_references_spec.rb | 2 +- spec/features/projects/blobs/edit_spec.rb | 2 +- spec/features/reportable_note/commit_spec.rb | 2 +- .../reportable_note/merge_request_spec.rb | 2 +- spec/finders/members_finder_spec.rb | 2 +- spec/helpers/button_helper_spec.rb | 2 +- spec/helpers/ci_status_helper_spec.rb | 2 +- spec/helpers/diff_helper_spec.rb | 2 +- spec/helpers/issuables_helper_spec.rb | 4 +- spec/helpers/markup_helper_spec.rb | 2 +- spec/helpers/notes_helper_spec.rb | 2 +- spec/helpers/projects_helper_spec.rb | 6 +-- spec/javascripts/fixtures/balsamiq.rb | 2 +- spec/javascripts/fixtures/dashboard.rb | 2 +- spec/javascripts/fixtures/merge_requests.rb | 2 +- .../fixtures/merge_requests_diffs.rb | 2 +- spec/javascripts/fixtures/pdf.rb | 2 +- spec/javascripts/fixtures/projects.rb | 2 +- spec/javascripts/fixtures/raw.rb | 2 +- .../banzai/filter/reference_filter_spec.rb | 2 +- spec/lib/container_registry/tag_spec.rb | 2 +- spec/lib/gitlab/badge/coverage/report_spec.rb | 2 +- .../cache/ci/project_pipeline_status_spec.rb | 4 +- .../base_event_fetcher_spec.rb | 2 +- .../lib/gitlab/cycle_analytics/events_spec.rb | 2 +- .../v1/rename_base_spec.rb | 6 +-- .../v1/rename_namespaces_spec.rb | 12 +++--- .../v1/rename_projects_spec.rb | 2 +- spec/lib/gitlab/diff/parser_spec.rb | 2 +- .../gitlab/email/attachment_uploader_spec.rb | 2 +- .../handler/create_issue_handler_spec.rb | 2 +- .../lib/gitlab/github_import/importer_spec.rb | 2 +- .../github_import/wiki_formatter_spec.rb | 2 +- spec/lib/gitlab/gl_repository_spec.rb | 2 +- spec/lib/gitlab/import_export/fork_spec.rb | 4 +- .../merge_request_parser_spec.rb | 4 +- .../import_export/repo_restorer_spec.rb | 2 +- spec/lib/gitlab/issuable_sorter_spec.rb | 42 +++++++++---------- spec/lib/gitlab/middleware/go_spec.rb | 4 +- .../lib/gitlab/project_search_results_spec.rb | 6 +-- spec/lib/gitlab/repo_path_spec.rb | 2 +- spec/lib/gitlab/user_access_spec.rb | 2 +- .../rename_system_namespaces_spec.rb | 16 +++---- spec/models/blob_viewer/composer_json_spec.rb | 2 +- spec/models/blob_viewer/gemspec_spec.rb | 2 +- spec/models/blob_viewer/gitlab_ci_yml_spec.rb | 2 +- spec/models/blob_viewer/package_json_spec.rb | 2 +- spec/models/blob_viewer/podspec_json_spec.rb | 2 +- spec/models/blob_viewer/podspec_spec.rb | 2 +- spec/models/blob_viewer/route_map_spec.rb | 2 +- spec/models/concerns/participable_spec.rb | 6 +-- spec/models/concerns/resolvable_note_spec.rb | 2 +- spec/models/concerns/routable_spec.rb | 2 +- spec/models/container_repository_spec.rb | 4 +- spec/models/deployment_spec.rb | 2 +- spec/models/diff_discussion_spec.rb | 2 +- spec/models/environment_spec.rb | 2 +- spec/models/forked_project_link_spec.rb | 2 +- spec/models/gpg_signature_spec.rb | 2 +- spec/models/issue_spec.rb | 2 +- spec/models/project_group_link_spec.rb | 2 +- .../chat_notification_service_spec.rb | 2 +- .../issue_tracker_service_spec.rb | 2 +- .../project_services/jira_service_spec.rb | 2 +- spec/models/project_spec.rb | 16 +++---- spec/models/project_team_spec.rb | 2 +- spec/models/repository_spec.rb | 2 +- spec/models/sent_notification_spec.rb | 4 +- spec/policies/ci/build_policy_spec.rb | 2 +- spec/policies/ci/pipeline_policy_spec.rb | 2 +- spec/policies/environment_policy_spec.rb | 8 ++-- .../merge_request_presenter_spec.rb | 2 +- spec/requests/api/files_spec.rb | 4 +- spec/requests/api/pipeline_schedules_spec.rb | 2 +- spec/requests/api/todos_spec.rb | 2 +- spec/requests/api/triggers_spec.rb | 2 +- spec/requests/api/v3/files_spec.rb | 2 +- spec/requests/api/v3/project_hooks_spec.rb | 4 +- spec/requests/api/v3/triggers_spec.rb | 2 +- spec/requests/lfs_http_spec.rb | 6 +-- spec/requests/request_profiler_spec.rb | 2 +- .../ci/create_pipeline_service_spec.rb | 2 +- spec/services/ci/play_build_service_spec.rb | 2 +- spec/services/labels/create_service_spec.rb | 4 +- spec/services/labels/update_service_spec.rb | 2 +- .../milestones/destroy_service_spec.rb | 2 +- spec/services/users/destroy_service_spec.rb | 4 +- .../migrate_to_ghost_user_service_spec.rb | 2 +- ...issuable_slash_commands_shared_examples.rb | 9 +++- .../import_export/export_file_helper.rb | 2 +- ...e_to_ghost_user_service_shared_examples.rb | 9 +++- spec/uploaders/file_uploader_spec.rb | 2 +- .../validators/dynamic_path_validator_spec.rb | 2 +- .../layouts/nav/_project.html.haml_spec.rb | 2 +- .../pipeline_failed_email.html.haml_spec.rb | 2 +- .../pipeline_success_email.html.haml_spec.rb | 2 +- .../create_gpg_signature_worker_spec.rb | 4 +- spec/workers/process_commit_worker_spec.rb | 2 +- 115 files changed, 214 insertions(+), 200 deletions(-) diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index 2565622f8df..23ab2b141cf 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -4,7 +4,7 @@ describe Admin::ApplicationSettingsController do include StubENV let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } let(:admin) { create(:admin) } let(:user) { create(:user)} diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb index 6eb9f7867d5..b290c45cdd4 100644 --- a/spec/controllers/admin/dashboard_controller_spec.rb +++ b/spec/controllers/admin/dashboard_controller_spec.rb @@ -8,8 +8,8 @@ describe Admin::DashboardController do it 'does not retrieve projects that are pending deletion' do sign_in(create(:admin)) - project = create(:project) - pending_delete_project = create(:project, pending_delete: true) + project = create(:empty_project) + pending_delete_project = create(:empty_project, pending_delete: true) get :index diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 58486f33229..fa5660050ec 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -65,7 +65,7 @@ describe AutocompleteController do end context 'non-member login for public project' do - let!(:project) { create(:project, :public) } + let!(:project) { create(:empty_project, :public) } before do sign_in(non_member) @@ -127,7 +127,7 @@ describe AutocompleteController do end context 'unauthenticated user' do - let(:public_project) { create(:project, :public) } + let(:public_project) { create(:empty_project, :public) } let(:body) { JSON.parse(response.body) } describe 'GET #users with public project' do @@ -231,8 +231,8 @@ describe AutocompleteController do end context 'GET projects' do - let(:authorized_project) { create(:project) } - let(:authorized_search_project) { create(:project, name: 'rugged') } + let(:authorized_project) { create(:empty_project) } + let(:authorized_search_project) { create(:empty_project, name: 'rugged') } before do sign_in(user) @@ -289,8 +289,8 @@ describe AutocompleteController do context 'authorized projects apply limit' do before do - authorized_project2 = create(:project) - authorized_project3 = create(:project) + authorized_project2 = create(:empty_project) + authorized_project3 = create(:empty_project) authorized_project.add_master(user) authorized_project2.add_master(user) @@ -315,8 +315,8 @@ describe AutocompleteController do context 'authorized projects with offset' do before do - authorized_project2 = create(:project) - authorized_project3 = create(:project) + authorized_project2 = create(:empty_project) + authorized_project3 = create(:empty_project) authorized_project.add_master(user) authorized_project2.add_master(user) diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 566d8515198..05561946e9b 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe DashboardController do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } before do project.team << [user, :master] diff --git a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb index 9278ac8edd8..393d38c6e6b 100644 --- a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::MergeRequests::ConflictsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { project.owner } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } let(:merge_request_with_conflicts) do diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb index f9d8f0f5fcf..fc4cec53374 100644 --- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::MergeRequests::CreationsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { project.owner } let(:fork_project) { create(:forked_project_with_submodules) } @@ -83,7 +83,7 @@ describe Projects::MergeRequests::CreationsController do end context 'when the source branch is in a different project to the target' do - let(:other_project) { create(:project) } + let(:other_project) { create(:project, :repository) } before do other_project.team << [user, :master] diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb index 53fe2bdb189..fd6cb3c5dbb 100644 --- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::MergeRequests::DiffsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { project.owner } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } @@ -36,7 +36,7 @@ describe Projects::MergeRequests::DiffsController do context 'with forked projects with submodules' do render_views - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:fork_project) { create(:forked_project_with_submodules) } let(:merge_request) { create(:merge_request_with_diffs, source_project: fork_project, source_branch: 'add-submodule-version-bump', target_branch: 'master', target_project: project) } diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 0bfe83f1d98..bed2d4f8c98 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::MergeRequestsController do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { project.owner } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } let(:merge_request_with_conflicts) do @@ -191,7 +191,7 @@ describe Projects::MergeRequestsController do end context 'there is no source project' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:fork_project) { create(:forked_project_with_submodules) } let(:merge_request) { create(:merge_request, source_project: fork_project, source_branch: 'add-submodule-version-bump', target_branch: 'master', target_project: project) } @@ -429,7 +429,7 @@ describe Projects::MergeRequestsController do context "when the user is owner" do let(:owner) { create(:user) } let(:namespace) { create(:namespace, owner: owner) } - let(:project) { create(:project, namespace: namespace) } + let(:project) { create(:project, :repository, namespace: namespace) } before do sign_in owner @@ -587,7 +587,7 @@ describe Projects::MergeRequestsController do describe 'GET ci_environments_status' do context 'the environment is from a forked project' do - let!(:forked) { create(:project) } + let!(:forked) { create(:project, :repository) } let!(:environment) { create(:environment, project: forked) } let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } let(:admin) { create(:admin) } diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 3b88d5b0d7d..4a6eb76e71f 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -167,10 +167,10 @@ describe Projects::NotesController do end context 'when creating a commit comment from an MR fork' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:fork_project) do - create(:project).tap do |fork| + create(:project, :repository).tap do |fork| create(:forked_project_link, forked_to_project: fork, forked_from_project: project) end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index c8de275ca3e..ed63e4dec26 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -61,7 +61,7 @@ describe Projects::PipelinesController do create_build('post deploy', 3, 'pages 0') end - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) do create(:ci_empty_pipeline, project: project, user: user, sha: project.commit.id) end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index cc444f31797..1f082ce7102 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -156,7 +156,7 @@ describe Projects::SnippetsController do end describe 'PUT #update' do - let(:project) { create :project, :public } + let(:project) { create :empty_project, :public } let(:snippet) { create :project_snippet, author: user, project: project, visibility_level: visibility_level } def update_snippet(snippet_params = {}, additional_params = {}) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 192cca45d99..9f19cc30582 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -196,7 +196,7 @@ describe ProjectsController do context "redirection from http://someproject.git" do it 'redirects to project page (format.html)' do - project = create(:project, :public) + project = create(:empty_project, :public) get :show, namespace_id: project.namespace, id: project, format: :git @@ -254,7 +254,7 @@ describe ProjectsController do describe '#transfer' do render_views - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:admin) { create(:admin) } let(:new_namespace) { create(:namespace) } @@ -311,8 +311,8 @@ describe ProjectsController do end context "when the project is forked" do - let(:project) { create(:project) } - let(:fork_project) { create(:project, forked_from_project: project) } + let(:project) { create(:project, :repository) } + let(:fork_project) { create(:project, :repository, forked_from_project: project) } let(:merge_request) do create(:merge_request, source_project: fork_project, @@ -390,7 +390,7 @@ describe ProjectsController do end context 'with forked project' do - let(:project_fork) { create(:project, namespace: user.namespace) } + let(:project_fork) { create(:project, :repository, namespace: user.namespace) } before do create(:forked_project_link, forked_to_project: project_fork) @@ -408,7 +408,7 @@ describe ProjectsController do end context 'when project not forked' do - let(:unforked_project) { create(:project, namespace: user.namespace) } + let(:unforked_project) { create(:empty_project, namespace: user.namespace) } it 'does nothing if project was not forked' do delete(:remove_fork, @@ -430,7 +430,7 @@ describe ProjectsController do end describe "GET refs" do - let(:public_project) { create(:project, :public) } + let(:public_project) { create(:project, :public, :repository) } it "gets a list of branches and tags" do get :refs, namespace_id: public_project.namespace, id: public_project diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 475ceda11fe..a6edcca08f4 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -273,7 +273,7 @@ describe SnippetsController do end describe 'PUT #update' do - let(:project) { create :project } + let(:project) { create :empty_project } let(:snippet) { create :personal_snippet, author: user, project: project, visibility_level: visibility_level } def update_snippet(snippet_params = {}, additional_params = {}) diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb index 3222c41c3d8..f49dbe6dc9e 100644 --- a/spec/factories/uploads.rb +++ b/spec/factories/uploads.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :upload do - model { build(:project) } + model { build(:empty_project) } path { "uploads/-/system/project/avatar/avatar.jpg" } size 100.kilobytes uploader "AvatarUploader" diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index abb9e5eef96..29ac96adc0e 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' feature 'Dashboard Projects' do let(:user) { create(:user) } - let(:project) { create(:project, name: 'awesome stuff') } - let(:project2) { create(:project, :public, name: 'Community project') } + let(:project) { create(:project, :repository, name: 'awesome stuff') } + let(:project2) { create(:empty_project, :public, name: 'Community project') } before do project.team << [user, :developer] diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb index fa83ad5d17c..0375d0bf8ff 100644 --- a/spec/features/discussion_comments/commit_spec.rb +++ b/spec/features/discussion_comments/commit_spec.rb @@ -4,7 +4,7 @@ describe 'Discussion Comments Merge Request', :js do include RepoHelpers let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } before do diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb index 042f39f47e0..b0019c32189 100644 --- a/spec/features/discussion_comments/merge_request_spec.rb +++ b/spec/features/discussion_comments/merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe 'Discussion Comments Merge Request', :js do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } before do diff --git a/spec/features/issuables/markdown_references_spec.rb b/spec/features/issuables/markdown_references_spec.rb index 169381d703a..04d38165a15 100644 --- a/spec/features/issuables/markdown_references_spec.rb +++ b/spec/features/issuables/markdown_references_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' describe 'Markdown References', :js do let(:user) { create(:user) } - let(:actual_project) { create(:project, :public) } + let(:actual_project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, target_project: actual_project, source_project: actual_project)} let(:issue_actual_project) { create(:issue, project: actual_project) } let!(:other_project) { create(:empty_project, :public) } diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb index 9855cfd85c4..62ac9fd0e95 100644 --- a/spec/features/projects/blobs/edit_spec.rb +++ b/spec/features/projects/blobs/edit_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Editing file blob', js: true do include TreeHelper - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') } let(:branch) { 'master' } let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] } diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb index 1074eb62b33..3bf25221e36 100644 --- a/spec/features/reportable_note/commit_spec.rb +++ b/spec/features/reportable_note/commit_spec.rb @@ -4,7 +4,7 @@ describe 'Reportable note on commit', :js do include RepoHelpers let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do project.add_master(user) diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb index a491abdb6cb..bb296546e06 100644 --- a/spec/features/reportable_note/merge_request_spec.rb +++ b/spec/features/reportable_note/merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe 'Reportable note on merge request', :js do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } before do diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb index 300ba8422e8..9fb9ffa95e7 100644 --- a/spec/finders/members_finder_spec.rb +++ b/spec/finders/members_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe MembersFinder, '#execute' do let(:group) { create(:group) } let(:nested_group) { create(:group, :access_requestable, parent: group) } - let(:project) { create(:project, namespace: nested_group) } + let(:project) { create(:empty_project, namespace: nested_group) } let(:user1) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb index 7ecb75da8ce..7c6f3b101d7 100644 --- a/spec/helpers/button_helper_spec.rb +++ b/spec/helpers/button_helper_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ButtonHelper do describe 'http_clone_button' do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { build_stubbed(:empty_project) } let(:has_tooltip_class) { 'has-tooltip' } def element diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb index e6bb953e9d8..3f8df388014 100644 --- a/spec/helpers/ci_status_helper_spec.rb +++ b/spec/helpers/ci_status_helper_spec.rb @@ -48,7 +48,7 @@ describe CiStatusHelper do describe "#pipeline_status_cache_key" do it "builds a cache key for pipeline status" do pipeline_status = Gitlab::Cache::Ci::ProjectPipelineStatus.new( - build(:project), + build_stubbed(:empty_project), pipeline_info: { sha: "123abc", status: "success" diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index 060c112e20d..f81a9b6492c 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe DiffHelper do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } let(:diffs) { commit.raw_diffs } diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 7789cfa3554..1aad8a85ab7 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -180,7 +180,7 @@ describe IssuablesHelper do context 'when show_full_reference falsey' do context 'when @group present' do it 'display issuable reference to @group' do - project = build_stubbed(:project) + project = build_stubbed(:empty_project) assign(:show_full_reference, nil) assign(:group, project.namespace) @@ -193,7 +193,7 @@ describe IssuablesHelper do context 'when @project present' do it 'display issuable reference to @project' do - project = build_stubbed(:project) + project = build_stubbed(:empty_project) assign(:show_full_reference, nil) assign(:group, nil) diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 70eb01c9c44..6dfffb7ac63 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -42,7 +42,7 @@ describe MarkupHelper do describe "override default project" do let(:actual) { issue.to_reference } - let(:second_project) { create(:project, :public) } + let(:second_project) { create(:empty_project, :public) } let(:second_issue) { create(:issue, project: second_project) } it 'links to the issue' do diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb index 56f252ba273..da1044cef3f 100644 --- a/spec/helpers/notes_helper_spec.rb +++ b/spec/helpers/notes_helper_spec.rb @@ -40,7 +40,7 @@ describe NotesHelper do end describe '#discussion_path' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } context 'for a merge request discusion' do let(:merge_request) { create(:merge_request, source_project: project, target_project: project, importing: true) } diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index faf26931efc..c447c061919 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -46,7 +46,7 @@ describe ProjectsHelper do end describe "readme_cache_key" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do helper.instance_variable_set(:@project, project) @@ -64,7 +64,7 @@ describe ProjectsHelper do end describe "#project_list_cache_key", clean_gitlab_redis_shared_state: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } it "includes the route" do expect(helper.project_list_cache_key(project)).to include(project.route.cache_key) @@ -251,7 +251,7 @@ describe ProjectsHelper do end describe '#sanitized_import_error' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do allow(project).to receive(:repository_storage_path).and_return('/base/repo/path') diff --git a/spec/javascripts/fixtures/balsamiq.rb b/spec/javascripts/fixtures/balsamiq.rb index b5372821bf5..234e246119a 100644 --- a/spec/javascripts/fixtures/balsamiq.rb +++ b/spec/javascripts/fixtures/balsamiq.rb @@ -4,7 +4,7 @@ describe 'Balsamiq file', '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'balsamiq-project') } + let(:project) { create(:project, :repository, namespace: namespace, path: 'balsamiq-project') } before(:all) do clean_frontend_fixtures('blob/balsamiq/') diff --git a/spec/javascripts/fixtures/dashboard.rb b/spec/javascripts/fixtures/dashboard.rb index e83db8daaf2..b2114e65d27 100644 --- a/spec/javascripts/fixtures/dashboard.rb +++ b/spec/javascripts/fixtures/dashboard.rb @@ -5,7 +5,7 @@ describe Dashboard::ProjectsController, '(JavaScript fixtures)', type: :controll let(:admin) { create(:admin) } let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'builds-project') } + let(:project) { create(:empty_project, namespace: namespace, path: 'builds-project') } render_views diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb index 7e2f364ffa4..f9d8b5c569c 100644 --- a/spec/javascripts/fixtures/merge_requests.rb +++ b/spec/javascripts/fixtures/merge_requests.rb @@ -5,7 +5,7 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont let(:admin) { create(:admin) } let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') } + let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') } let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') } let(:merged_merge_request) { create(:merge_request, :merged, source_project: project, target_project: project) } let(:pipeline) do diff --git a/spec/javascripts/fixtures/merge_requests_diffs.rb b/spec/javascripts/fixtures/merge_requests_diffs.rb index ac5b06ace6d..4481a187f63 100644 --- a/spec/javascripts/fixtures/merge_requests_diffs.rb +++ b/spec/javascripts/fixtures/merge_requests_diffs.rb @@ -6,7 +6,7 @@ describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type let(:admin) { create(:admin) } let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') } + let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') } let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') } let(:path) { "files/ruby/popen.rb" } let(:position) do diff --git a/spec/javascripts/fixtures/pdf.rb b/spec/javascripts/fixtures/pdf.rb index 6b2422a7986..ef9976b9fd3 100644 --- a/spec/javascripts/fixtures/pdf.rb +++ b/spec/javascripts/fixtures/pdf.rb @@ -4,7 +4,7 @@ describe 'PDF file', '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'pdf-project') } + let(:project) { create(:project, :repository, namespace: namespace, path: 'pdf-project') } before(:all) do clean_frontend_fixtures('blob/pdf/') diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb index 6c33b240e5c..b828eee6629 100644 --- a/spec/javascripts/fixtures/projects.rb +++ b/spec/javascripts/fixtures/projects.rb @@ -5,7 +5,7 @@ describe ProjectsController, '(JavaScript fixtures)', type: :controller do let(:admin) { create(:admin) } let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'builds-project') } + let(:project) { create(:empty_project, namespace: namespace, path: 'builds-project') } render_views diff --git a/spec/javascripts/fixtures/raw.rb b/spec/javascripts/fixtures/raw.rb index 17533443d76..25f5a3b0bb3 100644 --- a/spec/javascripts/fixtures/raw.rb +++ b/spec/javascripts/fixtures/raw.rb @@ -4,7 +4,7 @@ describe 'Raw files', '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, namespace: namespace, path: 'raw-project') } + let(:project) { create(:project, :repository, namespace: namespace, path: 'raw-project') } before(:all) do clean_frontend_fixtures('blob/notebook/') diff --git a/spec/lib/banzai/filter/reference_filter_spec.rb b/spec/lib/banzai/filter/reference_filter_spec.rb index b9ca68e8935..81f244a4539 100644 --- a/spec/lib/banzai/filter/reference_filter_spec.rb +++ b/spec/lib/banzai/filter/reference_filter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Banzai::Filter::ReferenceFilter do - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } describe '#each_node' do it 'iterates over the nodes in a document' do diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb index cb4ae3be525..e76463b5e7c 100644 --- a/spec/lib/container_registry/tag_spec.rb +++ b/spec/lib/container_registry/tag_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ContainerRegistry::Tag do let(:group) { create(:group, name: 'group') } - let(:project) { create(:project, path: 'test', group: group) } + let(:project) { create(:project, :repository, path: 'test', group: group) } let(:repository) do create(:container_repository, name: '', project: project) diff --git a/spec/lib/gitlab/badge/coverage/report_spec.rb b/spec/lib/gitlab/badge/coverage/report_spec.rb index 1547bd3228c..da789bf3705 100644 --- a/spec/lib/gitlab/badge/coverage/report_spec.rb +++ b/spec/lib/gitlab/badge/coverage/report_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Badge::Coverage::Report do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:job_name) { nil } let(:badge) do diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb index 0daf41a7c86..1fab3b29424 100644 --- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb +++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let(:pipeline_status) { described_class.new(project) } let(:cache_key) { "projects/#{project.id}/pipeline_status" } @@ -18,7 +18,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do let(:sha) { '424d1b73bc0d3cb726eb7dc4ce17a4d48552f8c6' } let(:ref) { 'master' } let(:pipeline_info) { { sha: sha, status: status, ref: ref } } - let!(:project_without_status) { create(:project) } + let!(:project_without_status) { create(:project, :repository) } describe '.load_in_batch_for_projects' do it 'preloads pipeline_status on projects' do diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb index d8757c601ab..854aaa34c73 100644 --- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::CycleAnalytics::BaseEventFetcher do let(:max_events) { 2 } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user, :admin) } let(:start_time_attrs) { Issue.arel_table[:created_at] } let(:end_time_attrs) { [Issue::Metrics.arel_table[:first_associated_with_milestone_at]] } diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index a1b3fe8509e..28ea7d4c303 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'cycle analytics events' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb index df7d1b5d27a..254ce06381e 100644 --- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb @@ -94,7 +94,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca end it "renames the route for projects of the namespace" do - project = create(:project, path: "project-path", namespace: namespace) + project = create(:project, :repository, path: "project-path", namespace: namespace) subject.rename_path_for_routable(migration_namespace(namespace)) @@ -120,7 +120,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca context "the-path namespace -> subgroup -> the-path0 project" do it "updates the route of the project correctly" do subgroup = create(:group, path: "subgroup", parent: namespace) - project = create(:project, path: "the-path0", namespace: subgroup) + project = create(:project, :repository, path: "the-path0", namespace: subgroup) subject.rename_path_for_routable(migration_namespace(namespace)) @@ -165,7 +165,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca it 'renames all the routes for the namespace' do child = create(:group, path: 'child', parent: namespace) - project = create(:project, namespace: child, path: 'the-project') + project = create(:project, :repository, namespace: child, path: 'the-project') other_one = create(:namespace, path: 'the-path-is-similar') subject.perform_rename(migration_namespace(namespace), 'the-path', 'renamed') diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb index 803e923b4a5..95695ca8c16 100644 --- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb @@ -94,7 +94,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, : describe '#move_repositories' do let(:namespace) { create(:group, name: 'hello-group') } it 'moves a project for a namespace' do - create(:project, namespace: namespace, path: 'hello-project') + create(:project, :repository, namespace: namespace, path: 'hello-project') expected_path = File.join(TestEnv.repos_path, 'bye-group', 'hello-project.git') subject.move_repositories(namespace, 'hello-group', 'bye-group') @@ -104,7 +104,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, : it 'moves a namespace in a subdirectory correctly' do child_namespace = create(:group, name: 'sub-group', parent: namespace) - create(:project, namespace: child_namespace, path: 'hello-project') + create(:project, :repository, namespace: child_namespace, path: 'hello-project') expected_path = File.join(TestEnv.repos_path, 'hello-group', 'renamed-sub-group', 'hello-project.git') @@ -115,7 +115,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, : it 'moves a parent namespace with subdirectories' do child_namespace = create(:group, name: 'sub-group', parent: namespace) - create(:project, namespace: child_namespace, path: 'hello-project') + create(:project, :repository, namespace: child_namespace, path: 'hello-project') expected_path = File.join(TestEnv.repos_path, 'renamed-group', 'sub-group', 'hello-project.git') subject.move_repositories(child_namespace, 'hello-group', 'renamed-group') @@ -166,7 +166,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, : describe '#rename_namespace_dependencies' do it "moves the the repository for a project in the namespace" do - create(:project, namespace: namespace, path: "the-path-project") + create(:project, :repository, namespace: namespace, path: "the-path-project") expected_repo = File.join(TestEnv.repos_path, "the-path0", "the-path-project.git") subject.rename_namespace_dependencies(namespace, 'the-path', 'the-path0') @@ -243,7 +243,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, : describe '#revert_renames', redis: true do it 'renames the routes back to the previous values' do - project = create(:project, path: 'a-project', namespace: namespace) + project = create(:project, :repository, path: 'a-project', namespace: namespace) subject.rename_namespace(namespace) expect(subject).to receive(:perform_rename) @@ -261,7 +261,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, : end it 'moves the repositories back to their original place' do - project = create(:project, path: 'a-project', namespace: namespace) + project = create(:project, :repository, path: 'a-project', namespace: namespace) project.create_repository subject.rename_namespace(namespace) diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb index 0e240a5ccf1..19e23a32bda 100644 --- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb @@ -104,7 +104,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :tr describe '#move_repository' do let(:known_parent) { create(:namespace, path: 'known-parent') } - let(:project) { create(:project, path: 'the-path', namespace: known_parent) } + let(:project) { create(:project, :repository, path: 'the-path', namespace: known_parent) } it 'moves the repository for a project' do expected_path = File.join(TestEnv.repos_path, 'known-parent', 'new-repo.git') diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb index c71568e2a65..8af49ed50ff 100644 --- a/spec/lib/gitlab/diff/parser_spec.rb +++ b/spec/lib/gitlab/diff/parser_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Diff::Parser do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { project.commit(sample_commit.id) } let(:diff) { commit.raw_diffs.first } let(:parser) { described_class.new } diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb index f61dbc67ad1..89258d2eca7 100644 --- a/spec/lib/gitlab/email/attachment_uploader_spec.rb +++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" describe Gitlab::Email::AttachmentUploader do describe "#execute" do - let(:project) { build(:project) } + let(:project) { build(:empty_project) } let(:message_raw) { fixture_file("emails/attachment.eml") } let(:message) { Mail::Message.new(message_raw) } diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb index bd36d1d309d..6d0e715e889 100644 --- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb @@ -69,7 +69,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler do end context "when project is private" do - let(:project) { create(:project, :private, namespace: namespace) } + let(:project) { create(:empty_project, :private, namespace: namespace) } it "raises a ProjectNotFound if the user is not a member" do expect { receiver.execute }.to raise_error(Gitlab::Email::ProjectNotFound) diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index d00a2deaf7b..d570f34985b 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -207,7 +207,7 @@ describe Gitlab::GithubImport::Importer do end end - let(:project) { create(:project, :wiki_disabled, import_url: "#{repo_root}/octocat/Hello-World.git") } + let(:project) { create(:project, :repository, :wiki_disabled, import_url: "#{repo_root}/octocat/Hello-World.git") } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') } let(:credentials) { { user: 'joe' } } diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index fcd90fab547..119bf2b5662 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::GithubImport::WikiFormatter do let(:project) do - create(:project, + create(:empty_project, namespace: create(:namespace, path: 'gitlabhq'), import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git') end diff --git a/spec/lib/gitlab/gl_repository_spec.rb b/spec/lib/gitlab/gl_repository_spec.rb index ac3558ab386..4e09020471b 100644 --- a/spec/lib/gitlab/gl_repository_spec.rb +++ b/spec/lib/gitlab/gl_repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ::Gitlab::GlRepository do describe '.parse' do - set(:project) { create(:project) } + set(:project) { create(:project, :repository) } it 'parses a project gl_repository' do expect(described_class.parse("project-#{project.id}")).to eq([project, false]) diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb index 0ff64cbe880..230cc2d3006 100644 --- a/spec/lib/gitlab/import_export/fork_spec.rb +++ b/spec/lib/gitlab/import_export/fork_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' describe 'forked project import' do let(:user) { create(:user) } - let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let!(:project_with_repo) { create(:project, :repository, name: 'test-repo-restorer', path: 'test-repo-restorer') } let!(:project) { create(:empty_project, name: 'test-repo-restorer-no-repo', path: 'test-repo-restorer-no-repo') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } - let(:forked_from_project) { create(:project) } + let(:forked_from_project) { create(:project, :repository) } let(:fork_link) { create(:forked_project_link, forked_from_project: project_with_repo) } let(:repo_saver) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) } let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) } diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb index f2b66c4421c..4d87f27ce05 100644 --- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb +++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe Gitlab::ImportExport::MergeRequestParser do let(:user) { create(:user) } - let!(:project) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } - let(:forked_from_project) { create(:project) } + let!(:project) { create(:project, :repository, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let(:forked_from_project) { create(:project, :repository) } let(:fork_link) { create(:forked_project_link, forked_from_project: project) } let!(:merge_request) do diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index 82935af2670..d57833c3fec 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::RepoRestorer do describe 'bundle a project Git repo' do let(:user) { create(:user) } - let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let!(:project_with_repo) { create(:project, :repository, name: 'test-repo-restorer', path: 'test-repo-restorer') } let!(:project) { create(:empty_project) } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } diff --git a/spec/lib/gitlab/issuable_sorter_spec.rb b/spec/lib/gitlab/issuable_sorter_spec.rb index aeb32ef96d6..c4138c063c8 100644 --- a/spec/lib/gitlab/issuable_sorter_spec.rb +++ b/spec/lib/gitlab/issuable_sorter_spec.rb @@ -1,25 +1,25 @@ require 'spec_helper' describe Gitlab::IssuableSorter do - let(:namespace1) { build(:namespace, id: 1) } - let(:project1) { build(:project, id: 1, namespace: namespace1) } + let(:namespace1) { build_stubbed(:namespace, id: 1) } + let(:project1) { build_stubbed(:empty_project, id: 1, namespace: namespace1) } - let(:project2) { build(:project, id: 2, path: "a", namespace: project1.namespace) } - let(:project3) { build(:project, id: 3, path: "b", namespace: project1.namespace) } + let(:project2) { build_stubbed(:empty_project, id: 2, path: "a", namespace: project1.namespace) } + let(:project3) { build_stubbed(:empty_project, id: 3, path: "b", namespace: project1.namespace) } - let(:namespace2) { build(:namespace, id: 2, path: "a") } - let(:namespace3) { build(:namespace, id: 3, path: "b") } - let(:project4) { build(:project, id: 4, path: "a", namespace: namespace2) } - let(:project5) { build(:project, id: 5, path: "b", namespace: namespace2) } - let(:project6) { build(:project, id: 6, path: "a", namespace: namespace3) } + let(:namespace2) { build_stubbed(:namespace, id: 2, path: "a") } + let(:namespace3) { build_stubbed(:namespace, id: 3, path: "b") } + let(:project4) { build_stubbed(:empty_project, id: 4, path: "a", namespace: namespace2) } + let(:project5) { build_stubbed(:empty_project, id: 5, path: "b", namespace: namespace2) } + let(:project6) { build_stubbed(:empty_project, id: 6, path: "a", namespace: namespace3) } let(:unsorted) { [sorted[2], sorted[3], sorted[0], sorted[1]] } let(:sorted) do - [build(:issue, iid: 1, project: project1), - build(:issue, iid: 2, project: project1), - build(:issue, iid: 10, project: project1), - build(:issue, iid: 20, project: project1)] + [build_stubbed(:issue, iid: 1, project: project1), + build_stubbed(:issue, iid: 2, project: project1), + build_stubbed(:issue, iid: 10, project: project1), + build_stubbed(:issue, iid: 20, project: project1)] end it 'sorts references by a given key' do @@ -41,14 +41,14 @@ describe Gitlab::IssuableSorter do context 'for references from multiple projects and namespaces' do let(:sorted) do - [build(:issue, iid: 1, project: project1), - build(:issue, iid: 2, project: project1), - build(:issue, iid: 10, project: project1), - build(:issue, iid: 1, project: project2), - build(:issue, iid: 1, project: project3), - build(:issue, iid: 1, project: project4), - build(:issue, iid: 1, project: project5), - build(:issue, iid: 1, project: project6)] + [build_stubbed(:issue, iid: 1, project: project1), + build_stubbed(:issue, iid: 2, project: project1), + build_stubbed(:issue, iid: 10, project: project1), + build_stubbed(:issue, iid: 1, project: project2), + build_stubbed(:issue, iid: 1, project: project3), + build_stubbed(:issue, iid: 1, project: project4), + build_stubbed(:issue, iid: 1, project: project5), + build_stubbed(:issue, iid: 1, project: project6)] end let(:unsorted) do [sorted[3], sorted[1], sorted[4], sorted[2], diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb index 6af1564da19..de4c8b68ffa 100644 --- a/spec/lib/gitlab/middleware/go_spec.rb +++ b/spec/lib/gitlab/middleware/go_spec.rb @@ -18,7 +18,7 @@ describe Gitlab::Middleware::Go do let(:current_user) { nil } context 'with simple 2-segment project path' do - let!(:project) { create(:project, :private) } + let!(:project) { create(:empty_project, :private) } context 'with subpackages' do let(:path) { "#{project.full_path}/subpackage" } @@ -39,7 +39,7 @@ describe Gitlab::Middleware::Go do context 'with a nested project path' do let(:group) { create(:group, :nested) } - let!(:project) { create(:project, :public, namespace: group) } + let!(:project) { create(:empty_project, :public, namespace: group) } shared_examples 'a nested project' do context 'when the project is public' do diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index d17b436b910..7851beb956b 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -100,14 +100,14 @@ describe Gitlab::ProjectSearchResults do end describe 'wiki search' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:wiki) { build(:project_wiki, project: project) } let!(:wiki_page) { wiki.create_page('Title', 'Content') } subject(:results) { described_class.new(user, project, 'Content').objects('wiki_blobs') } context 'when wiki is disabled' do - let(:project) { create(:project, :public, :wiki_disabled) } + let(:project) { create(:empty_project, :public, :wiki_disabled) } it 'hides wiki blobs from members' do project.add_reporter(user) @@ -121,7 +121,7 @@ describe Gitlab::ProjectSearchResults do end context 'when wiki is internal' do - let(:project) { create(:project, :public, :wiki_private) } + let(:project) { create(:empty_project, :public, :wiki_private) } it 'finds wiki blobs for guest' do project.add_guest(user) diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb index efea4f429bf..6336f4a7125 100644 --- a/spec/lib/gitlab/repo_path_spec.rb +++ b/spec/lib/gitlab/repo_path_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ::Gitlab::RepoPath do describe '.parse' do - set(:project) { create(:project) } + set(:project) { create(:project, :repository) } context 'a repository storage path' do it 'parses a full repository path' do diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 5ebaf6c1507..cd97416bcc9 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::UserAccess do let(:access) { described_class.new(user, project: project) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } describe '#can_push_to_branch?' do diff --git a/spec/migrations/rename_system_namespaces_spec.rb b/spec/migrations/rename_system_namespaces_spec.rb index 626a6005838..747694cbe33 100644 --- a/spec/migrations/rename_system_namespaces_spec.rb +++ b/spec/migrations/rename_system_namespaces_spec.rb @@ -58,7 +58,7 @@ describe RenameSystemNamespaces, truncate: true do end it "renames the route for projects of the namespace" do - project = build(:project, path: "project-path", namespace: system_namespace) + project = build(:project, :repository, path: "project-path", namespace: system_namespace) save_invalid_routable(project) migration.up @@ -68,7 +68,7 @@ describe RenameSystemNamespaces, truncate: true do it "doesn't touch routes of namespaces that look like system" do namespace = create(:group, path: 'systemlookalike') - project = create(:project, namespace: namespace, path: 'the-project') + project = create(:project, :repository, namespace: namespace, path: 'the-project') migration.up @@ -77,7 +77,7 @@ describe RenameSystemNamespaces, truncate: true do end it "moves the the repository for a project in the namespace" do - project = build(:project, namespace: system_namespace, path: "system-project") + project = build(:project, :repository, namespace: system_namespace, path: "system-project") save_invalid_routable(project) TestEnv.copy_repo(project, bare_repo: TestEnv.factory_repo_path_bare, @@ -105,7 +105,7 @@ describe RenameSystemNamespaces, truncate: true do describe "clears the markdown cache for projects in the system namespace" do let!(:project) do - project = build(:project, namespace: system_namespace) + project = build(:project, :repository, namespace: system_namespace) save_invalid_routable(project) project end @@ -161,7 +161,7 @@ describe RenameSystemNamespaces, truncate: true do it "updates the route of the project correctly" do subgroup = build(:group, path: "subgroup", parent: system_namespace) save_invalid_routable(subgroup) - project = build(:project, path: "system0", namespace: subgroup) + project = build(:project, :repository, path: "system0", namespace: subgroup) save_invalid_routable(project) migration.up @@ -174,7 +174,7 @@ describe RenameSystemNamespaces, truncate: true do describe "#move_repositories" do let(:namespace) { create(:group, name: "hello-group") } it "moves a project for a namespace" do - create(:project, namespace: namespace, path: "hello-project") + create(:project, :repository, namespace: namespace, path: "hello-project") expected_path = File.join(TestEnv.repos_path, "bye-group", "hello-project.git") migration.move_repositories(namespace, "hello-group", "bye-group") @@ -184,7 +184,7 @@ describe RenameSystemNamespaces, truncate: true do it "moves a namespace in a subdirectory correctly" do child_namespace = create(:group, name: "sub-group", parent: namespace) - create(:project, namespace: child_namespace, path: "hello-project") + create(:project, :repository, namespace: child_namespace, path: "hello-project") expected_path = File.join(TestEnv.repos_path, "hello-group", "renamed-sub-group", "hello-project.git") @@ -195,7 +195,7 @@ describe RenameSystemNamespaces, truncate: true do it "moves a parent namespace with subdirectories" do child_namespace = create(:group, name: "sub-group", parent: namespace) - create(:project, namespace: child_namespace, path: "hello-project") + create(:project, :repository, namespace: child_namespace, path: "hello-project") expected_path = File.join(TestEnv.repos_path, "renamed-group", "sub-group", "hello-project.git") migration.move_repositories(child_namespace, "hello-group", "renamed-group") diff --git a/spec/models/blob_viewer/composer_json_spec.rb b/spec/models/blob_viewer/composer_json_spec.rb index 82f6f7e5046..35e7e1805d4 100644 --- a/spec/models/blob_viewer/composer_json_spec.rb +++ b/spec/models/blob_viewer/composer_json_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::ComposerJson do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) do <<-SPEC.strip_heredoc { diff --git a/spec/models/blob_viewer/gemspec_spec.rb b/spec/models/blob_viewer/gemspec_spec.rb index 14cc5f3c0fd..5406ff552f8 100644 --- a/spec/models/blob_viewer/gemspec_spec.rb +++ b/spec/models/blob_viewer/gemspec_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::Gemspec do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) do <<-SPEC.strip_heredoc Gem::Specification.new do |s| diff --git a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb index 7a4f9866375..344fcdeb9b6 100644 --- a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb +++ b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::GitlabCiYml do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) } let(:blob) { fake_blob(path: '.gitlab-ci.yml', data: data) } subject { described_class.new(blob) } diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb index 96fb1b08c99..976c2ed3639 100644 --- a/spec/models/blob_viewer/package_json_spec.rb +++ b/spec/models/blob_viewer/package_json_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::PackageJson do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) do <<-SPEC.strip_heredoc { diff --git a/spec/models/blob_viewer/podspec_json_spec.rb b/spec/models/blob_viewer/podspec_json_spec.rb index f510077a87b..b98360ea7b7 100644 --- a/spec/models/blob_viewer/podspec_json_spec.rb +++ b/spec/models/blob_viewer/podspec_json_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::PodspecJson do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) do <<-SPEC.strip_heredoc { diff --git a/spec/models/blob_viewer/podspec_spec.rb b/spec/models/blob_viewer/podspec_spec.rb index 7c38083550c..ad2bbe034ea 100644 --- a/spec/models/blob_viewer/podspec_spec.rb +++ b/spec/models/blob_viewer/podspec_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::Podspec do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) do <<-SPEC.strip_heredoc Pod::Spec.new do |spec| diff --git a/spec/models/blob_viewer/route_map_spec.rb b/spec/models/blob_viewer/route_map_spec.rb index 115731b4970..12b6519a344 100644 --- a/spec/models/blob_viewer/route_map_spec.rb +++ b/spec/models/blob_viewer/route_map_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BlobViewer::RouteMap do include FakeBlobHelpers - let(:project) { build(:project) } + let(:project) { build_stubbed(:empty_project) } let(:data) do <<-MAP.strip_heredoc # Team data diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb index 431f1482615..6c4b5a9e9d6 100644 --- a/spec/models/concerns/participable_spec.rb +++ b/spec/models/concerns/participable_spec.rb @@ -24,7 +24,7 @@ describe Participable do user1 = build(:user) user2 = build(:user) user3 = build(:user) - project = build(:project, :public) + project = build(:empty_project, :public) instance = model.new expect(instance).to receive(:foo).and_return(user2) @@ -57,7 +57,7 @@ describe Participable do other = other_model.new user1 = build(:user) user2 = build(:user) - project = build(:project, :public) + project = build(:empty_project, :public) expect(instance).to receive(:foo).and_return(other) expect(other).to receive(:bar).and_return(user2) @@ -69,7 +69,7 @@ describe Participable do context 'when using a Proc as an attribute' do it 'calls the supplied Proc' do user1 = build(:user) - project = build(:project, :public) + project = build(:empty_project, :public) user_arg = nil ext_arg = nil diff --git a/spec/models/concerns/resolvable_note_spec.rb b/spec/models/concerns/resolvable_note_spec.rb index 53eaa6f8461..d00faa4f8be 100644 --- a/spec/models/concerns/resolvable_note_spec.rb +++ b/spec/models/concerns/resolvable_note_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Note, ResolvableNote do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } subject { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) } diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index 36aedd2f701..866a835d62a 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -27,7 +27,7 @@ describe Group, 'Routable' do it 'ensure route path uniqueness across different objects' do create(:group, parent: group, path: 'xyz') - duplicate = build(:project, namespace: group, path: 'xyz') + duplicate = build(:empty_project, namespace: group, path: 'xyz') expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Route path has already been taken, Route is invalid') end diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb index eff41d85972..bae88cb1d24 100644 --- a/spec/models/container_repository_spec.rb +++ b/spec/models/container_repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ContainerRepository do let(:group) { create(:group, name: 'group') } - let(:project) { create(:project, path: 'test', group: group) } + let(:project) { create(:project, :repository, path: 'test', group: group) } let(:repository) do create(:container_repository, name: 'my_image', project: project) @@ -41,7 +41,7 @@ describe ContainerRepository do end context 'when path contains uppercase letters' do - let(:project) { create(:project, path: 'MY_PROJECT', group: group) } + let(:project) { create(:project, :repository, path: 'MY_PROJECT', group: group) } it 'returns a full path without capital letters' do expect(repository.path).to eq('group/my_project/my_image') diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 6447095078b..c5708e70ef9 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -91,7 +91,7 @@ describe Deployment do end describe '#additional_metrics' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:deployment) { create(:deployment, project: project) } subject { deployment.additional_metrics } diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb index 2704698f6c9..fa02434b0fd 100644 --- a/spec/models/diff_discussion_spec.rb +++ b/spec/models/diff_discussion_spec.rb @@ -5,7 +5,7 @@ describe DiffDiscussion do subject { described_class.new([diff_note]) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) } diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index ebf2c070116..9a35213c98d 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -21,7 +21,7 @@ describe Environment do it { is_expected.to validate_uniqueness_of(:external_url).scoped_to(:project_id) } describe '.order_by_last_deployed_at' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let!(:environment1) { create(:environment, project: project) } let!(:environment2) { create(:environment, project: project) } let!(:environment3) { create(:environment, project: project) } diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb index 38fbdd2536a..7dbeb4d2e74 100644 --- a/spec/models/forked_project_link_spec.rb +++ b/spec/models/forked_project_link_spec.rb @@ -41,7 +41,7 @@ describe ForkedProjectLink, "add link on fork" do end describe '#forked?' do - let(:project_to) { create(:project, forked_project_link: forked_project_link) } + let(:project_to) { create(:project, :repository, forked_project_link: forked_project_link) } let(:forked_project_link) { create(:forked_project_link) } before do diff --git a/spec/models/gpg_signature_spec.rb b/spec/models/gpg_signature_spec.rb index 9a9b1900aa5..c58fd46762a 100644 --- a/spec/models/gpg_signature_spec.rb +++ b/spec/models/gpg_signature_spec.rb @@ -16,7 +16,7 @@ RSpec.describe GpgSignature do describe '#commit' do it 'fetches the commit through the project' do commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' - project = create :project + project = create :project, :repository commit = create :commit, project: project gpg_signature = create :gpg_signature, commit_sha: commit_sha diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index d72790eefe5..eaf06668327 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -724,7 +724,7 @@ describe Issue do end describe '#check_for_spam' do - let(:project) { create :project, visibility_level: visibility_level } + let(:project) { create :empty_project, visibility_level: visibility_level } let(:issue) { create :issue, project: project } subject do diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb index d68d8b719cd..0938a7b1b6d 100644 --- a/spec/models/project_group_link_spec.rb +++ b/spec/models/project_group_link_spec.rb @@ -9,7 +9,7 @@ describe ProjectGroupLink do describe "Validation" do let(:parent_group) { create(:group) } let(:group) { create(:group, parent: parent_group) } - let(:project) { create(:project, group: group) } + let(:project) { create(:empty_project, group: group) } let!(:project_group_link) { create(:project_group_link, project: project) } it { is_expected.to validate_presence_of(:project_id) } diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb index 413ceed73bf..ae80c4ae002 100644 --- a/spec/models/project_services/chat_notification_service_spec.rb +++ b/spec/models/project_services/chat_notification_service_spec.rb @@ -20,7 +20,7 @@ describe ChatNotificationService do context 'with repository' do it 'returns true' do - subject.project = create(:project) + subject.project = create(:project, :repository) expect(subject.can_test?).to be true end diff --git a/spec/models/project_services/issue_tracker_service_spec.rb b/spec/models/project_services/issue_tracker_service_spec.rb index e6a1752576b..3ca123cb75d 100644 --- a/spec/models/project_services/issue_tracker_service_spec.rb +++ b/spec/models/project_services/issue_tracker_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe IssueTrackerService do describe 'Validations' do - let(:project) { create :project } + let(:project) { create :empty_project } describe 'only one issue tracker per project' do let(:service) { RedmineService.new(project: project, active: true) } diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index bc9374d6dbb..69bb8f1c725 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -190,7 +190,7 @@ describe JiraService do describe '#test_settings' do let(:jira_service) do described_class.new( - project: create(:project), + project: create(:empty_project), url: 'http://jira.example.com', username: 'jira_username', password: 'jira_password' diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 29cc9d35fc9..3bc6fb09778 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -261,27 +261,27 @@ describe Project do describe 'path validation' do it 'allows paths reserved on the root namespace' do - project = build(:project, path: 'api') + project = build(:empty_project, path: 'api') expect(project).to be_valid end it 'rejects paths reserved on another level' do - project = build(:project, path: 'tree') + project = build(:empty_project, path: 'tree') expect(project).not_to be_valid end it 'rejects nested paths' do parent = create(:group, :nested, path: 'environments') - project = build(:project, path: 'folders', namespace: parent) + project = build(:empty_project, path: 'folders', namespace: parent) expect(project).not_to be_valid end it 'allows a reserved group name' do parent = create(:group) - project = build(:project, path: 'avatar', namespace: parent) + project = build(:empty_project, path: 'avatar', namespace: parent) expect(project).to be_valid end @@ -2045,7 +2045,7 @@ describe Project do end describe '#route_map_for' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:route_map) do <<-MAP.strip_heredoc - source: /source/(.*)/ @@ -2082,7 +2082,7 @@ describe Project do end describe '#public_path_for_source_path' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:route_map) do Gitlab::RouteMap.new(<<-MAP.strip_heredoc) - source: /source/(.*)/ @@ -2196,7 +2196,7 @@ describe Project do end describe '#pipeline_status' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } it 'builds a pipeline status' do expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus) end @@ -2207,7 +2207,7 @@ describe Project do end describe '#append_or_update_attribute' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } it 'shows full error updating an invalid MR' do error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\ diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index 68228a038a8..9ccd366a48e 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -330,7 +330,7 @@ describe ProjectTeam do end shared_examples 'max member access for users' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:group) { create(:group) } let(:second_group) { create(:group) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 0fd3a4d622a..3107037925b 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -300,7 +300,7 @@ describe Repository do end context "when committing to another project" do - let(:forked_project) { create(:project) } + let(:forked_project) { create(:project, :repository) } it "creates a fork and commit to the forked project" do expect do diff --git a/spec/models/sent_notification_spec.rb b/spec/models/sent_notification_spec.rb index 8b6b02916ae..8f05deb8b15 100644 --- a/spec/models/sent_notification_spec.rb +++ b/spec/models/sent_notification_spec.rb @@ -21,7 +21,7 @@ describe SentNotification do end context "when the noteable project and discussion project match" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:issue) { create(:issue, project: project) } let(:discussion_id) { create(:note, project: project, noteable: issue).discussion_id } subject { build(:sent_notification, project: project, noteable: issue, in_reply_to_discussion_id: discussion_id) } @@ -128,7 +128,7 @@ describe SentNotification do end context 'for commit' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { project.commit } subject { described_class.record(commit, project.creator.id) } diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb index a83a83a7349..11a4c3c742e 100644 --- a/spec/policies/ci/build_policy_spec.rb +++ b/spec/policies/ci/build_policy_spec.rb @@ -97,7 +97,7 @@ describe Ci::BuildPolicy do end describe 'rules for protected ref' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, ref: 'some-ref', pipeline: pipeline) } before do diff --git a/spec/policies/ci/pipeline_policy_spec.rb b/spec/policies/ci/pipeline_policy_spec.rb index b11b06d301f..48a8064c5fc 100644 --- a/spec/policies/ci/pipeline_policy_spec.rb +++ b/spec/policies/ci/pipeline_policy_spec.rb @@ -10,7 +10,7 @@ describe Ci::PipelinePolicy, :models do describe 'rules' do describe 'rules for protected ref' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do project.add_developer(user) diff --git a/spec/policies/environment_policy_spec.rb b/spec/policies/environment_policy_spec.rb index 035e20c7452..de4cb5b30c5 100644 --- a/spec/policies/environment_policy_spec.rb +++ b/spec/policies/environment_policy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe EnvironmentPolicy do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:environment) do create(:environment, :with_review_app, project: project) @@ -14,7 +14,7 @@ describe EnvironmentPolicy do describe '#rules' do context 'when user does not have access to the project' do - let(:project) { create(:project, :private) } + let(:project) { create(:project, :private, :repository) } it 'does not include ability to stop environment' do expect(policy).to be_disallowed :stop_environment @@ -22,7 +22,7 @@ describe EnvironmentPolicy do end context 'when anonymous user has access to the project' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } it 'does not include ability to stop environment' do expect(policy).to be_disallowed :stop_environment @@ -30,7 +30,7 @@ describe EnvironmentPolicy do end context 'when team member has access to the project' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } before do project.add_developer(user) diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index c1a0313b13c..0dd4ede5538 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -105,7 +105,7 @@ describe MergeRequestPresenter do end context 'issues links' do - let(:project) { create(:project, :private, creator: user, namespace: user.namespace) } + let(:project) { create(:project, :private, :repository, creator: user, namespace: user.namespace) } let(:issue_a) { create(:issue, project: project) } let(:issue_b) { create(:issue, project: project) } diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 9e268adf950..55c998b13b8 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -80,7 +80,7 @@ describe API::Files do context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository files' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:current_user) { nil } end end @@ -153,7 +153,7 @@ describe API::Files do context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository raw files' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:current_user) { nil } end end diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb index b34555d2815..9ff2b782b52 100644 --- a/spec/requests/api/pipeline_schedules_spec.rb +++ b/spec/requests/api/pipeline_schedules_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::PipelineSchedules do set(:developer) { create(:user) } set(:user) { create(:user) } - set(:project) { create(:project) } + set(:project) { create(:project, :repository) } before do project.add_developer(developer) diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index 9fc73c6e092..f5413913c1e 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe API::Todos do - let(:project_1) { create(:project) } + let(:project_1) { create(:project, :repository) } let(:project_2) { create(:empty_project) } let(:author_1) { create(:user) } let(:author_2) { create(:user) } diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 153596c2975..d5c53b703dd 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -13,7 +13,7 @@ describe API::Triggers do let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') } describe 'POST /projects/:project_id/trigger/pipeline' do - let!(:project2) { create(:project) } + let!(:project2) { create(:project, :repository) } let(:options) do { token: trigger_token diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb index 8b2d165c763..4ffa5d1784e 100644 --- a/spec/requests/api/v3/files_spec.rb +++ b/spec/requests/api/v3/files_spec.rb @@ -74,7 +74,7 @@ describe API::V3::Files do context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository files' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:current_user) { nil } end end diff --git a/spec/requests/api/v3/project_hooks_spec.rb b/spec/requests/api/v3/project_hooks_spec.rb index b0eddbb5dd2..e7014458773 100644 --- a/spec/requests/api/v3/project_hooks_spec.rb +++ b/spec/requests/api/v3/project_hooks_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::ProjectHooks, 'ProjectHooks' do let(:user) { create(:user) } let(:user3) { create(:user) } - let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } + let!(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) } let!(:hook) do create(:project_hook, :all_events_enabled, @@ -204,7 +204,7 @@ describe API::ProjectHooks, 'ProjectHooks' do it "returns a 404 if a user attempts to delete project hooks he/she does not own" do test_user = create(:user) - other_project = create(:project) + other_project = create(:empty_project) other_project.team << [test_user, :master] delete v3_api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user) diff --git a/spec/requests/api/v3/triggers_spec.rb b/spec/requests/api/v3/triggers_spec.rb index 60212660fb6..2a593dd8135 100644 --- a/spec/requests/api/v3/triggers_spec.rb +++ b/spec/requests/api/v3/triggers_spec.rb @@ -10,7 +10,7 @@ describe API::V3::Triggers do let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) } describe 'POST /projects/:project_id/trigger' do - let!(:project2) { create(:project) } + let!(:project2) { create(:empty_project) } let(:options) do { token: trigger_token diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 4f1a90750b3..ff470a6cad0 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -600,7 +600,7 @@ describe 'Git LFS API and storage' do context 'when user is not authenticated' do describe 'is accessing public project' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:update_lfs_permissions) do project.lfs_objects << lfs_object @@ -642,7 +642,7 @@ describe 'Git LFS API and storage' do end describe 'upload' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:body) do { 'operation' => 'upload', @@ -1019,7 +1019,7 @@ describe 'Git LFS API and storage' do end describe 'to a forked project' do - let(:upstream_project) { create(:project, :public) } + let(:upstream_project) { create(:empty_project, :public) } let(:project_owner) { create(:user) } let(:project) { fork_project(upstream_project, project_owner) } diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb index 9afeb2983b0..1325ba37fd8 100644 --- a/spec/requests/request_profiler_spec.rb +++ b/spec/requests/request_profiler_spec.rb @@ -13,7 +13,7 @@ describe 'Request Profiler' do end it 'creates a profile of the request' do - project = create(:project, namespace: user.namespace) + project = create(:empty_project, namespace: user.namespace) time = Time.now path = "/#{project.full_path}" diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 4ec495f612e..730df4e0336 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -75,7 +75,7 @@ describe Ci::CreatePipelineService do end context 'when merge request target project is different from source project' do - let!(:target_project) { create(:project) } + let!(:target_project) { create(:project, :repository) } let!(:forked_project_link) { create(:forked_project_link, forked_to_project: project, forked_from_project: target_project) } it 'updates head pipeline for merge request' do diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index 1ced26ff98d..04f926ccb32 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -33,7 +33,7 @@ describe Ci::PlayBuildService, '#execute' do end context 'when project has repository' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } it 'allows user with developer role to play a build' do project.add_developer(user) diff --git a/spec/services/labels/create_service_spec.rb b/spec/services/labels/create_service_spec.rb index ecb88653001..9ff3a5375b9 100644 --- a/spec/services/labels/create_service_spec.rb +++ b/spec/services/labels/create_service_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe Labels::CreateService do describe '#execute' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:group) { create(:group) } - + let(:hex_color) { '#FF0000' } let(:named_color) { 'red' } let(:upcase_color) { 'RED' } diff --git a/spec/services/labels/update_service_spec.rb b/spec/services/labels/update_service_spec.rb index bb95fe20fbf..f0bec7e9f9c 100644 --- a/spec/services/labels/update_service_spec.rb +++ b/spec/services/labels/update_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Labels::UpdateService do describe '#execute' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:hex_color) { '#FF0000' } let(:named_color) { 'red' } diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb index 5739386dd0d..f7a6c3d323e 100644 --- a/spec/services/milestones/destroy_service_spec.rb +++ b/spec/services/milestones/destroy_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Milestones::DestroyService do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:milestone) { create(:milestone, title: 'Milestone v1.0', project: project) } let(:issue) { create(:issue, project: project, milestone: milestone) } let(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) } diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index 786335120fd..db30fe2c24e 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -40,7 +40,7 @@ describe Users::DestroyService do end context "a deleted user's issues" do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } before do project.add_developer(user) @@ -66,7 +66,7 @@ describe Users::DestroyService do end context "a deleted user's merge_requests" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do project.add_developer(user) diff --git a/spec/services/users/migrate_to_ghost_user_service_spec.rb b/spec/services/users/migrate_to_ghost_user_service_spec.rb index a0030ce8809..ac3a8738cac 100644 --- a/spec/services/users/migrate_to_ghost_user_service_spec.rb +++ b/spec/services/users/migrate_to_ghost_user_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Users::MigrateToGhostUserService do let!(:user) { create(:user) } - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let(:service) { described_class.new(user) } context "migrating a user's associated records to the ghost user" do diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb index 035428a7d9b..47a7fd0c2ea 100644 --- a/spec/support/features/issuable_slash_commands_shared_examples.rb +++ b/spec/support/features/issuable_slash_commands_shared_examples.rb @@ -5,7 +5,14 @@ shared_examples 'issuable record that supports quick actions in its description include QuickActionsHelpers let(:master) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) do + case issuable_type + when :merge_request + create(:project, :public, :repository) + when :issue + create(:empty_project, :public) + end + end let!(:milestone) { create(:milestone, project: project, title: 'ASAP') } let!(:label_bug) { create(:label, project: project, title: 'bug') } let!(:label_feature) { create(:label, project: project, title: 'feature') } diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb index 57b6abe12b7..2011408be93 100644 --- a/spec/support/import_export/export_file_helper.rb +++ b/spec/support/import_export/export_file_helper.rb @@ -6,7 +6,7 @@ module ExportFileHelper ObjectWithParent = Struct.new(:object, :parent, :key_found) def setup_project - project = create(:project, :public) + project = create(:project, :public, :repository) create(:release, project: project) diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb index 855051921f0..bbb63a08374 100644 --- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb +++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb @@ -3,7 +3,14 @@ require "spec_helper" shared_examples "migrating a deleted user's associated records to the ghost user" do |record_class, fields| record_class_name = record_class.to_s.titleize.downcase - let(:project) { create(:project) } + let(:project) do + case record_class + when MergeRequest + create(:project, :repository) + else + create(:empty_project) + end + end before do project.add_developer(user) diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb index 47e9365e13d..c2510a10286 100644 --- a/spec/uploaders/file_uploader_spec.rb +++ b/spec/uploaders/file_uploader_spec.rb @@ -5,7 +5,7 @@ describe FileUploader do describe '.absolute_path' do it 'returns the correct absolute path by building it dynamically' do - project = build_stubbed(:project) + project = build_stubbed(:empty_project) upload = double(model: project, path: 'secret/foo.jpg') dynamic_segment = project.path_with_namespace diff --git a/spec/validators/dynamic_path_validator_spec.rb b/spec/validators/dynamic_path_validator_spec.rb index 8bd5306ff98..dd90a836a78 100644 --- a/spec/validators/dynamic_path_validator_spec.rb +++ b/spec/validators/dynamic_path_validator_spec.rb @@ -28,7 +28,7 @@ describe DynamicPathValidator do describe '#path_valid_for_record?' do context 'for project' do it 'calls valid_project_path?' do - project = build(:project, path: 'activity') + project = build(:empty_project, path: 'activity') expect(described_class).to receive(:valid_project_path?).with(project.full_path).and_call_original diff --git a/spec/views/layouts/nav/_project.html.haml_spec.rb b/spec/views/layouts/nav/_project.html.haml_spec.rb index fd1637ca91b..faea2505e40 100644 --- a/spec/views/layouts/nav/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/_project.html.haml_spec.rb @@ -5,7 +5,7 @@ describe 'layouts/nav/_project' do before do stub_container_registry_config(enabled: true) - assign(:project, create(:project)) + assign(:project, create(:project, :repository)) allow(view).to receive(:current_ref).and_return('master') allow(view).to receive(:can?).and_return(true) diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb index f627f9165fb..d9d73f789c5 100644 --- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb +++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb @@ -4,7 +4,7 @@ describe 'notify/pipeline_failed_email.html.haml' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, :simple, source_project: project) } let(:pipeline) do diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb index ecd096ee579..a793b37e412 100644 --- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb +++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb @@ -4,7 +4,7 @@ describe 'notify/pipeline_success_email.html.haml' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, :simple, source_project: project) } let(:pipeline) do diff --git a/spec/workers/create_gpg_signature_worker_spec.rb b/spec/workers/create_gpg_signature_worker_spec.rb index c6a17d77d73..62ec011a3fe 100644 --- a/spec/workers/create_gpg_signature_worker_spec.rb +++ b/spec/workers/create_gpg_signature_worker_spec.rb @@ -4,7 +4,7 @@ describe CreateGpgSignatureWorker do context 'when GpgKey is found' do it 'calls Commit#signature' do commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' - project = create :project + project = create :empty_project commit = instance_double(Commit) allow(Project).to receive(:find_by).with(id: project.id).and_return(project) @@ -18,7 +18,7 @@ describe CreateGpgSignatureWorker do context 'when Commit is not found' do let(:nonexisting_commit_sha) { 'bogus' } - let(:project) { create :project } + let(:project) { create :empty_project } it 'does not raise errors' do expect { described_class.new.perform(nonexisting_commit_sha, project.id) }.not_to raise_error diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb index 6ebc94bb544..24f8ca67594 100644 --- a/spec/workers/process_commit_worker_spec.rb +++ b/spec/workers/process_commit_worker_spec.rb @@ -33,7 +33,7 @@ describe ProcessCommitWorker do end context 'when commit already exists in upstream project' do - let(:forked) { create(:project, :public) } + let(:forked) { create(:project, :public, :repository) } it 'does not process commit message' do create(:forked_project_link, forked_to_project: forked, forked_from_project: project) From feb94e8ea3b003938f5df963d3c61757ffe27bcb Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 1 Aug 2017 21:51:53 +0200 Subject: [PATCH 084/108] Move timeframe_start and timeframe_end to common query context --- .../queries/additional_metrics_deployment_query.rb | 9 ++++----- .../queries/additional_metrics_environment_query.rb | 9 +-------- .../prometheus/queries/query_additional_metrics.rb | 4 +++- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb index 51d934b9ae2..69d055c901c 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb @@ -6,14 +6,13 @@ module Gitlab def query(deployment_id) Deployment.find_by(id: deployment_id).try do |deployment| - query_context = common_query_context(deployment.environment).merge( - { + query_metrics( + common_query_context( + deployment.environment, timeframe_start: (deployment.created_at - 30.minutes).to_f, timeframe_end: (deployment.created_at + 30.minutes).to_f - } + ) ) - - query_metrics(query_context) end end end diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb index 9f798f5b892..580153556ea 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb @@ -6,14 +6,7 @@ module Gitlab def query(environment_id) Environment.find_by(id: environment_id).try do |environment| - query_context = common_query_context(environment).merge( - { - timeframe_start: 8.hours.ago.to_f, - timeframe_end: Time.now.to_f - } - ) - - query_metrics(query_context) + query_metrics(common_query_context(environment)) end end end diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb index d96921a9ee7..d5f219ce6f9 100644 --- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb +++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb @@ -71,8 +71,10 @@ module Gitlab result.select { |group| group.metrics.any? } end - def common_query_context(environment) + def common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f) { + timeframe_start: timeframe_start, + timeframe_end: timeframe_end, ci_environment_slug: environment.slug, kube_namespace: environment.project.kubernetes_service&.actual_namespace || '', environment_filter: %{container_name!="POD",environment="#{environment.slug}"} From ba97a42193d85182cf0974b8aac508d40b6f368a Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 1 Aug 2017 22:11:59 +0200 Subject: [PATCH 085/108] Remove default arguments for common query context --- .../queries/additional_metrics_environment_query.rb | 4 +++- lib/gitlab/prometheus/queries/query_additional_metrics.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb index 580153556ea..db4708b22e4 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb @@ -6,7 +6,9 @@ module Gitlab def query(environment_id) Environment.find_by(id: environment_id).try do |environment| - query_metrics(common_query_context(environment)) + query_metrics( + common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f) + ) end end end diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb index d5f219ce6f9..7ac6162b54d 100644 --- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb +++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb @@ -71,7 +71,7 @@ module Gitlab result.select { |group| group.metrics.any? } end - def common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f) + def common_query_context(environment, timeframe_start:, timeframe_end:) { timeframe_start: timeframe_start, timeframe_end: timeframe_end, From 9f22762dd965c0ca8345e87225a7ef6c38649ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B6=9B?= Date: Wed, 2 Aug 2017 09:31:43 +0800 Subject: [PATCH 086/108] Synchronous zanata community contribution translation --- locale/ja/gitlab.po | 2 +- locale/pt_BR/gitlab.po | 45 ++-- locale/ru/gitlab.po | 489 ++++++++++++++++++++++------------------- locale/uk/gitlab.po | 5 +- locale/zh_TW/gitlab.po | 5 +- 5 files changed, 294 insertions(+), 252 deletions(-) diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po index 04c61906c73..801674ce964 100644 --- a/locale/ja/gitlab.po +++ b/locale/ja/gitlab.po @@ -29,7 +29,7 @@ msgid_plural "%d commits" msgstr[0] "%d個のコミット" msgid "%{commit_author_link} committed %{commit_timeago}" -msgstr "%{commit_author_link}は%{commit_timeago}前、コミットしました。" +msgstr "%{commit_timeago}に%{commit_author_link}がコミットしました。" msgid "1 pipeline" msgid_plural "%d pipelines" diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po index ee00b816b84..9e3c78b6148 100644 --- a/locale/pt_BR/gitlab.po +++ b/locale/pt_BR/gitlab.po @@ -11,7 +11,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language-Team: Portuguese (Brazil) (https://translate.zanata.org/project/view/GitLab)\n" -"PO-Revision-Date: 2017-07-14 01:17-0400\n" +"PO-Revision-Date: 2017-08-01 09:47-0400\n" "Last-Translator: Huang Tao \n" "Language: pt-BR\n" "X-Generator: Zanata 3.9.6\n" @@ -42,7 +42,7 @@ msgid "A collection of graphs regarding Continuous Integration" msgstr "Uma coleção de gráficos sobre Integração Contínua" msgid "About auto deploy" -msgstr "Sobre a implantação automática" +msgstr "Sobre o deploy automático" msgid "Active" msgstr "Ativo" @@ -84,12 +84,12 @@ msgid "" "choose a GitLab CI Yaml template and commit your changes. " "%{link_to_autodeploy_doc}" msgstr "" -"O branch %{branch_name} foi criado. Para configurar a " -"implantação automática, selecione um modelo de Yaml do GitLab CI e registre " -"suas mudanças. %{link_to_autodeploy_doc}" +"O branch %{branch_name} foi criado. Para configurar o " +"deploy automático, selecione um modelo de Yaml do GitLab CI e commit suas " +"mudanças. %{link_to_autodeploy_doc}" msgid "BranchSwitcherPlaceholder|Search branches" -msgstr "BranchSwitcherPlaceholder|Procurar por branches" +msgstr "Procurar por branches" msgid "BranchSwitcherTitle|Switch branch" msgstr "BranchSwitcherTitle|Mudar de branch" @@ -113,7 +113,7 @@ msgid "ByAuthor|by" msgstr "por" msgid "CI configuration" -msgstr "Configuração da Integração Contínua" +msgstr "Configuração da IC" msgid "Cancel" msgstr "Cancelar" @@ -269,7 +269,7 @@ msgid "CreateTag|Tag" msgstr "Tag" msgid "CreateTokenToCloneLink|create a personal access token" -msgstr "CreateTokenToCloneLink|criar um token de acesso pessoal" +msgstr "criar um token de acesso pessoal" msgid "Cron Timezone" msgstr "Fuso horário do cron" @@ -419,8 +419,7 @@ msgid "From issue creation until deploy to production" msgstr "Da abertura de tarefas até a implantação para a produção" msgid "From merge request merge until deploy to production" -msgstr "" -"Do merge request até a implantação em produção" +msgstr "Do merge request até a implantação em produção" msgid "Go to your fork" msgstr "Ir para seu fork" @@ -618,19 +617,19 @@ msgid "Pipeline Schedules" msgstr "Agendamentos da Pipeline" msgid "PipelineCharts|Failed:" -msgstr "PipelineCharts|Falhou:" +msgstr "Falhou:" msgid "PipelineCharts|Overall statistics" -msgstr "PipelineCharts|Estatísticas gerais" +msgstr "Estatísticas gerais" msgid "PipelineCharts|Success ratio:" -msgstr "PipelineCharts|Taxa de sucesso:" +msgstr "Taxa de sucesso:" msgid "PipelineCharts|Successful:" -msgstr "PipelineCharts|Sucesso:" +msgstr "Sucesso:" msgid "PipelineCharts|Total:" -msgstr "PipelineCharts|Total:" +msgstr "Total:" msgid "PipelineSchedules|Activated" msgstr "Ativado" @@ -645,10 +644,10 @@ msgid "PipelineSchedules|Inactive" msgstr "Inativo" msgid "PipelineSchedules|Input variable key" -msgstr "PipelineSchedules|Chave da variável de entrada" +msgstr "Chave da variável de entrada" msgid "PipelineSchedules|Input variable value" -msgstr "PipelineSchedules|Valor da variável de entrada" +msgstr "Valor da variável de entrada" msgid "PipelineSchedules|Next Run" msgstr "Próxima Execução" @@ -660,7 +659,7 @@ msgid "PipelineSchedules|Provide a short description for this pipeline" msgstr "Digite uma descrição curta para esta pipeline" msgid "PipelineSchedules|Remove variable row" -msgstr "PipelineSchedules|Remova a linha da variável" +msgstr "Remova a linha da variável" msgid "PipelineSchedules|Take ownership" msgstr "Tornar-se proprietário" @@ -669,7 +668,7 @@ msgid "PipelineSchedules|Target" msgstr "Destino" msgid "PipelineSchedules|Variables" -msgstr "PipelineSchedules|Variáveis" +msgstr "Variáveis" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "Personalizado" @@ -681,10 +680,10 @@ msgid "Pipelines charts" msgstr "Gráficos de pipelines" msgid "Pipeline|all" -msgstr "Pipeline|todos" +msgstr "todos" msgid "Pipeline|success" -msgstr "Pipeline|sucesso" +msgstr "sucesso" msgid "Pipeline|with stage" msgstr "com etapa" @@ -974,7 +973,7 @@ msgid "Time before an issue gets scheduled" msgstr "Tempo até que uma issue seja agendada" msgid "Time before an issue starts implementation" -msgstr "Tempo até que uma issue comece a ser implementada" +msgstr "Tempo até que uma issue comece a ser implementado" msgid "Time between merge request creation and merge/close" msgstr "" @@ -1136,7 +1135,7 @@ msgid "Upload file" msgstr "Enviar arquivo" msgid "UploadLink|click to upload" -msgstr "UploadLink|clique para fazer upload" +msgstr "clique para fazer upload" msgid "Use your global notification setting" msgstr "Utilizar configuração de notificação global" diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po index 4643bed98e2..f9e8dcd05e7 100644 --- a/locale/ru/gitlab.po +++ b/locale/ru/gitlab.po @@ -4,13 +4,13 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-28 13:32+0200\n" +"POT-Creation-Date: 2017-07-05 08:50-0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-11 05:13-0400\n" -"Last-Translator: SAS \n" "Language-Team: Russian (https://translate.zanata.org/project/view/GitLab)\n" +"PO-Revision-Date: 2017-08-01 09:15-0400\n" +"Last-Translator: Андрей П. \n" "Language: ru\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " @@ -32,20 +32,20 @@ msgstr[2] "" msgid "%d commit" msgid_plural "%d commits" msgstr[0] "%d коммит" -msgstr[1] "%d коммит(а|ов)" -msgstr[2] "%d коммит(а|ов)" +msgstr[1] "%d коммитов" +msgstr[2] "%d коммитов" msgid "%{commit_author_link} committed %{commit_timeago}" -msgstr "%{commit_author_link} закоммичено %{commit_timeago}" +msgstr "%{commit_author_link} коммичено %{commit_timeago}" msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "1 конвейер" +msgstr[1] "%d конвейеры" +msgstr[2] "%d конвейеры" msgid "A collection of graphs regarding Continuous Integration" -msgstr "" +msgstr "Графики относительно непрерывной интеграции" msgid "About auto deploy" msgstr "Автоматическое развертывание" @@ -57,10 +57,10 @@ msgid "Activity" msgstr "Активность" msgid "Add Changelog" -msgstr "Добавить в журнал изменений" +msgstr "Добавить журнал изменений" msgid "Add Contribution guide" -msgstr "Добавить руководство для контрибьютеров" +msgstr "Добавить руководство" msgid "Add License" msgstr "Добавить лицензию" @@ -71,7 +71,7 @@ msgstr "" "SSH." msgid "Add new directory" -msgstr "Добавить новую директорию" +msgstr "Добавить каталог" msgid "Archived project! Repository is read-only" msgstr "Архивный проект! Репозиторий доступен только для чтения" @@ -98,16 +98,16 @@ msgstr "" "%{link_to_autodeploy_doc}" msgid "BranchSwitcherPlaceholder|Search branches" -msgstr "BranchSwitcherPlaceholder|Поиск веток" +msgstr "Поиск веток" msgid "BranchSwitcherTitle|Switch branch" -msgstr "BranchSwitcherTitle|Переключить ветку" +msgstr "Переключить ветку" msgid "Branches" msgstr "Ветки" msgid "Browse Directory" -msgstr "Просмотр директории" +msgstr "Обзор" msgid "Browse File" msgstr "Просмотр файла" @@ -119,7 +119,7 @@ msgid "Browse files" msgstr "Просмотр файлов" msgid "ByAuthor|by" -msgstr "ByAuthor|по автору" +msgstr "по автору" msgid "CI configuration" msgstr "Настройка CI" @@ -128,22 +128,22 @@ msgid "Cancel" msgstr "Отмена" msgid "ChangeTypeActionLabel|Pick into branch" -msgstr "ChangeTypeActionLabel|Выбрать в ветке" +msgstr "Выбрать в ветке" msgid "ChangeTypeActionLabel|Revert in branch" -msgstr "ChangeTypeActionLabel|Отменить в ветке" +msgstr "Отменить в ветке" msgid "ChangeTypeAction|Cherry-pick" -msgstr "ChangeTypeAction|Подобрать" +msgstr "Подобрать" msgid "ChangeTypeAction|Revert" -msgstr "ChangeTypeAction|Отменить" +msgstr "Отменить" msgid "Changelog" msgstr "Журнал изменений" msgid "Charts" -msgstr "Графики" +msgstr "Диаграммы" msgid "Cherry-pick this commit" msgstr "Подобрать в этом коммите" @@ -152,58 +152,58 @@ msgid "Cherry-pick this merge request" msgstr "Побрать в этом запросе на слияние" msgid "CiStatusLabel|canceled" -msgstr "CiStatusLabel|отменено" +msgstr "отменено" msgid "CiStatusLabel|created" -msgstr "CiStatusLabel|создано" +msgstr "создано" msgid "CiStatusLabel|failed" -msgstr "CiStatusLabel|неудачно" +msgstr "неудачно" msgid "CiStatusLabel|manual action" -msgstr "CiStatusLabel|ручное действие" +msgstr "ручное действие" msgid "CiStatusLabel|passed" -msgstr "CiStatusLabel|пройдено" +msgstr "пройдено" msgid "CiStatusLabel|passed with warnings" -msgstr "CiStatusLabel|пройдено с предупреждениями" +msgstr "пройдено с предупреждениями" msgid "CiStatusLabel|pending" -msgstr "CiStatusLabel|в ожидании" +msgstr "в ожидании" msgid "CiStatusLabel|skipped" -msgstr "CiStatusLabel|пропущено" +msgstr "пропущено" msgid "CiStatusLabel|waiting for manual action" -msgstr "CiStatusLabel|ожидание ручных действий" +msgstr "ожидание ручных действий" msgid "CiStatusText|blocked" -msgstr "CiStatusText|блокировано" +msgstr "блокировано" msgid "CiStatusText|canceled" -msgstr "CiStatusText|отменено" +msgstr "отменено" msgid "CiStatusText|created" -msgstr "CiStatusText|создано" +msgstr "создано" msgid "CiStatusText|failed" -msgstr "CiStatusText|неудачно" +msgstr "неудачно" msgid "CiStatusText|manual" -msgstr "CiStatusText|ручное" +msgstr "ручное" msgid "CiStatusText|passed" -msgstr "CiStatusText|пройдено" +msgstr "пройдено" msgid "CiStatusText|pending" -msgstr "CiStatusText|в ожидании" +msgstr "в ожидании" msgid "CiStatusText|skipped" -msgstr "CiStatusText|пропущено" +msgstr "пропущено" msgid "CiStatus|running" -msgstr "CiStatus|выполняется" +msgstr "выполняется" msgid "Commit" msgid_plural "Commits" @@ -212,13 +212,13 @@ msgstr[1] "Коммиты" msgstr[2] "Коммиты" msgid "Commit duration in minutes for last 30 commits" -msgstr "" +msgstr "Продолжительность последних 30 фиксаций(коммитов) в минутах" msgid "Commit message" msgstr "Описание коммита" msgid "CommitBoxTitle|Commit" -msgstr "CommitBoxTitle|Коммит" +msgstr "Коммит" msgid "CommitMessage|Add %{file_name}" msgstr "CommitMessage|Добавить %{file_name}" @@ -227,22 +227,22 @@ msgid "Commits" msgstr "Коммиты" msgid "Commits feed" -msgstr "" +msgstr "Фиксировать подачу" msgid "Commits|History" -msgstr "Commits|История" +msgstr "История" msgid "Committed by" -msgstr "Коммит" +msgstr "Фиксировано" msgid "Compare" -msgstr "Сравнение" +msgstr "Сравнить" msgid "Contribution guide" -msgstr "Руководство контрибьютора" +msgstr "Руководство участника" msgid "Contributors" -msgstr "Контрибьюторы" +msgstr "Участники" msgid "Copy URL to clipboard" msgstr "Копировать URL в буфер обмена" @@ -251,18 +251,20 @@ msgid "Copy commit SHA to clipboard" msgstr "Копировать SHA коммита в буфер обмена" msgid "Create New Directory" -msgstr "Создать новую директорию" +msgstr "Создать директорию" msgid "" "Create a personal access token on your account to pull or push via " "%{protocol}." msgstr "" +"Создать личный токен на аккаунте для получения или отправки через " +"%{protocol}." msgid "Create directory" msgstr "Создать директорию" msgid "Create empty bare repository" -msgstr "Создать пустой пустой репозиторий" +msgstr "Создать пустой репозиторий" msgid "Create merge request" msgstr "Создать запрос на объединение" @@ -271,13 +273,13 @@ msgid "Create new..." msgstr "Новый" msgid "CreateNewFork|Fork" -msgstr "CreateNewFork|Форк" +msgstr "Форк" msgid "CreateTag|Tag" -msgstr "CreateTag|Тэг" +msgstr "Тег" msgid "CreateTokenToCloneLink|create a personal access token" -msgstr "" +msgstr "создать персональный токен доступа" msgid "Cron Timezone" msgstr "Временная зона Cron" @@ -299,33 +301,35 @@ msgstr "" "посмотрите %{notification_link}." msgid "Cycle Analytics" -msgstr "Аналитика цикла разработки" +msgstr "Аналитический цикл" msgid "" "Cycle Analytics gives an overview of how much time it takes to go from idea " "to production in your project." msgstr "" +"Аналитический цикл дает представление о том, сколько времени требуется, " +"чтобы перейти от идеи к производству в проекте." msgid "CycleAnalyticsStage|Code" -msgstr "CycleAnalyticsStage|Код" +msgstr "Написание кода" msgid "CycleAnalyticsStage|Issue" -msgstr "CycleAnalyticsStage|Обращение" +msgstr "Обращение" msgid "CycleAnalyticsStage|Plan" -msgstr "" +msgstr "Планирование" msgid "CycleAnalyticsStage|Production" -msgstr "" +msgstr "Производство" msgid "CycleAnalyticsStage|Review" -msgstr "CycleAnalyticsStage|Ревьюв" +msgstr "Контроль" msgid "CycleAnalyticsStage|Staging" -msgstr "" +msgstr "Постановка" msgid "CycleAnalyticsStage|Test" -msgstr "" +msgstr "Тестирование" msgid "Define a custom pattern with cron syntax" msgstr "Определить настраиваемый шаблон с синтаксисом cron" @@ -335,45 +339,45 @@ msgstr "Удалить" msgid "Deploy" msgid_plural "Deploys" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Разместить" +msgstr[1] "Размещение" +msgstr[2] "Размещение" msgid "Description" msgstr "Описание" msgid "Directory name" -msgstr "Наименование директории" +msgstr "Каталог" msgid "Don't show again" msgstr "Не показывать снова" msgid "Download" -msgstr "Загрузить" +msgstr "Скачать" msgid "Download tar" -msgstr "Загрузить tar" +msgstr "Скачать tar" msgid "Download tar.bz2" -msgstr "Загрузить tar.bz2" +msgstr "Скачать tar.bz2" msgid "Download tar.gz" -msgstr "Загрузить tar.gz" +msgstr "Скачать tar.gz" msgid "Download zip" -msgstr "Загрузить zip" +msgstr "Скачать zip" msgid "DownloadArtifacts|Download" -msgstr "DownloadArtifacts|Загрузка" +msgstr "Скачать" msgid "DownloadCommit|Email Patches" -msgstr "DownloadCommit|Email-патчи" +msgstr "Email-патчи" msgid "DownloadCommit|Plain Diff" -msgstr "DownloadCommit|Plain Diff" +msgstr "Простой Diff" msgid "DownloadSource|Download" -msgstr "DownloadSource|Загрузка" +msgstr "Скачать" msgid "Edit" msgstr "Редактировать" @@ -409,10 +413,10 @@ msgid "Find file" msgstr "Найти файл" msgid "FirstPushedBy|First" -msgstr "" +msgstr "Первый" msgid "FirstPushedBy|pushed by" -msgstr "" +msgstr "протолкнул" msgid "Fork" msgid_plural "Forks" @@ -421,22 +425,22 @@ msgstr[1] "Форки" msgstr[2] "Форки" msgid "ForkedFromProjectPath|Forked from" -msgstr "ForkedFromProjectPath|Форк от " +msgstr "Форк от " msgid "From issue creation until deploy to production" -msgstr "" +msgstr "От создания проблемы до развертывания в рабочей среде" msgid "From merge request merge until deploy to production" -msgstr "" +msgstr "От запроса на слияние до развертывания в рабочей среде" msgid "Go to your fork" msgstr "Перейти к вашему форку" msgid "GoToYourFork|Fork" -msgstr "GoToYourFork|Форк" +msgstr "Форк" msgid "Home" -msgstr "Домашняя" +msgstr "Главная" msgid "Housekeeping successfully started" msgstr "Очистка успешно запущена" @@ -448,28 +452,28 @@ msgid "Interval Pattern" msgstr "Шаблон интервала" msgid "Introducing Cycle Analytics" -msgstr "" +msgstr "Внедрение Цикла Аналитики" msgid "Jobs for last month" -msgstr "" +msgstr "Работы за прошлый месяц" msgid "Jobs for last week" -msgstr "" +msgstr "Работы за прошлую неделю" msgid "Jobs for last year" -msgstr "" +msgstr "Работы за прошлый год" msgid "LFSStatus|Disabled" -msgstr "LFSStatus|Отключено" +msgstr "Отключено" msgid "LFSStatus|Enabled" -msgstr "LFSStatus|Включено" +msgstr "Включено" msgid "Last %d day" msgid_plural "Last %d days" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Последний %d день" +msgstr[1] "Последние %d дни" +msgstr[2] "Последние %d дни" msgid "Last Pipeline" msgstr "Последний конвейер" @@ -494,21 +498,21 @@ msgstr "Покинуть проект" msgid "Limited to showing %d event at most" msgid_plural "Limited to showing %d events at most" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Ограничение %d события" +msgstr[1] "Ограничение %d событий" +msgstr[2] "Ограничение %d событий" msgid "Median" -msgstr "Медиана" +msgstr "Среднее" msgid "MissingSSHKeyWarningLink|add an SSH key" -msgstr "MissingSSHKeyWarningLink|добавить ключ SSH" +msgstr "добавить ключ SSH" msgid "New Issue" msgid_plural "New Issues" -msgstr[0] "Новое обращение" -msgstr[1] "Новые обращения" -msgstr[2] "Новые обращения" +msgstr[0] "Обращение" +msgstr[1] "Обращения" +msgstr[2] "Обращения" msgid "New Pipeline Schedule" msgstr "Новое расписание конвейера" @@ -535,7 +539,7 @@ msgid "New snippet" msgstr "Новый сниппет" msgid "New tag" -msgstr "Новый тэг" +msgstr "Новый тег" msgid "No repository" msgstr "Нет репозитория" @@ -544,70 +548,70 @@ msgid "No schedules" msgstr "Нет расписания" msgid "Not available" -msgstr "" +msgstr "Недоступно" msgid "Not enough data" -msgstr "" +msgstr "Нет данных" msgid "Notification events" msgstr "Уведомления о событиях" msgid "NotificationEvent|Close issue" -msgstr "NotificationEvent|Обращение закрыто" +msgstr "Обращение закрыто" msgid "NotificationEvent|Close merge request" msgstr "Запрос на объединение закрыт" msgid "NotificationEvent|Failed pipeline" -msgstr "NotificationEvent|Неудача в конвейере" +msgstr "Неудача в конвейере" msgid "NotificationEvent|Merge merge request" -msgstr "NotificationEvent|Объединить запрос на слияние" +msgstr "Объединить запрос на слияние" msgid "NotificationEvent|New issue" -msgstr "NotificationEvent|Новое обращение" +msgstr "Новое обращение" msgid "NotificationEvent|New merge request" -msgstr "NotificationEvent|Новый запрос на слияние" +msgstr "Новый запрос на слияние" msgid "NotificationEvent|New note" -msgstr "NotificationEvent|Новая заметка" +msgstr "Новая заметка" msgid "NotificationEvent|Reassign issue" -msgstr "NotificationEvent|Переназначить обращение" +msgstr "Переназначить обращение" msgid "NotificationEvent|Reassign merge request" -msgstr "NotificationEvent|Переназначить запрос на слияние" +msgstr "Переназначить запрос на слияние" msgid "NotificationEvent|Reopen issue" -msgstr "NotificationEvent|Переоткрыть обращение" +msgstr "Переоткрыть обращение" msgid "NotificationEvent|Successful pipeline" -msgstr "NotificationEvent|Успешно в конвейере" +msgstr "Успешно в конвейере" msgid "NotificationLevel|Custom" -msgstr "NotificationLevel|Настраиваемый" +msgstr "Настраиваемый" msgid "NotificationLevel|Disabled" -msgstr "NotificationLevel|Отключено" +msgstr "Отключено" msgid "NotificationLevel|Global" -msgstr "NotificationLevel|Глобальный" +msgstr "Глобальный" msgid "NotificationLevel|On mention" -msgstr "NotificationLevel|С упоминанием" +msgstr "С упоминанием" msgid "NotificationLevel|Participate" -msgstr "NotificationLevel|По участию" +msgstr "По участию" msgid "NotificationLevel|Watch" -msgstr "NotificationLevel|Отслеживать" +msgstr "Отслеживать" msgid "OfSearchInADropdown|Filter" -msgstr "OfSearchInADropdown|Фильтр" +msgstr "Фильтр" msgid "OpenedNDaysAgo|Opened" -msgstr "OpenedNDaysAgo|Открыто" +msgstr "Открыто" msgid "Options" msgstr "Настройки" @@ -619,7 +623,7 @@ msgid "Pipeline" msgstr "Конвейер" msgid "Pipeline Health" -msgstr "" +msgstr "Жизненный цикл конвейера" msgid "Pipeline Schedule" msgstr "Расписание конвейера" @@ -628,67 +632,79 @@ msgid "Pipeline Schedules" msgstr "Расписания конвейеров" msgid "PipelineCharts|Failed:" -msgstr "" +msgstr "Неудача:" msgid "PipelineCharts|Overall statistics" -msgstr "" +msgstr "Статистика" msgid "PipelineCharts|Success ratio:" -msgstr "" +msgstr "Коэффициент успеха:" msgid "PipelineCharts|Successful:" -msgstr "" +msgstr "Успех:" msgid "PipelineCharts|Total:" -msgstr "" +msgstr "Всего:" msgid "PipelineSchedules|Activated" -msgstr "PipelineSchedules|Активировано" +msgstr "Активировано" msgid "PipelineSchedules|Active" -msgstr "PipelineSchedules|Активно" +msgstr "Активно" msgid "PipelineSchedules|All" -msgstr "PipelineSchedules|Все" +msgstr "Все" msgid "PipelineSchedules|Inactive" -msgstr "PipelineSchedules|Неактивно" +msgstr "Неактивно" + +msgid "PipelineSchedules|Input variable key" +msgstr "Ввод ключевой переменной" + +msgid "PipelineSchedules|Input variable value" +msgstr "Вставить значение" msgid "PipelineSchedules|Next Run" -msgstr "PipelineSchedules|Следующий запуск" +msgstr "Следующий запуск" msgid "PipelineSchedules|None" -msgstr "PipelineSchedules|None" +msgstr "Отсутствует" msgid "PipelineSchedules|Provide a short description for this pipeline" -msgstr "PipelineSchedules|Предоставьте краткое описание этого конвейера" +msgstr "Предоставьте краткое описание этого конвейера" + +msgid "PipelineSchedules|Remove variable row" +msgstr "Удалить значение" msgid "PipelineSchedules|Take ownership" -msgstr "PipelineSchedules|Стать владельцем" +msgstr "Стать владельцем" msgid "PipelineSchedules|Target" -msgstr "PipelineSchedules|Цель" +msgstr "Цель" + +msgid "PipelineSchedules|Variables" +msgstr "Переменные" msgid "PipelineSheduleIntervalPattern|Custom" -msgstr "PipelineSheduleIntervalPattern|Настраиваемый" +msgstr "Настраиваемый" msgid "Pipelines" -msgstr "" +msgstr "Конвейер" msgid "Pipelines charts" -msgstr "" +msgstr "Диаграмма конвейера" msgid "Pipeline|all" -msgstr "" +msgstr "все" msgid "Pipeline|success" -msgstr "" +msgstr "успех" msgid "Pipeline|with stage" -msgstr "Pipeline|со стадией" +msgstr "со стадией" msgid "Pipeline|with stages" -msgstr "Pipeline|со стадиями" +msgstr "со стадиями" msgid "Project '%{project_name}' queued for deletion." msgstr "Проект '%{project_name}' добавлен в очередь на удаление." @@ -727,49 +743,49 @@ msgid "Project home" msgstr "Домашняя страница проекта" msgid "ProjectFeature|Disabled" -msgstr "ProjectFeature|Отключено" +msgstr "Отключено" msgid "ProjectFeature|Everyone with access" -msgstr "ProjectFeature|Все с доступом" +msgstr "Все с доступом" msgid "ProjectFeature|Only team members" -msgstr "ProjectFeature|Только члены команды" +msgstr "Только члены команды" msgid "ProjectFileTree|Name" -msgstr "ProjectFileTree|Имя" +msgstr "Наименование" msgid "ProjectLastActivity|Never" -msgstr "ProjectLastActivity|Никогда" +msgstr "Никогда" msgid "ProjectLifecycle|Stage" -msgstr "" +msgstr "Этап" msgid "ProjectNetworkGraph|Graph" -msgstr "ProjectNetworkGraph|Граф" +msgstr "Граф" msgid "Read more" -msgstr "" +msgstr "Подробнее" msgid "Readme" msgstr "Readme" msgid "RefSwitcher|Branches" -msgstr "RefSwitcher|Ветки" +msgstr "Ветки" msgid "RefSwitcher|Tags" -msgstr "RefSwitcher|Тэги" +msgstr "Теги" msgid "Related Commits" -msgstr "" +msgstr "Связанные коммиты" msgid "Related Deployed Jobs" -msgstr "" +msgstr "Связанные задачи выгрузки" msgid "Related Issues" -msgstr "" +msgstr "Связанные вопросы" msgid "Related Jobs" -msgstr "" +msgstr "Связанные задачи" msgid "Related Merge Requests" msgstr "Связанные запросы на слияние" @@ -802,7 +818,7 @@ msgid "Scheduling Pipelines" msgstr "Планирование конвейеров" msgid "Search branches and tags" -msgstr "Найти ветки и тэги" +msgstr "Найти ветки и теги" msgid "Select Archive Format" msgstr "Выбрать формат архива" @@ -828,34 +844,34 @@ msgid "Set up auto deploy" msgstr "Настройка автоматического развертывания" msgid "SetPasswordToCloneLink|set a password" -msgstr "SetPasswordToCloneLink|установить пароль" +msgstr "установить пароль" msgid "Showing %d event" msgid_plural "Showing %d events" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Показано %d событие" +msgstr[1] "Показано %d событий" +msgstr[2] "Показано %d событий" msgid "Source code" msgstr "Исходный код" msgid "StarProject|Star" -msgstr "StarProject|Отметить" +msgstr "Отметить" msgid "Start a %{new_merge_request} with these changes" msgstr "Начать %{new_merge_request} с этих изменений" msgid "Switch branch/tag" -msgstr "Переключить ветка/тэг" +msgstr "Переключить ветка/тег" msgid "Tag" msgid_plural "Tags" -msgstr[0] "Тэг" -msgstr[1] "Тэги" -msgstr[2] "Тэги" +msgstr[0] "Тег" +msgstr[1] "теги" +msgstr[2] "Теги" msgid "Tags" -msgstr "Тэги" +msgstr "Теги" msgid "Target Branch" msgstr "Целевая ветка" @@ -865,9 +881,12 @@ msgid "" "request. The data will automatically be added here once you create your " "first merge request." msgstr "" +"На этапе написания кода показывает время первого коммита до создания запроса " +"на слияние. Данные автоматически добавятся после того, как вы создать свой " +"первый запрос на слияние." msgid "The collection of events added to the data gathered for that stage." -msgstr "" +msgstr "Коллекция событий добавленных в данные собранные для этого этапа." msgid "The fork relationship has been removed." msgstr "Связь форка удалена." @@ -890,7 +909,7 @@ msgid "" "project access based on their associated user." msgstr "" "Расписание конвейеров запускает в будущем неоднократно конвейеры, для " -"определенных ветвей или тэгов. Запланированные конвейеры наследуют " +"определенных ветвей или тегов. Запланированные конвейеры наследуют " "ограничения на доступ к проекту на основе связанного с ними пользователя." msgid "" @@ -898,12 +917,18 @@ msgid "" "first commit. This time will be added automatically once you push your first " "commit." msgstr "" +"На этапе планирования показывает время от предыдущего шага до проталкивания " +"первого коммита. Добавляется автоматически, как только проталкиваете свой " +"первый коммит." msgid "" "The production stage shows the total time it takes between creating an issue " "and deploying the code to production. The data will be automatically added " "once you have completed the full idea to production cycle." msgstr "" +"Производственный этап показывает общее время между созданием задачи и " +"развертывание кода в производственной среде. Данные будут автоматически " +"добавлены после полного завершения идеи производственного цикла." msgid "The project can be accessed by any logged in user." msgstr "Доступ к проекту возможен любым зарегистрированным пользователем." @@ -919,27 +944,38 @@ msgid "" "it. The data will automatically be added after you merge your first merge " "request." msgstr "" +"Этап обзора показывает время от создания запроса слияния до его выполнения. " +"Данные будут автоматически добавлены после завершения первого запроса на " +"слияние." msgid "" "The staging stage shows the time between merging the MR and deploying code " "to the production environment. The data will be automatically added once you " "deploy to production for the first time." msgstr "" +"Этап постановки показывает время между слиянием \"MR\" и развертыванием кода " +"в производственной среде. Данные будут автоматически добавлены после " +"развертывания в производстве первый раз." msgid "" "The testing stage shows the time GitLab CI takes to run every pipeline for " "the related merge request. The data will automatically be added after your " "first pipeline finishes running." msgstr "" +"Этап тестирования показывает время, которое GitLab CI занимает для запуска " +"каждого конвейера для соответствующего запроса на слияние. Данные будут " +"автоматически добавлены после завершения работы вашего первого конвейера." msgid "The time taken by each data entry gathered by that stage." -msgstr "" +msgstr "Время, затраченное каждым элементом, собранным на этом этапе." msgid "" "The value lying at the midpoint of a series of observed values. E.g., " "between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 =" " 6." msgstr "" +"Среднее значение в ряду. Пример: между 3, 5, 9, среднее 5, между 3, 5, 7, 8, " +"среднее (5+7)/2 = 6." msgid "" "This means you can not push code until you create an empty repository or " @@ -949,139 +985,139 @@ msgstr "" "репозиторий или не импортируете существующий." msgid "Time before an issue gets scheduled" -msgstr "" +msgstr " Время до начала попадания проблемы в планировщик" msgid "Time before an issue starts implementation" -msgstr "" +msgstr "Время до начала работы над проблемой" msgid "Time between merge request creation and merge/close" msgstr "Время между созданием запроса слияния и слиянием / закрытием" msgid "Time until first merge request" -msgstr "" +msgstr "Время до первого запроса на слияние" msgid "Timeago|%s days ago" -msgstr "Timeago|%s дн(я|ей) назад" +msgstr "%s день назад" msgid "Timeago|%s days remaining" -msgstr "Timeago|Осталось %s дн(я|ей)" +msgstr "Осталось %s день" msgid "Timeago|%s hours remaining" -msgstr "Timeago|Осталось %s часов" +msgstr "Осталось %s часов" msgid "Timeago|%s minutes ago" -msgstr "Timeago|%s минут назад" +msgstr "%s минут назад" msgid "Timeago|%s minutes remaining" -msgstr "Timeago|Осталось %s минут(а|ы)" +msgstr "Осталось %s минут" msgid "Timeago|%s months ago" -msgstr "Timeago|%s минут(а|ы) назад" +msgstr "%s минут назад" msgid "Timeago|%s months remaining" -msgstr "Timeago|Осталось %s месяцев(а)" +msgstr "Осталось %s месяц" msgid "Timeago|%s seconds remaining" -msgstr "Timeago|Осталось %s секунд(ы)" +msgstr "Осталось %s секунд(ы)" msgid "Timeago|%s weeks ago" -msgstr "Timeago|%s недель(и) назад" +msgstr "%s недели назад" msgid "Timeago|%s weeks remaining" -msgstr "Timeago|Осталось %s недель(и)" +msgstr "Осталось %s недели" msgid "Timeago|%s years ago" -msgstr "Timeago|%s лет/года назад" +msgstr "%s год назад" msgid "Timeago|%s years remaining" -msgstr "Timeago|Осталось %s лет/года" +msgstr "Осталось %s год" msgid "Timeago|1 day remaining" -msgstr "Timeago|Остался день" +msgstr "Остался день" msgid "Timeago|1 hour remaining" -msgstr "Timeago|Остался час" +msgstr "Остался час" msgid "Timeago|1 minute remaining" -msgstr "Timeago|Осталась одна минута" +msgstr "Осталась одна минута" msgid "Timeago|1 month remaining" -msgstr "Timeago|Остался месяц" +msgstr "Остался месяц" msgid "Timeago|1 week remaining" -msgstr "Timeago|Осталась неделя" +msgstr "Осталась неделя" msgid "Timeago|1 year remaining" -msgstr "Timeago|Остался год" +msgstr "Остался год" msgid "Timeago|Past due" -msgstr "Timeago|Просрочено" +msgstr "Просрочено" msgid "Timeago|a day ago" -msgstr "Timeago|день назад" +msgstr "день назад" msgid "Timeago|a month ago" -msgstr "Timeago|месяц назад" +msgstr "месяц назад" msgid "Timeago|a week ago" -msgstr "Timeago|неделю назад" +msgstr "неделю назад" msgid "Timeago|a while" -msgstr "Timeago|какое-то время" +msgstr "какое-то время" msgid "Timeago|a year ago" -msgstr "Timeago|год назад" +msgstr "год назад" msgid "Timeago|about %s hours ago" -msgstr "Timeago|около %s часов назад" +msgstr "около %s часов назад" msgid "Timeago|about a minute ago" -msgstr "Timeago|около минуты назад" +msgstr "около минуты назад" msgid "Timeago|about an hour ago" -msgstr "Timeago|около часа назад" +msgstr "около часа назад" msgid "Timeago|in %s days" -msgstr "Timeago|через %s дня(ей)" +msgstr "через %s день" msgid "Timeago|in %s hours" -msgstr "Timeago|через %s часа(ов)" +msgstr "через %s час" msgid "Timeago|in %s minutes" -msgstr "Timeago|через %s минут(ы)" +msgstr "через %s минут" msgid "Timeago|in %s months" -msgstr "Timeago|через %s месяц(а|ев)" +msgstr "через %s месяц" msgid "Timeago|in %s seconds" -msgstr "Timeago|через %s секунд(ы)" +msgstr "через %s секунд(ы)" msgid "Timeago|in %s weeks" -msgstr "Timeago|через %s недели" +msgstr "через %s недели" msgid "Timeago|in %s years" -msgstr "Timeago|через %s лет/года" +msgstr "через %s год" msgid "Timeago|in 1 day" -msgstr "Timeago|через день" +msgstr "через день" msgid "Timeago|in 1 hour" -msgstr "Timeago|через час" +msgstr "через час" msgid "Timeago|in 1 minute" -msgstr "Timeago|через минуту" +msgstr "через минуту" msgid "Timeago|in 1 month" -msgstr "Timeago|через месяц" +msgstr "через месяц" msgid "Timeago|in 1 week" -msgstr "Timeago|через неделю" +msgstr "через неделю" msgid "Timeago|in 1 year" -msgstr "Timeago|через год" +msgstr "через год" msgid "Timeago|less than a minute ago" -msgstr "Timeago|менее чем минуту назад" +msgstr "менее чем минуту назад" msgid "Time|hr" msgid_plural "Time|hrs" @@ -1102,19 +1138,19 @@ msgid "Total Time" msgstr "Общее время" msgid "Total test time for all commits/merges" -msgstr "" +msgstr "Общее время тестирования фиксаций/слияний" msgid "Unstar" msgstr "Снять отметку" msgid "Upload New File" -msgstr "Выгрузить новый файл" +msgstr "Загрузить новый файл" msgid "Upload file" -msgstr "Выгрузить файл" +msgstr "Загрузить файл" msgid "UploadLink|click to upload" -msgstr "UploadLink|кликните для выгрузки" +msgstr "кликните для загрузки" msgid "Use your global notification setting" msgstr "Используются глобальный настройки уведомлений" @@ -1123,23 +1159,32 @@ msgid "View open merge request" msgstr "Просмотреть открытый запрос на слияние" msgid "VisibilityLevel|Internal" -msgstr "VisibilityLevel|Ограниченный" +msgstr "Ограниченный" msgid "VisibilityLevel|Private" -msgstr "VisibilityLevel|Приватный" +msgstr "Приватный" msgid "VisibilityLevel|Public" -msgstr "VisibilityLevel|Публичный" +msgstr "Публичный" msgid "Want to see the data? Please ask an administrator for access." -msgstr "" +msgstr "Хотите увидеть данные? Обратитесь к администратору за доступом." msgid "We don't have enough data to show this stage." -msgstr "" +msgstr "Информация по этапу отсутствует." msgid "Withdraw Access Request" msgstr "Отменить запрос доступа" +msgid "" +"You are going to remove %{group_name}.\n" +"Removed groups CANNOT be restored!\n" +"Are you ABSOLUTELY sure?" +msgstr "" +"Вы собираетесь удалить %{group_name}.\n" +"Удаленные группы НЕ МОГУТ быть восстановлены!\n" +"Вы АБСОЛЮТНО уверены?" + msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po index 2ded61f40d7..c1b99be3433 100644 --- a/locale/uk/gitlab.po +++ b/locale/uk/gitlab.po @@ -9,7 +9,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language-Team: Ukrainian (https://translate.zanata.org/project/view/GitLab)\n" -"PO-Revision-Date: 2017-07-25 03:27-0400\n" +"PO-Revision-Date: 2017-08-01 09:15-0400\n" "Last-Translator: Андрей Витюк \n" "Language: uk\n" "X-Generator: Zanata 3.9.6\n" @@ -504,7 +504,7 @@ msgid "Median" msgstr "Медіана" msgid "MissingSSHKeyWarningLink|add an SSH key" -msgstr "додати SSH ключ" +msgstr "не додасте SSH ключ" msgid "New Issue" msgid_plural "New Issues" @@ -1254,3 +1254,4 @@ msgid_plural "parents" msgstr[0] "джерело" msgstr[1] "джерела" msgstr[2] "джерел" + diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index 8d30a78145d..af663275602 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -1120,10 +1120,7 @@ msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" -msgstr "" -"即將要刪除 %{project_name_with_namespace}。\n" -"被刪除的專案完全無法救回來喔!\n" -"真的「100%確定」要這麼做嗎?" +msgstr "即將要刪除 %{project_name_with_namespace}。被刪除的專案完全無法救回來喔!真的「100%確定」要這麼做嗎?" msgid "" "You are going to remove the fork relationship to source project " From 24704acc77ebc85d653fc22d3fe9c7e5eb2a707d Mon Sep 17 00:00:00 2001 From: sue445 Date: Wed, 2 Aug 2017 15:18:17 +0900 Subject: [PATCH 087/108] Expose target_iid in Events API --- changelogs/unreleased/13247-api_project_events_target_iid.yml | 4 ++++ doc/api/events.md | 2 ++ lib/api/entities.rb | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/13247-api_project_events_target_iid.yml diff --git a/changelogs/unreleased/13247-api_project_events_target_iid.yml b/changelogs/unreleased/13247-api_project_events_target_iid.yml new file mode 100644 index 00000000000..08a31039f77 --- /dev/null +++ b/changelogs/unreleased/13247-api_project_events_target_iid.yml @@ -0,0 +1,4 @@ +--- +title: Expose target_iid in Events API +merge_request: 13247 +author: sue445 diff --git a/doc/api/events.md b/doc/api/events.md index e7829c9f479..6e530317f6c 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -302,6 +302,7 @@ Example response: "project_id":1, "action_name":"opened", "target_id":160, + "target_iid":160, "target_type":"Issue", "author_id":25, "data":null, @@ -322,6 +323,7 @@ Example response: "project_id":1, "action_name":"opened", "target_id":159, + "target_iid":159, "target_type":"Issue", "author_id":21, "data":null, diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 5cdc441e8cb..0a71c976c7e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -483,7 +483,7 @@ module API class Event < Grape::Entity expose :title, :project_id, :action_name - expose :target_id, :target_type, :author_id + expose :target_id, :target_iid, :target_type, :author_id expose :data, :target_title expose :created_at expose :note, using: Entities::Note, if: ->(event, options) { event.note? } From e67c4a6d913ccdb4615639cb317634141da2a37b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 2 Aug 2017 08:46:25 +0100 Subject: [PATCH 088/108] changed variable names --- app/assets/javascripts/fly_out_nav.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index ae04826896b..3f64c16a79d 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -7,32 +7,32 @@ export const calculateTop = (boundingRect, outerHeight) => { }; export const showSubLevelItems = (el) => { - const $subitems = el.querySelector('.sidebar-sub-level-items'); + const subItems = el.querySelector('.sidebar-sub-level-items'); - if (!$subitems) return; + if (!subItems) return; - $subitems.style.display = 'block'; + subItems.style.display = 'block'; el.classList.add('is-over'); const boundingRect = el.getBoundingClientRect(); - const top = calculateTop(boundingRect, $subitems.offsetHeight); + const top = calculateTop(boundingRect, subItems.offsetHeight); const isAbove = top < boundingRect.top; - $subitems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; + subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; if (isAbove) { - $subitems.classList.add('is-above'); + subItems.classList.add('is-above'); } }; export const hideSubLevelItems = (el) => { - const $subitems = el.querySelector('.sidebar-sub-level-items'); + const subItems = el.querySelector('.sidebar-sub-level-items'); - if (!$subitems) return; + if (!subItems) return; el.classList.remove('is-over'); - $subitems.style.display = 'none'; - $subitems.classList.remove('is-above'); + subItems.style.display = 'none'; + subItems.classList.remove('is-above'); }; export default () => { From e7d6af729f14b487a238da3c62c3dbde3b29bbb8 Mon Sep 17 00:00:00 2001 From: kushalpandya Date: Wed, 2 Aug 2017 14:44:46 +0530 Subject: [PATCH 089/108] Remove unnecessary name property --- app/assets/javascripts/groups/components/group_identicon.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/groups/components/group_identicon.vue b/app/assets/javascripts/groups/components/group_identicon.vue index ba921a177e9..0edd820743f 100644 --- a/app/assets/javascripts/groups/components/group_identicon.vue +++ b/app/assets/javascripts/groups/components/group_identicon.vue @@ -1,6 +1,5 @@