From 26c69c5df8bf7800e16a2d12d207f2ae8779fb9e Mon Sep 17 00:00:00 2001 From: winniehell Date: Sat, 5 Nov 2016 12:28:48 +0100 Subject: [PATCH 001/386] Display error code for U2F errors (!7305) --- app/assets/javascripts/u2f/authenticate.js | 3 ++- app/assets/javascripts/u2f/error.js | 1 - app/assets/javascripts/u2f/register.js | 3 ++- app/views/u2f/_authenticate.html.haml | 2 +- app/views/u2f/_register.html.haml | 2 +- changelogs/unreleased/remove-u2f-error-logging.yml | 4 ++++ 6 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/remove-u2f-error-logging.yml diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js index 35f2b1e2b25..6f0dbca96ca 100644 --- a/app/assets/javascripts/u2f/authenticate.js +++ b/app/assets/javascripts/u2f/authenticate.js @@ -85,7 +85,8 @@ U2FAuthenticate.prototype.renderError = function(error) { this.renderTemplate('error', { - error_message: error.message() + error_message: error.message(), + error_code: error.errorCode }); return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); }; diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js index aff605169e4..fab4165efe5 100644 --- a/app/assets/javascripts/u2f/error.js +++ b/app/assets/javascripts/u2f/error.js @@ -7,7 +7,6 @@ this.errorCode = errorCode; this.message = bind(this.message, this); this.httpsDisabled = window.location.protocol !== 'https:'; - console.error("U2F Error Code: " + this.errorCode); } U2FError.prototype.message = function() { diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js index 22fbf9f3a91..3e0a2d53bd2 100644 --- a/app/assets/javascripts/u2f/register.js +++ b/app/assets/javascripts/u2f/register.js @@ -72,7 +72,8 @@ U2FRegister.prototype.renderError = function(error) { this.renderTemplate('error', { - error_message: error.message() + error_message: error.message(), + error_code: error.errorCode }); return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); }; diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml index 232ca26c1af..fa998c91f72 100644 --- a/app/views/u2f/_authenticate.html.haml +++ b/app/views/u2f/_authenticate.html.haml @@ -13,7 +13,7 @@ %script#js-authenticate-u2f-error{ type: "text/template" } %div - %p <%= error_message %> + %p <%= error_message %> (error code: <%= error_code %>) %a.btn.btn-warning#js-u2f-try-again Try again? %script#js-authenticate-u2f-authenticated{ type: "text/template" } diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index 8f7b42eb351..fcc33f04237 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -23,7 +23,7 @@ %script#js-register-u2f-error{ type: "text/template" } %div %p - %span <%= error_message %> + %span <%= error_message %> (error code: <%= error_code %>) %a.btn.btn-warning#js-u2f-try-again Try again? %script#js-register-u2f-registered{ type: "text/template" } diff --git a/changelogs/unreleased/remove-u2f-error-logging.yml b/changelogs/unreleased/remove-u2f-error-logging.yml new file mode 100644 index 00000000000..edbe576a976 --- /dev/null +++ b/changelogs/unreleased/remove-u2f-error-logging.yml @@ -0,0 +1,4 @@ +--- +title: Display error code for U2F errors +merge_request: 7305 +author: winniehell From 6970c1f331e1f56424ff90fe43113d9f512bce95 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 2 Nov 2016 16:41:32 +0100 Subject: [PATCH 002/386] Allow to use Dockerfile templates --- app/assets/javascripts/api.js | 5 +++ .../javascripts/blob/blob_ci_yaml.js.es6 | 36 +++++++++++++++++++ app/assets/javascripts/blob_edit/edit_blob.js | 3 ++ app/assets/stylesheets/pages/editor.scss | 6 ++-- app/helpers/blob_helper.rb | 4 +++ app/views/projects/blob/_editor.html.haml | 2 ++ lib/api/templates.rb | 11 +++--- lib/gitlab/template/dockerfile_template.rb | 30 ++++++++++++++++ vendor/dockerfile/HTTPdDockerfile | 3 ++ 9 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 lib/gitlab/template/dockerfile_template.rb create mode 100644 vendor/dockerfile/HTTPdDockerfile diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 1cab66e109e..291efd2e286 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -10,6 +10,7 @@ licensePath: "/api/:version/templates/licenses/:key", gitignorePath: "/api/:version/templates/gitignores/:key", gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", + dockerfilePath: "/api/:version/dockerfiles/:key", issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", group: function(group_id, callback) { var url = Api.buildUrl(Api.groupPath) @@ -119,6 +120,10 @@ return callback(file); }); }, + dockerfileYml: function(key, callback) { + var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); + $.get(url, callback); + }, issueTemplate: function(namespacePath, projectPath, key, type, callback) { var url = Api.buildUrl(Api.issuableTemplatePath) .replace(':key', key) diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js.es6 b/app/assets/javascripts/blob/blob_ci_yaml.js.es6 index 37531aaec9b..62e686b8d9b 100644 --- a/app/assets/javascripts/blob/blob_ci_yaml.js.es6 +++ b/app/assets/javascripts/blob/blob_ci_yaml.js.es6 @@ -38,4 +38,40 @@ global.BlobCiYamlSelectors = BlobCiYamlSelectors; + class BlobDockerfileSelector extends gl.TemplateSelector { + requestFile(query) { + return Api.dockerfileYml(query.name, this.requestFileSuccess.bind(this)); + } + + requestFileSuccess(file) { + return super.requestFileSuccess(file); + } + } + + global.BlobDockerfileSelector = BlobDockerfileSelector; + + class BlobDockerfileSelectors { + constructor({ editor, $dropdowns } = {}) { + this.editor = editor; + this.$dropdowns = $dropdowns || $('.js-dockerfile-selector'); + this.initSelectors(); + } + + initSelectors() { + const editor = this.editor; + this.$dropdowns.each((i, dropdown) => { + const $dropdown = $(dropdown); + return new BlobDockerfileSelector({ + editor, + pattern: /(Dockerfile)/, + data: $dropdown.data('data'), + wrapper: $dropdown.closest('.js-dockerfile-selector-wrap'), + dropdown: $dropdown + }); + }); + } + } + + global.BlobDockerfileSelectors = BlobDockerfileSelectors; + })(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index 60840560dd3..11dff7dfab4 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -33,6 +33,9 @@ new gl.BlobCiYamlSelectors({ editor: this.editor }); + new gl.BlobDockerfileSelectors({ + editor: this.editor + }); } EditBlob.prototype.initModePanesAndLinks = function() { diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 778126bcfb7..ac968618c79 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -67,7 +67,8 @@ .soft-wrap-toggle, .license-selector, .gitignore-selector, - .gitlab-ci-yml-selector { + .gitlab-ci-yml-selector, + .dockerfile-selector { display: inline-block; vertical-align: top; font-family: $regular_font; @@ -97,7 +98,8 @@ .gitignore-selector, .license-selector, - .gitlab-ci-yml-selector { + .gitlab-ci-yml-selector, + .dockerfile-selector { .dropdown { line-height: 21px; } diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 07ff6fb9488..f31d4fb897d 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -191,6 +191,10 @@ module BlobHelper @gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names end + def dockerfile_names + @dockerfile_names ||= Gitlab::Template::DockerfileTemplate.dropdown_names + end + def blob_editor_paths { 'relative-url-root' => Rails.application.config.relative_url_root, diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 4a6aa92e3f3..34dc96bcb64 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -21,6 +21,8 @@ = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'btn js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) + .gitlab-ci-yml-selector.js-dockerfile-selector-wrap.hidden + = dropdown_tag("Choose a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) = button_tag class: 'soft-wrap-toggle btn', type: 'button' do %span.no-wrap = custom_icon('icon_no_wrap') diff --git a/lib/api/templates.rb b/lib/api/templates.rb index 8a53d9c0095..442fef1a848 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -8,7 +8,8 @@ module API gitlab_ci_ymls: { klass: Gitlab::Template::GitlabCiYmlTemplate, gitlab_version: 8.9 - } + }, + dockerfiles: Gitlab::Template::DockerfileTemplate }.freeze PROJECT_TEMPLATE_REGEX = /[\<\{\[] @@ -51,7 +52,7 @@ module API end params do optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' - end + end get route do options = { featured: declared(params).popular.present? ? true : nil @@ -69,7 +70,7 @@ module API end params do requires :name, type: String, desc: 'The name of the template' - end + end get route, requirements: { name: /[\w\.-]+/ } do not_found!('License') unless Licensee::License.find(declared(params).name) @@ -78,7 +79,7 @@ module API present template, with: Entities::RepoLicense end end - + GLOBAL_TEMPLATE_TYPES.each do |template_type, properties| klass = properties[:klass] gitlab_version = properties[:gitlab_version] @@ -104,7 +105,7 @@ module API end params do requires :name, type: String, desc: 'The name of the template' - end + end get route do new_template = klass.find(declared(params).name) diff --git a/lib/gitlab/template/dockerfile_template.rb b/lib/gitlab/template/dockerfile_template.rb new file mode 100644 index 00000000000..d5d3e045a42 --- /dev/null +++ b/lib/gitlab/template/dockerfile_template.rb @@ -0,0 +1,30 @@ +module Gitlab + module Template + class DockerfileTemplate < BaseTemplate + def content + explanation = "# This file is a template, and might need editing before it works on your project." + [explanation, super].join("\n") + end + + class << self + def extension + 'Dockerfile' + end + + def categories + { + "General" => '' + } + end + + def base_dir + Rails.root.join('vendor/dockerfile') + end + + def finder(project = nil) + Gitlab::Template::Finders::GlobalTemplateFinder.new(self.base_dir, self.extension, self.categories) + end + end + end + end +end diff --git a/vendor/dockerfile/HTTPdDockerfile b/vendor/dockerfile/HTTPdDockerfile new file mode 100644 index 00000000000..2f05427323c --- /dev/null +++ b/vendor/dockerfile/HTTPdDockerfile @@ -0,0 +1,3 @@ +FROM httpd:alpine + +COPY ./ /usr/local/apache2/htdocs/ From dcd20236ec0f05b46702611f09e2af8094a383ae Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 8 Nov 2016 12:42:58 +0000 Subject: [PATCH 003/386] Refactored JS Added spec --- .../javascripts/blob/blob_ci_yaml.js.es6 | 36 ------------------- .../blob/blob_dockerfile_selector.js.es6 | 18 ++++++++++ .../blob/blob_dockerfile_selectors.js.es6 | 27 ++++++++++++++ app/assets/javascripts/blob_edit/edit_blob.js | 6 ++-- app/views/projects/blob/_editor.html.haml | 4 +-- lib/api/templates.rb | 5 ++- .../files/dockerfile_dropdown_spec.rb | 30 ++++++++++++++++ 7 files changed, 84 insertions(+), 42 deletions(-) create mode 100644 app/assets/javascripts/blob/blob_dockerfile_selector.js.es6 create mode 100644 app/assets/javascripts/blob/blob_dockerfile_selectors.js.es6 create mode 100644 spec/features/projects/files/dockerfile_dropdown_spec.rb diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js.es6 b/app/assets/javascripts/blob/blob_ci_yaml.js.es6 index 62e686b8d9b..37531aaec9b 100644 --- a/app/assets/javascripts/blob/blob_ci_yaml.js.es6 +++ b/app/assets/javascripts/blob/blob_ci_yaml.js.es6 @@ -38,40 +38,4 @@ global.BlobCiYamlSelectors = BlobCiYamlSelectors; - class BlobDockerfileSelector extends gl.TemplateSelector { - requestFile(query) { - return Api.dockerfileYml(query.name, this.requestFileSuccess.bind(this)); - } - - requestFileSuccess(file) { - return super.requestFileSuccess(file); - } - } - - global.BlobDockerfileSelector = BlobDockerfileSelector; - - class BlobDockerfileSelectors { - constructor({ editor, $dropdowns } = {}) { - this.editor = editor; - this.$dropdowns = $dropdowns || $('.js-dockerfile-selector'); - this.initSelectors(); - } - - initSelectors() { - const editor = this.editor; - this.$dropdowns.each((i, dropdown) => { - const $dropdown = $(dropdown); - return new BlobDockerfileSelector({ - editor, - pattern: /(Dockerfile)/, - data: $dropdown.data('data'), - wrapper: $dropdown.closest('.js-dockerfile-selector-wrap'), - dropdown: $dropdown - }); - }); - } - } - - global.BlobDockerfileSelectors = BlobDockerfileSelectors; - })(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/blob/blob_dockerfile_selector.js.es6 b/app/assets/javascripts/blob/blob_dockerfile_selector.js.es6 new file mode 100644 index 00000000000..bdf95017613 --- /dev/null +++ b/app/assets/javascripts/blob/blob_dockerfile_selector.js.es6 @@ -0,0 +1,18 @@ +/* global Api */ +/*= require blob/template_selector */ + +(() => { + const global = window.gl || (window.gl = {}); + + class BlobDockerfileSelector extends gl.TemplateSelector { + requestFile(query) { + return Api.dockerfileYml(query.name, this.requestFileSuccess.bind(this)); + } + + requestFileSuccess(file) { + return super.requestFileSuccess(file); + } + } + + global.BlobDockerfileSelector = BlobDockerfileSelector; +})(); diff --git a/app/assets/javascripts/blob/blob_dockerfile_selectors.js.es6 b/app/assets/javascripts/blob/blob_dockerfile_selectors.js.es6 new file mode 100644 index 00000000000..9cee79fa5d5 --- /dev/null +++ b/app/assets/javascripts/blob/blob_dockerfile_selectors.js.es6 @@ -0,0 +1,27 @@ +(() => { + const global = window.gl || (window.gl = {}); + + class BlobDockerfileSelectors { + constructor({ editor, $dropdowns } = {}) { + this.editor = editor; + this.$dropdowns = $dropdowns || $('.js-dockerfile-selector'); + this.initSelectors(); + } + + initSelectors() { + const editor = this.editor; + this.$dropdowns.each((i, dropdown) => { + const $dropdown = $(dropdown); + return new gl.BlobDockerfileSelector({ + editor, + pattern: /(Dockerfile)/, + data: $dropdown.data('data'), + wrapper: $dropdown.closest('.js-dockerfile-selector-wrap'), + dropdown: $dropdown, + }); + }); + } + } + + global.BlobDockerfileSelectors = BlobDockerfileSelectors; +})(); diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index 11dff7dfab4..74cc0af2486 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -33,9 +33,9 @@ new gl.BlobCiYamlSelectors({ editor: this.editor }); - new gl.BlobDockerfileSelectors({ - editor: this.editor - }); + new gl.BlobDockerfileSelectors({ + editor: this.editor + }); } EditBlob.prototype.initModePanesAndLinks = function() { diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 34dc96bcb64..1d058daa094 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -21,8 +21,8 @@ = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'btn js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) - .gitlab-ci-yml-selector.js-dockerfile-selector-wrap.hidden - = dropdown_tag("Choose a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) + .dockerfile-selector.js-dockerfile-selector-wrap.hidden + = dropdown_tag("Choose a Dockerfile template", options: { toggle_class: 'btn js-dockerfile-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) = button_tag class: 'soft-wrap-toggle btn', type: 'button' do %span.no-wrap = custom_icon('icon_no_wrap') diff --git a/lib/api/templates.rb b/lib/api/templates.rb index 442fef1a848..2d887e15f28 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -9,7 +9,10 @@ module API klass: Gitlab::Template::GitlabCiYmlTemplate, gitlab_version: 8.9 }, - dockerfiles: Gitlab::Template::DockerfileTemplate + dockerfiles: { + klass: Gitlab::Template::DockerfileTemplate, + gitlab_version: 8.9 + } }.freeze PROJECT_TEMPLATE_REGEX = /[\<\{\[] diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb new file mode 100644 index 00000000000..32f33a3ca97 --- /dev/null +++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +feature 'User wants to add a Dockerfile file', feature: true do + include WaitForAjax + + before do + user = create(:user) + project = create(:project) + project.team << [user, :master] + login_as user + visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: 'Dockerfile') + end + + scenario 'user can see Dockerfile dropdown' do + expect(page).to have_css('.dockerfile-selector') + end + + scenario 'user can pick a Dockerfile file from the dropdown', js: true do + find('.js-dockerfile-selector').click + wait_for_ajax + within '.dockerfile-selector' do + find('.dropdown-input-field').set('HTTPd') + find('.dropdown-content li', text: 'HTTPd').click + end + wait_for_ajax + + expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'HTTPd') + expect(page).to have_content('COPY ./ /usr/local/apache2/htdocs/') + end +end From 69aaa972038c7b285c06804e04ff5932e468e523 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 19 Aug 2016 17:59:58 -0300 Subject: [PATCH 004/386] Remove omniauth-bitbucket gem --- Gemfile | 1 - Gemfile.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/Gemfile b/Gemfile index 9e815925a1f..3576a670f93 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,6 @@ gem 'doorkeeper', '~> 4.2.0' gem 'omniauth', '~> 1.3.1' gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-azure-oauth2', '~> 0.0.6' -gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-cas3', '~> 1.1.2' gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-github', '~> 1.1.1' diff --git a/Gemfile.lock b/Gemfile.lock index bdc60552480..af1facf255a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -438,10 +438,6 @@ GEM jwt (~> 1.0) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-bitbucket (0.0.2) - multi_json (~> 1.7) - omniauth (~> 1.1) - omniauth-oauth (~> 1.0) omniauth-cas3 (1.1.3) addressable (~> 2.3) nokogiri (~> 1.6.6) @@ -905,7 +901,6 @@ DEPENDENCIES omniauth (~> 1.3.1) omniauth-auth0 (~> 1.4.1) omniauth-azure-oauth2 (~> 0.0.6) - omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 4.0.0) omniauth-github (~> 1.1.1) From a2ef52b32bd3d1ee70ea333566e330c44e6c9170 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 15:36:41 -0300 Subject: [PATCH 005/386] Add custom OmniAuth strategy for Bitbucket OAuth2 --- config/initializers/omniauth.rb | 6 ++++ lib/omniauth/strategies/bitbucket.rb | 45 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 lib/omniauth/strategies/bitbucket.rb diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 26c30e523a7..ab5a0561b8c 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -26,3 +26,9 @@ if Gitlab.config.omniauth.enabled end end end + +module OmniAuth + module Strategies + autoload :Bitbucket, Rails.root.join('lib', 'omniauth', 'strategies', 'bitbucket') + end +end diff --git a/lib/omniauth/strategies/bitbucket.rb b/lib/omniauth/strategies/bitbucket.rb new file mode 100644 index 00000000000..2006e7582ce --- /dev/null +++ b/lib/omniauth/strategies/bitbucket.rb @@ -0,0 +1,45 @@ +require 'omniauth-oauth2' + +module OmniAuth + module Strategies + class Bitbucket < OmniAuth::Strategies::OAuth2 + option :name, 'bitbucket' + + option :client_options, { + site: 'https://bitbucket.org', + authorize_url: 'https://bitbucket.org/site/oauth2/authorize', + token_url: 'https://bitbucket.org/site/oauth2/access_token' + } + + def callback_url + full_host + script_name + callback_path + end + + uid do + raw['username'] + end + + info do + { + name: raw_info['display_name'], + avatar: raw_info['links']['avatar']['href'], + email: primary_email + } + end + + def raw_info + @raw_info ||= access_token.get('user').parsed + end + + def primary_email + primary = emails.find{ |i| i['is_primary'] && i['is_confirmed'] } + primary && primary['email'] || nil + end + + def emails + email_response = access_token.get('user/emails').parsed + @emails ||= email_response && email_response['values'] || nil + end + end + end +end From fc34c9560324b7db5bdaf205cbdf46a339102af2 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 15:42:26 -0300 Subject: [PATCH 006/386] Add a Bitbucket client for the OAuth2 API --- lib/bitbucket/client.rb | 11 ++++++ lib/bitbucket/connection.rb | 69 +++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 lib/bitbucket/client.rb create mode 100644 lib/bitbucket/connection.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb new file mode 100644 index 00000000000..5f48e52da98 --- /dev/null +++ b/lib/bitbucket/client.rb @@ -0,0 +1,11 @@ +module Bitbucket + class Client + def initialize(options = {}) + @connection = options.fetch(:connection, Connection.new(options)) + end + + private + + attr_reader :connection + end +end diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb new file mode 100644 index 00000000000..00f127f9507 --- /dev/null +++ b/lib/bitbucket/connection.rb @@ -0,0 +1,69 @@ +module Bitbucket + class Connection + DEFAULT_API_VERSION = '2.0' + DEFAULT_BASE_URI = 'https://api.bitbucket.org/' + DEFAULT_QUERY = {} + + def initialize(options = {}) + @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) + @base_uri = options.fetch(:base_uri, DEFAULT_BASE_URI) + @query = options.fetch(:query, DEFAULT_QUERY) + + @token = options.fetch(:token) + @expires_at = options.fetch(:expires_at) + @expires_in = options.fetch(:expires_in) + @refresh_token = options.fetch(:refresh_token) + + @client = OAuth2::Client.new(provider.app_id, provider.app_secret, options) + @connection = OAuth2::AccessToken.new(@client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) + end + + def query(params = {}) + @query.update(params) + end + + def get(path, query = {}) + refresh! if expired? + + response = connection.get(build_url(path), params: @query.merge(query)) + response.parsed + end + + def expired? + connection.expired? + end + + def refresh! + response = connection.refresh! + + @token = response.token + @expires_at = response.expires_at + @expires_in = response.expires_in + @refresh_token = response.refresh_token + + @connection = OAuth2::AccessToken.new(@client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) + end + + private + + attr_reader :connection, :expires_at, :expires_in, :refresh_token, :token + + def build_url(path) + return path if path.starts_with?(root_url) + + "#{root_url}#{path}" + end + + def root_url + @root_url ||= "#{@base_uri}#{@api_version}" + end + + def provider + Gitlab.config.omniauth.providers.find { |provider| provider.name == 'bitbucket' } + end + + def options + OmniAuth::Strategies::Bitbucket.default_options[:client_options].deep_symbolize_keys + end + end +end From f1f5863bfc5727dba4767a54a299b0cf526b025a Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 15:45:29 -0300 Subject: [PATCH 007/386] Add an endpoint to get the Bitbucket user profile --- lib/bitbucket/client.rb | 5 +++++ lib/bitbucket/representation/base.rb | 13 +++++++++++++ lib/bitbucket/representation/user.rb | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 lib/bitbucket/representation/base.rb create mode 100644 lib/bitbucket/representation/user.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 5f48e52da98..c05fc35f36e 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -4,6 +4,11 @@ module Bitbucket @connection = options.fetch(:connection, Connection.new(options)) end + def user + parsed_response = connection.get('/user') + Representation::User.new(parsed_response) + end + private attr_reader :connection diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb new file mode 100644 index 00000000000..7b639492d38 --- /dev/null +++ b/lib/bitbucket/representation/base.rb @@ -0,0 +1,13 @@ +module Bitbucket + module Representation + class Base + def initialize(raw) + @raw = raw + end + + private + + attr_reader :raw + end + end +end diff --git a/lib/bitbucket/representation/user.rb b/lib/bitbucket/representation/user.rb new file mode 100644 index 00000000000..ba6b7667b49 --- /dev/null +++ b/lib/bitbucket/representation/user.rb @@ -0,0 +1,9 @@ +module Bitbucket + module Representation + class User < Representation::Base + def username + raw['username'] + end + end + end +end From e2f7f32a60a7663d12b5dae0320f640150f354e7 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 15:48:31 -0300 Subject: [PATCH 008/386] Add support for Bitbucket pagination when fetching collections --- lib/bitbucket/collection.rb | 21 ++++++++++++++++++++ lib/bitbucket/page.rb | 36 +++++++++++++++++++++++++++++++++++ lib/bitbucket/paginator.rb | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 lib/bitbucket/collection.rb create mode 100644 lib/bitbucket/page.rb create mode 100644 lib/bitbucket/paginator.rb diff --git a/lib/bitbucket/collection.rb b/lib/bitbucket/collection.rb new file mode 100644 index 00000000000..9cc8947417c --- /dev/null +++ b/lib/bitbucket/collection.rb @@ -0,0 +1,21 @@ +module Bitbucket + class Collection < Enumerator + def initialize(paginator) + super() do |yielder| + loop do + paginator.next.each { |item| yielder << item } + end + end + + lazy + end + + def method_missing(method, *args) + return super unless self.respond_to?(method) + + self.send(method, *args) do |item| + block_given? ? yield(item) : item + end + end + end +end diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb new file mode 100644 index 00000000000..ad9a2baba36 --- /dev/null +++ b/lib/bitbucket/page.rb @@ -0,0 +1,36 @@ +module Bitbucket + class Page + attr_reader :attrs, :items + + def initialize(raw, type) + @attrs = parse_attrs(raw) + @items = parse_values(raw, representation_class(type)) + end + + def next? + attrs.fetch(:next, false) + end + + def next + attrs.fetch(:next) + end + + private + + def parse_attrs(raw) + attrs = %w(size page pagelen next previous) + attrs.map { |attr| { attr.to_sym => raw[attr] } }.reduce(&:merge) + end + + def parse_values(raw, representation_class) + return [] if raw['values'].nil? || !raw['values'].is_a?(Array) + + raw['values'].map { |hash| representation_class.new(hash) } + end + + def representation_class(type) + class_name = "Bitbucket::Representation::#{type.to_s.camelize}" + class_name.constantize + end + end +end diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb new file mode 100644 index 00000000000..a1672d9eaa1 --- /dev/null +++ b/lib/bitbucket/paginator.rb @@ -0,0 +1,38 @@ +module Bitbucket + class Paginator + PAGE_LENGTH = 50 # The minimum length is 10 and the maximum is 100. + + def initialize(connection, url, type) + @connection = connection + @type = type + @url = url + @page = nil + + connection.query(pagelen: PAGE_LENGTH, sort: :created_on) + end + + def next + raise StopIteration unless has_next_page? + + @page = fetch_next_page + @page.items + end + + private + + attr_reader :connection, :page, :url, :type + + def has_next_page? + page.nil? || page.next? + end + + def page_url + page.nil? ? url : page.next + end + + def fetch_next_page + parsed_response = connection.get(page_url) + Page.new(parsed_response, type) + end + end +end From 6418c6f88efe9015c8bc2ebd4f7db1a7277a4dc9 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 15:53:46 -0300 Subject: [PATCH 009/386] Add an endpoint to get the user's repositories --- lib/bitbucket/client.rb | 14 ++++++- lib/bitbucket/representation/repo.rb | 57 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 lib/bitbucket/representation/repo.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index c05fc35f36e..39b52ae25a6 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -4,9 +4,19 @@ module Bitbucket @connection = options.fetch(:connection, Connection.new(options)) end + + def repos + relative_path = "/repositories/#{user.username}" + paginator = Paginator.new(connection, relative_path, :repo) + + Collection.new(paginator) + end + def user - parsed_response = connection.get('/user') - Representation::User.new(parsed_response) + @user ||= begin + parsed_response = connection.get('/user') + Representation::User.new(parsed_response) + end end private diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb new file mode 100644 index 00000000000..fe5cda66ab9 --- /dev/null +++ b/lib/bitbucket/representation/repo.rb @@ -0,0 +1,57 @@ +module Bitbucket + module Representation + class Repo < Representation::Base + attr_reader :owner, :slug + + def initialize(raw) + super(raw) + + if full_name && full_name.split('/').size == 2 + @owner, @slug = full_name.split('/') + end + end + + def clone_url(token = nil) + url = raw['links']['clone'].find { |link| link['name'] == 'https' }.fetch('href') + + if token.present? + url.sub(/^[^\@]*/, "https://x-token-auth:#{token}") + else + url + end + end + + def description + raw['description'] + end + + def full_name + raw['full_name'] + end + + def has_issues? + raw['has_issues'] + end + + def name + raw['name'] + end + + def valid? + raw['scm'] == 'git' + end + + def visibility_level + if raw['is_private'] + Gitlab::VisibilityLevel::PRIVATE + else + Gitlab::VisibilityLevel::PUBLIC + end + end + + def to_s + full_name + end + end + end +end From 411d0a93723467eb355155fe52f1d159d4527556 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 15:55:08 -0300 Subject: [PATCH 010/386] Add an endpoint to get a single user repository --- lib/bitbucket/client.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 39b52ae25a6..24984ca0793 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -5,6 +5,11 @@ module Bitbucket end + def repo(name) + parsed_response = connection.get("/repositories/#{name}") + Representation::Repo.new(parsed_response) + end + def repos relative_path = "/repositories/#{user.username}" paginator = Paginator.new(connection, relative_path, :repo) From 56cb4762d42f758ad6e4ec1874b7eed8e1c1f687 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:02:48 -0300 Subject: [PATCH 011/386] Refactoring Bitbucket import controller to use the new OAuth2 client --- .../import/bitbucket_controller.rb | 79 +++++++++++-------- app/views/import/bitbucket/status.html.haml | 35 ++++---- lib/bitbucket/error/unauthorized.rb | 6 ++ 3 files changed, 68 insertions(+), 52 deletions(-) create mode 100644 lib/bitbucket/error/unauthorized.rb diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 6ea54744da8..ee30a24ab77 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -3,49 +3,54 @@ class Import::BitbucketController < Import::BaseController before_action :bitbucket_auth, except: :callback rescue_from OAuth::Error, with: :bitbucket_unauthorized - rescue_from Gitlab::BitbucketImport::Client::Unauthorized, with: :bitbucket_unauthorized + rescue_from Bitbucket::Error::Unauthorized, with: :bitbucket_unauthorized def callback - request_token = session.delete(:oauth_request_token) - raise "Session expired!" if request_token.nil? + response = client.auth_code.get_token(params[:code], redirect_uri: callback_import_bitbucket_url) - request_token.symbolize_keys! - - access_token = client.get_token(request_token, params[:oauth_verifier], callback_import_bitbucket_url) - - session[:bitbucket_access_token] = access_token.token - session[:bitbucket_access_token_secret] = access_token.secret + session[:bitbucket_token] = response.token + session[:bitbucket_expires_at] = response.expires_at + session[:bitbucket_expires_in] = response.expires_in + session[:bitbucket_refresh_token] = response.refresh_token redirect_to status_import_bitbucket_url end def status - @repos = client.projects - @incompatible_repos = client.incompatible_projects + client = Bitbucket::Client.new(credentials) + repos = client.repos - @already_added_projects = current_user.created_projects.where(import_type: "bitbucket") + @repos = repos.select(&:valid?) + @incompatible_repos = repos.reject(&:valid?) + + @already_added_projects = current_user.created_projects.where(import_type: 'bitbucket') already_added_projects_names = @already_added_projects.pluck(:import_source) - @repos.to_a.reject!{ |repo| already_added_projects_names.include? "#{repo["owner"]}/#{repo["slug"]}" } + @repos.to_a.reject! { |repo| already_added_projects_names.include?(repo.full_name) } end def jobs - jobs = current_user.created_projects.where(import_type: "bitbucket").to_json(only: [:id, :import_status]) - render json: jobs + render json: current_user.created_projects + .where(import_type: 'bitbucket') + .to_json(only: [:id, :import_status]) end def create - @repo_id = params[:repo_id].to_s - repo = client.project(@repo_id.gsub('___', '/')) - @project_name = repo['slug'] - @target_namespace = find_or_create_namespace(repo['owner'], client.user['user']['username']) + client = Bitbucket::Client.new(credentials) - unless Gitlab::BitbucketImport::KeyAdder.new(repo, current_user, access_params).execute - render 'deploy_key' and return - end + @repo_id = params[:repo_id].to_s + name = @repo_id.to_s.gsub('___', '/') + repo = client.repo(name) + @project_name = repo.name + + repo_owner = repo.owner + repo_owner = current_user.username if repo_owner == client.user.username + @target_namespace = params[:new_namespace].presence || repo_owner + + namespace = find_or_create_namespace(target_namespace_name, repo_owner) if current_user.can?(:create_projects, @target_namespace) - @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @target_namespace, current_user, access_params).execute + @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, current_user, credentials).execute else render 'unauthorized' end @@ -54,8 +59,15 @@ class Import::BitbucketController < Import::BaseController private def client - @client ||= Gitlab::BitbucketImport::Client.new(session[:bitbucket_access_token], - session[:bitbucket_access_token_secret]) + @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options) + end + + def provider + Gitlab.config.omniauth.providers.find { |provider| provider.name == 'bitbucket' } + end + + def options + OmniAuth::Strategies::Bitbucket.default_options[:client_options].deep_symbolize_keys end def verify_bitbucket_import_enabled @@ -63,26 +75,23 @@ class Import::BitbucketController < Import::BaseController end def bitbucket_auth - if session[:bitbucket_access_token].blank? - go_to_bitbucket_for_permissions - end + go_to_bitbucket_for_permissions if session[:bitbucket_token].blank? end def go_to_bitbucket_for_permissions - request_token = client.request_token(callback_import_bitbucket_url) - session[:oauth_request_token] = request_token - - redirect_to client.authorize_url(request_token, callback_import_bitbucket_url) + redirect_to client.auth_code.authorize_url(redirect_uri: callback_import_bitbucket_url) end def bitbucket_unauthorized go_to_bitbucket_for_permissions end - def access_params + def credentials { - bitbucket_access_token: session[:bitbucket_access_token], - bitbucket_access_token_secret: session[:bitbucket_access_token_secret] + token: session[:bitbucket_token], + expires_at: session[:bitbucket_expires_at], + expires_in: session[:bitbucket_expires_in], + refresh_token: session[:bitbucket_refresh_token] } end end diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index f8b4b107513..e62ff5f61f6 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -1,5 +1,6 @@ -- page_title "Bitbucket import" -- header_title "Projects", root_path +- page_title 'Bitbucket import' +- header_title 'Projects', root_path + %h3.page-title %i.fa.fa-bitbucket Import projects from Bitbucket @@ -10,13 +11,13 @@ %hr %p - if @incompatible_repos.any? - = button_tag class: "btn btn-import btn-success js-import-all" do + = button_tag class: 'btn btn-import btn-success js-import-all' do Import all compatible projects - = icon("spinner spin", class: "loading-icon") + = icon('spinner spin', class: 'loading-icon') - else - = button_tag class: "btn btn-success js-import-all" do + = button_tag class: 'btn btn-success js-import-all' do Import all projects - = icon("spinner spin", class: "loading-icon") + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -32,7 +33,7 @@ - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} %td - = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank" + = link_to project.import_source, 'https://bitbucket.org/#{project.import_source}', target: '_blank' %td = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status @@ -47,31 +48,31 @@ = project.human_import_status_name - @repos.each do |repo| - %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} + %tr{id: "repo_#{repo.owner}___#{repo.slug}"} %td - = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" + = link_to "#{repo.full_name}", "https://bitbucket.org/#{repo.full_name}", target: "_blank" %td.import-target - = import_project_target(repo['owner'], repo['slug']) + = "#{repo.full_name}" %td.import-actions.job-status - = button_tag class: "btn btn-import js-add-to-import" do + = button_tag class: 'btn btn-import js-add-to-import' do Import - = icon("spinner spin", class: "loading-icon") + = icon('spinner spin', class: 'loading-icon') - @incompatible_repos.each do |repo| - %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} + %tr{id: "repo_#{repo.owner}___#{repo.slug}"} %td - = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" + = link_to "#{repo.full_name}", "https://bitbucket.org/#{repo.full_name}", target: '_blank' %td.import-target %td.import-actions-job-status - = label_tag "Incompatible Project", nil, class: "label label-danger" + = label_tag 'Incompatible Project', nil, class: 'label label-danger' - if @incompatible_repos.any? %p One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git. Please convert - = link_to "them to Git,", "https://www.atlassian.com/git/tutorials/migrating-overview" + = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview' and go through the - = link_to "import flow", status_import_bitbucket_path, "data-no-turbolink" => "true" + = link_to 'import flow', status_import_bitbucket_path, 'data-no-turbolink' => 'true' again. .js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } } diff --git a/lib/bitbucket/error/unauthorized.rb b/lib/bitbucket/error/unauthorized.rb new file mode 100644 index 00000000000..5e2eb57bb0e --- /dev/null +++ b/lib/bitbucket/error/unauthorized.rb @@ -0,0 +1,6 @@ +module Bitbucket + module Error + class Unauthorized < StandardError + end + end +end From ceac7878e9b5ba56f31f605c40095e8b83d83b6f Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:04:11 -0300 Subject: [PATCH 012/386] Clone Bitbucket repositories over HTTPS --- lib/gitlab/bitbucket_import/project_creator.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index b90ef0b0fba..9d80c5d4f2b 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -13,15 +13,15 @@ module Gitlab def execute ::Projects::CreateService.new( current_user, - name: repo["name"], - path: repo["slug"], - description: repo["description"], + name: repo.name, + path: repo.slug, + description: repo.description, namespace_id: namespace.id, - visibility_level: repo["is_private"] ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC, - import_type: "bitbucket", - import_source: "#{repo["owner"]}/#{repo["slug"]}", - import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git", - import_data: { credentials: { bb_session: session_data } } + visibility_level: repo.visibility_level, + import_type: 'bitbucket', + import_source: repo.full_name, + import_url: repo.clone_url(@session_data[:token]), + import_data: { credentials: session_data } ).execute end end From 267e27b0cd543e8eeaa04686ad4678c4f553c479 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:05:44 -0300 Subject: [PATCH 013/386] Remove code to clone Bitbucket repositories using SSH --- app/controllers/application_controller.rb | 2 +- config/initializers/public_key.rb | 2 -- lib/gitlab/bitbucket_import/key_adder.rb | 24 ---------------------- lib/gitlab/bitbucket_import/key_deleter.rb | 23 --------------------- 4 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 config/initializers/public_key.rb delete mode 100644 lib/gitlab/bitbucket_import/key_adder.rb delete mode 100644 lib/gitlab/bitbucket_import/key_deleter.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 517ad4f03f3..a6b0a4af503 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -254,7 +254,7 @@ class ApplicationController < ActionController::Base end def bitbucket_import_configured? - Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present? + Gitlab::OAuth::Provider.enabled?(:bitbucket) end def google_code_import_enabled? diff --git a/config/initializers/public_key.rb b/config/initializers/public_key.rb deleted file mode 100644 index e4f09a2d020..00000000000 --- a/config/initializers/public_key.rb +++ /dev/null @@ -1,2 +0,0 @@ -path = File.expand_path("~/.ssh/bitbucket_rsa.pub") -Gitlab::BitbucketImport.public_key = File.read(path) if File.exist?(path) diff --git a/lib/gitlab/bitbucket_import/key_adder.rb b/lib/gitlab/bitbucket_import/key_adder.rb deleted file mode 100644 index 0b63f025d0a..00000000000 --- a/lib/gitlab/bitbucket_import/key_adder.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Gitlab - module BitbucketImport - class KeyAdder - attr_reader :repo, :current_user, :client - - def initialize(repo, current_user, access_params) - @repo, @current_user = repo, current_user - @client = Client.new(access_params[:bitbucket_access_token], - access_params[:bitbucket_access_token_secret]) - end - - def execute - return false unless BitbucketImport.public_key.present? - - project_identifier = "#{repo["owner"]}/#{repo["slug"]}" - client.add_deploy_key(project_identifier, BitbucketImport.public_key) - - true - rescue - false - end - end - end -end diff --git a/lib/gitlab/bitbucket_import/key_deleter.rb b/lib/gitlab/bitbucket_import/key_deleter.rb deleted file mode 100644 index e03c3155b3e..00000000000 --- a/lib/gitlab/bitbucket_import/key_deleter.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab - module BitbucketImport - class KeyDeleter - attr_reader :project, :current_user, :client - - def initialize(project) - @project = project - @current_user = project.creator - @client = Client.from_project(@project) - end - - def execute - return false unless BitbucketImport.public_key.present? - - client.delete_deploy_key(project.import_source, BitbucketImport.public_key) - - true - rescue - false - end - end - end -end From 3dd15d3f753a5a71522275a37393bfa56d6e3517 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:09:25 -0300 Subject: [PATCH 014/386] Add an endpoint to get a list of issues for a repo --- lib/bitbucket/client.rb | 7 ++++ lib/bitbucket/representation/issue.rb | 49 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 lib/bitbucket/representation/issue.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 24984ca0793..ac6e91bb526 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -4,6 +4,13 @@ module Bitbucket @connection = options.fetch(:connection, Connection.new(options)) end + def issues(repo) + relative_path = "/repositories/#{repo}/issues" + paginator = Paginator.new(connection, relative_path, :issue) + + Collection.new(paginator) + end + def repo(name) parsed_response = connection.get("/repositories/#{name}") diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb new file mode 100644 index 00000000000..48647ad51f6 --- /dev/null +++ b/lib/bitbucket/representation/issue.rb @@ -0,0 +1,49 @@ +module Bitbucket + module Representation + class Issue < Representation::Base + CLOSED_STATUS = %w(resolved invalid duplicate wontfix closed).freeze + + def iid + raw['id'] + end + + def author + reporter.fetch('username', 'Anonymous') + end + + def description + raw.dig('content', 'raw') + end + + def state + closed? ? 'closed' : 'opened' + end + + def title + raw['title'] + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['edited_on'] + end + + def to_s + iid + end + + private + + def closed? + CLOSED_STATUS.include?(raw['state']) + end + + def reporter + raw.fetch('reporter', {}) + end + end + end +end From 3f59d25d263d1ac9db76cd2d3d4d025fb6d6dff4 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:10:29 -0300 Subject: [PATCH 015/386] Add an endpoint to get a list of issue comments --- lib/bitbucket/client.rb | 10 +++++++++ lib/bitbucket/representation/comment.rb | 27 +++++++++++++++++++++++++ lib/bitbucket/representation/url.rb | 9 +++++++++ 3 files changed, 46 insertions(+) create mode 100644 lib/bitbucket/representation/comment.rb create mode 100644 lib/bitbucket/representation/url.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index ac6e91bb526..3d22347603d 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -11,6 +11,16 @@ module Bitbucket Collection.new(paginator) end + def issue_comments(repo, number) + relative_path = "/repositories/#{repo}/issues/#{number}/comments" + paginator = Paginator.new(connection, relative_path, :url) + + Collection.new(paginator).map do |comment_url| + parsed_response = connection.get(comment_url.to_s) + Representation::Comment.new(parsed_response) + end + end + def repo(name) parsed_response = connection.get("/repositories/#{name}") diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb new file mode 100644 index 00000000000..94bc18cbfab --- /dev/null +++ b/lib/bitbucket/representation/comment.rb @@ -0,0 +1,27 @@ +module Bitbucket + module Representation + class Comment < Representation::Base + def author + user.fetch('username', 'Anonymous') + end + + def note + raw.dig('content', 'raw') + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['updated_on'] || raw['created_on'] + end + + private + + def user + raw.fetch('user', {}) + end + end + end +end diff --git a/lib/bitbucket/representation/url.rb b/lib/bitbucket/representation/url.rb new file mode 100644 index 00000000000..24ae1048013 --- /dev/null +++ b/lib/bitbucket/representation/url.rb @@ -0,0 +1,9 @@ +module Bitbucket + module Representation + class Url < Representation::Base + def to_s + raw.dig('links', 'self', 'href') + end + end + end +end From 3c756b83ef04dbbb2a82a53cf785a87da0772255 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:11:45 -0300 Subject: [PATCH 016/386] Remove client for the Bitbucket API version 1.0 --- lib/gitlab/bitbucket_import/client.rb | 142 -------------------------- 1 file changed, 142 deletions(-) delete mode 100644 lib/gitlab/bitbucket_import/client.rb diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb deleted file mode 100644 index 8d1ad62fae0..00000000000 --- a/lib/gitlab/bitbucket_import/client.rb +++ /dev/null @@ -1,142 +0,0 @@ -module Gitlab - module BitbucketImport - class Client - class Unauthorized < StandardError; end - - attr_reader :consumer, :api - - def self.from_project(project) - import_data_credentials = project.import_data.credentials if project.import_data - if import_data_credentials && import_data_credentials[:bb_session] - token = import_data_credentials[:bb_session][:bitbucket_access_token] - token_secret = import_data_credentials[:bb_session][:bitbucket_access_token_secret] - new(token, token_secret) - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{project.id}" - end - end - - def initialize(access_token = nil, access_token_secret = nil) - @consumer = ::OAuth::Consumer.new( - config.app_id, - config.app_secret, - bitbucket_options - ) - - if access_token && access_token_secret - @api = ::OAuth::AccessToken.new(@consumer, access_token, access_token_secret) - end - end - - def request_token(redirect_uri) - request_token = consumer.get_request_token(oauth_callback: redirect_uri) - - { - oauth_token: request_token.token, - oauth_token_secret: request_token.secret, - oauth_callback_confirmed: request_token.callback_confirmed?.to_s - } - end - - def authorize_url(request_token, redirect_uri) - request_token = ::OAuth::RequestToken.from_hash(consumer, request_token) if request_token.is_a?(Hash) - - if request_token.callback_confirmed? - request_token.authorize_url - else - request_token.authorize_url(oauth_callback: redirect_uri) - end - end - - def get_token(request_token, oauth_verifier, redirect_uri) - request_token = ::OAuth::RequestToken.from_hash(consumer, request_token) if request_token.is_a?(Hash) - - if request_token.callback_confirmed? - request_token.get_access_token(oauth_verifier: oauth_verifier) - else - request_token.get_access_token(oauth_callback: redirect_uri) - end - end - - def user - JSON.parse(get("/api/1.0/user").body) - end - - def issues(project_identifier) - all_issues = [] - offset = 0 - per_page = 50 # Maximum number allowed by Bitbucket - index = 0 - - begin - issues = JSON.parse(get(issue_api_endpoint(project_identifier, per_page, offset)).body) - # Find out how many total issues are present - total = issues["count"] if index == 0 - all_issues.concat(issues["issues"]) - offset += issues["issues"].count - index += 1 - end while all_issues.count < total - - all_issues - end - - def issue_comments(project_identifier, issue_id) - comments = JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body) - comments.sort_by { |comment| comment["utc_created_on"] } - end - - def project(project_identifier) - JSON.parse(get("/api/1.0/repositories/#{project_identifier}").body) - end - - def find_deploy_key(project_identifier, key) - JSON.parse(get("/api/1.0/repositories/#{project_identifier}/deploy-keys").body).find do |deploy_key| - deploy_key["key"].chomp == key.chomp - end - end - - def add_deploy_key(project_identifier, key) - deploy_key = find_deploy_key(project_identifier, key) - return if deploy_key - - JSON.parse(api.post("/api/1.0/repositories/#{project_identifier}/deploy-keys", key: key, label: "GitLab import key").body) - end - - def delete_deploy_key(project_identifier, key) - deploy_key = find_deploy_key(project_identifier, key) - return unless deploy_key - - api.delete("/api/1.0/repositories/#{project_identifier}/deploy-keys/#{deploy_key["pk"]}").code == "204" - end - - def projects - JSON.parse(get("/api/1.0/user/repositories").body).select { |repo| repo["scm"] == "git" } - end - - def incompatible_projects - JSON.parse(get("/api/1.0/user/repositories").body).reject { |repo| repo["scm"] == "git" } - end - - private - - def get(url) - response = api.get(url) - raise Unauthorized if (400..499).cover?(response.code.to_i) - - response - end - - def issue_api_endpoint(project_identifier, per_page, offset) - "/api/1.0/repositories/#{project_identifier}/issues?sort=utc_created_on&limit=#{per_page}&start=#{offset}" - end - - def config - Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket" } - end - - def bitbucket_options - OmniAuth::Strategies::Bitbucket.default_options[:client_options].symbolize_keys - end - end - end -end From 317b020932736d2cd629542e3a8b3aef2219e033 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:13:56 -0300 Subject: [PATCH 017/386] Refactoring Bitbucket importer to use the new OAuth2 client --- lib/gitlab/bitbucket_import/importer.rb | 68 +++++++++++-------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index f4b5097adb1..67e906431f0 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -5,18 +5,14 @@ module Gitlab def initialize(project) @project = project - @client = Client.from_project(@project) + @client = Bitbucket::Client.new(project.import_data.credentials) @formatter = Gitlab::ImportFormatter.new end def execute - import_issues if has_issues? + import_issues true - rescue ActiveRecord::RecordInvalid => e - raise Projects::ImportService::Error.new, e.message - ensure - Gitlab::BitbucketImport::KeyDeleter.new(project).execute end private @@ -30,44 +26,40 @@ module Gitlab end end - def identifier - project.import_source - end - - def has_issues? - client.project(identifier)["has_issues"] + def repo + @repo ||= client.repo(project.import_source) end def import_issues - issues = client.issues(identifier) + return unless repo.has_issues? - issues.each do |issue| - body = '' - reporter = nil - author = 'Anonymous' + client.issues(repo).each do |issue| + description = @formatter.author_line(issue.author) + description += issue.description - if issue["reported_by"] && issue["reported_by"]["username"] - reporter = issue["reported_by"]["username"] - author = reporter - end + issue = project.issues.create( + iid: issue.iid, + title: issue.title, + description: description, + state: issue.state, + author_id: gl_user_id(project, issue.author), + created_at: issue.created_at, + updated_at: issue.updated_at + ) - body = @formatter.author_line(author) - body += issue["content"] + if issue.persisted? + client.issue_comments(repo, issue.iid).each do |comment| + note = @formatter.author_line(comment.author) + note += comment.note - comments = client.issue_comments(identifier, issue["local_id"]) - - if comments.any? - body += @formatter.comments_header - end - - comments.each do |comment| - author = 'Anonymous' - - if comment["author_info"] && comment["author_info"]["username"] - author = comment["author_info"]["username"] + issue.notes.create!( + project: project, + note: note, + author_id: gl_user_id(project, comment.author), + created_at: comment.created_at, + updated_at: comment.updated_at + ) end - - body += @formatter.comment(author, comment["utc_created_on"], comment["content"]) end project.issues.create!( @@ -77,8 +69,8 @@ module Gitlab author_id: gitlab_user_id(project, reporter) ) end - rescue ActiveRecord::RecordInvalid => e - raise Projects::ImportService::Error, e.message + rescue ActiveRecord::RecordInvalid + nil end end end From 64722a15e39436820a0636804179cf8c8957197e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:15:15 -0300 Subject: [PATCH 018/386] Add an endpoint to get a list of pull requests for a repo --- lib/bitbucket/client.rb | 6 ++++++ lib/bitbucket/representation/pull_request.rb | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 lib/bitbucket/representation/pull_request.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 3d22347603d..0368f388ea4 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -21,6 +21,12 @@ module Bitbucket end end + def pull_requests(repo) + relative_path = "/repositories/#{repo}/pullrequests" + paginator = Paginator.new(connection, relative_path, :pull_request) + + Collection.new(paginator) + end def repo(name) parsed_response = connection.get("/repositories/#{name}") diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb new file mode 100644 index 00000000000..7cbad91e9c8 --- /dev/null +++ b/lib/bitbucket/representation/pull_request.rb @@ -0,0 +1,6 @@ +module Bitbucket + module Representation + class PullRequest < Representation::Base + end + end +end From 704115c726999d6f0467bbf70087db3ae690d3ab Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 22 Aug 2016 16:15:39 -0300 Subject: [PATCH 019/386] Import opened pull request from Bitbucket --- lib/bitbucket/representation/pull_request.rb | 59 ++++++++++++++++++++ lib/gitlab/bitbucket_import/importer.rb | 31 ++++++++++ 2 files changed, 90 insertions(+) diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb index 7cbad91e9c8..e7b1f99e9a6 100644 --- a/lib/bitbucket/representation/pull_request.rb +++ b/lib/bitbucket/representation/pull_request.rb @@ -1,6 +1,65 @@ module Bitbucket module Representation class PullRequest < Representation::Base + def author + raw.fetch('author', {}).fetch('username', 'Anonymous') + end + + def description + raw['description'] + end + + def iid + raw['id'] + end + + def state + if raw['state'] == 'MERGED' + 'merged' + elsif raw['state'] == 'DECLINED' + 'closed' + else + 'opened' + end + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['updated_on'] + end + + def title + raw['title'] + end + + def source_branch_name + source_branch.dig('branch', 'name') + end + + def source_branch_sha + source_branch.dig('commit', 'hash') + end + + def target_branch_name + target_branch.dig('branch', 'name') + end + + def target_branch_sha + target_branch.dig('commit', 'hash') + end + + private + + def source_branch + raw['source'] + end + + def target_branch + raw['destination'] + end end end end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 67e906431f0..0060e350249 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -11,6 +11,7 @@ module Gitlab def execute import_issues + import_pull_requests true end @@ -72,6 +73,36 @@ module Gitlab rescue ActiveRecord::RecordInvalid nil end + + def import_pull_requests + pull_requests = client.pull_requests(repo) + + pull_requests.each do |pull_request| + begin + description = @formatter.author_line(pull_request.author) + description += pull_request.description + + project.merge_requests.create( + iid: pull_request.iid, + title: pull_request.title, + description: description, + source_project: project, + source_branch: pull_request.source_branch_name, + source_branch_sha: pull_request.source_branch_sha, + target_project: project, + target_branch: pull_request.target_branch_name, + target_branch_sha: pull_request.target_branch_sha, + state: pull_request.state, + author_id: gl_user_id(project, pull_request.author), + assignee_id: nil, + created_at: pull_request.created_at, + updated_at: pull_request.updated_at + ) + rescue ActiveRecord::RecordInvalid + nil + end + end + end end end end From a0959430516f57ad27df21447777ebb226890647 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 14:00:33 -0800 Subject: [PATCH 020/386] Fix rebase failures with Bitbucket changes --- app/controllers/import/bitbucket_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index ee30a24ab77..5326dce4ebb 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -47,9 +47,9 @@ class Import::BitbucketController < Import::BaseController repo_owner = current_user.username if repo_owner == client.user.username @target_namespace = params[:new_namespace].presence || repo_owner - namespace = find_or_create_namespace(target_namespace_name, repo_owner) + namespace = find_or_create_namespace(@target_namespace, repo_owner) - if current_user.can?(:create_projects, @target_namespace) + if current_user.can?(:create_projects, namespace) @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, current_user, credentials).execute else render 'unauthorized' From 478730bebd5c8a9505490d2b396ac3c866da1b09 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 14:44:31 -0800 Subject: [PATCH 021/386] Support selection of different namespace and project destination --- app/controllers/import/bitbucket_controller.rb | 6 +++--- app/views/import/bitbucket/status.html.haml | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 5326dce4ebb..e7150cb8e95 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -39,9 +39,9 @@ class Import::BitbucketController < Import::BaseController client = Bitbucket::Client.new(credentials) @repo_id = params[:repo_id].to_s - name = @repo_id.to_s.gsub('___', '/') + name = @repo_id.gsub('___', '/') repo = client.repo(name) - @project_name = repo.name + @project_name = params[:new_name].presence || repo.name repo_owner = repo.owner repo_owner = current_user.username if repo_owner == client.user.username @@ -50,7 +50,7 @@ class Import::BitbucketController < Import::BaseController namespace = find_or_create_namespace(@target_namespace, repo_owner) if current_user.can?(:create_projects, namespace) - @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, current_user, credentials).execute + @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, namespace, current_user, credentials).execute else render 'unauthorized' end diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index e62ff5f61f6..cc262e97ceb 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -15,7 +15,7 @@ Import all compatible projects = icon('spinner spin', class: 'loading-icon') - else - = button_tag class: 'btn btn-success js-import-all' do + = button_tag class: 'btn btn-import btn-success js-import-all' do Import all projects = icon('spinner spin', class: 'loading-icon') @@ -52,7 +52,17 @@ %td = link_to "#{repo.full_name}", "https://bitbucket.org/#{repo.full_name}", target: "_blank" %td.import-target - = "#{repo.full_name}" + %fieldset.row + .input-group + .project-path.input-group-btn + - if current_user.can_select_namespace? + - selected = params[:namespace_id] || :current_user + - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner, path: repo.owner) } : {} + = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } + - else + = text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true + %span.input-group-addon / + = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true %td.import-actions.job-status = button_tag class: 'btn btn-import js-add-to-import' do Import From e2688feeb3075265fb926bbd68560b2046afa0c5 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 15:44:52 -0800 Subject: [PATCH 022/386] Address initial review comments --- app/controllers/import/bitbucket_controller.rb | 13 ++++++------- app/views/import/bitbucket/status.html.haml | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index e7150cb8e95..72c90f9daf2 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -17,11 +17,10 @@ class Import::BitbucketController < Import::BaseController end def status - client = Bitbucket::Client.new(credentials) - repos = client.repos + bitbucket_client = Bitbucket::Client.new(credentials) + repos = bitbucket_client.repos - @repos = repos.select(&:valid?) - @incompatible_repos = repos.reject(&:valid?) + @repos, @incompatible_repos = repos.partition { |repo| repo.valid? } @already_added_projects = current_user.created_projects.where(import_type: 'bitbucket') already_added_projects_names = @already_added_projects.pluck(:import_source) @@ -36,15 +35,15 @@ class Import::BitbucketController < Import::BaseController end def create - client = Bitbucket::Client.new(credentials) + bitbucket_client = Bitbucket::Client.new(credentials) @repo_id = params[:repo_id].to_s name = @repo_id.gsub('___', '/') - repo = client.repo(name) + repo = bitbucket_client.repo(name) @project_name = params[:new_name].presence || repo.name repo_owner = repo.owner - repo_owner = current_user.username if repo_owner == client.user.username + repo_owner = current_user.username if repo_owner == bitbucket_client.user.username @target_namespace = params[:new_namespace].presence || repo_owner namespace = find_or_create_namespace(@target_namespace, repo_owner) diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index cc262e97ceb..ac09b71ae89 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -33,7 +33,7 @@ - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} %td - = link_to project.import_source, 'https://bitbucket.org/#{project.import_source}', target: '_blank' + = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank' %td = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status @@ -50,7 +50,7 @@ - @repos.each do |repo| %tr{id: "repo_#{repo.owner}___#{repo.slug}"} %td - = link_to "#{repo.full_name}", "https://bitbucket.org/#{repo.full_name}", target: "_blank" + = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: "_blank" %td.import-target %fieldset.row .input-group @@ -70,7 +70,7 @@ - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo.owner}___#{repo.slug}"} %td - = link_to "#{repo.full_name}", "https://bitbucket.org/#{repo.full_name}", target: '_blank' + = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank' %td.import-target %td.import-actions-job-status = label_tag 'Incompatible Project', nil, class: 'label label-danger' From 9860488360681a3b10c3de04606ef931c3639601 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 16:07:16 -0800 Subject: [PATCH 023/386] Fix missing Bitbucket ProjectController changes for specifying namespace/project --- lib/gitlab/bitbucket_import/project_creator.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 9d80c5d4f2b..b34be272af3 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -1,10 +1,11 @@ module Gitlab module BitbucketImport class ProjectCreator - attr_reader :repo, :namespace, :current_user, :session_data + attr_reader :repo, :name, :namespace, :current_user, :session_data - def initialize(repo, namespace, current_user, session_data) + def initialize(repo, name, namespace, current_user, session_data) @repo = repo + @name = name @namespace = namespace @current_user = current_user @session_data = session_data @@ -13,8 +14,8 @@ module Gitlab def execute ::Projects::CreateService.new( current_user, - name: repo.name, - path: repo.slug, + name: name, + path: name, description: repo.description, namespace_id: namespace.id, visibility_level: repo.visibility_level, From b25ebfe67b0e5448e9625e7a5c469ab41a4b7059 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 16:08:03 -0800 Subject: [PATCH 024/386] Lazily load Bitbucket connection --- lib/bitbucket/connection.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index 00f127f9507..e9cbf36a44b 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -13,13 +13,18 @@ module Bitbucket @expires_at = options.fetch(:expires_at) @expires_in = options.fetch(:expires_in) @refresh_token = options.fetch(:refresh_token) + end - @client = OAuth2::Client.new(provider.app_id, provider.app_secret, options) - @connection = OAuth2::AccessToken.new(@client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) + def client + @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options) + end + + def connection + @connection ||= OAuth2::AccessToken.new(client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) end def query(params = {}) - @query.update(params) + @query.merge!(params) end def get(path, query = {}) @@ -46,7 +51,7 @@ module Bitbucket private - attr_reader :connection, :expires_at, :expires_in, :refresh_token, :token + attr_reader :expires_at, :expires_in, :refresh_token, :token def build_url(path) return path if path.starts_with?(root_url) From 3b9d1c0f5d2996b946ab71704995b752c0ff8f60 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 16:10:12 -0800 Subject: [PATCH 025/386] Fix refresh to lazily load connection --- lib/bitbucket/connection.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index e9cbf36a44b..201e7e3b808 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -45,8 +45,7 @@ module Bitbucket @expires_at = response.expires_at @expires_in = response.expires_in @refresh_token = response.refresh_token - - @connection = OAuth2::AccessToken.new(@client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) + @connection = nil end private From 82d7a3a3dd61c70c87a8a4a116b2ce6c0612de59 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 16:33:28 -0800 Subject: [PATCH 026/386] Fix typos in pull requests failing to import --- lib/gitlab/bitbucket_import/importer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 0060e350249..15cc64dd7f4 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -43,7 +43,7 @@ module Gitlab title: issue.title, description: description, state: issue.state, - author_id: gl_user_id(project, issue.author), + author_id: gitlab_user_id(project, issue.author), created_at: issue.created_at, updated_at: issue.updated_at ) @@ -56,7 +56,7 @@ module Gitlab issue.notes.create!( project: project, note: note, - author_id: gl_user_id(project, comment.author), + author_id: gitlab_user_id(project, comment.author), created_at: comment.created_at, updated_at: comment.updated_at ) @@ -93,7 +93,7 @@ module Gitlab target_branch: pull_request.target_branch_name, target_branch_sha: pull_request.target_branch_sha, state: pull_request.state, - author_id: gl_user_id(project, pull_request.author), + author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil, created_at: pull_request.created_at, updated_at: pull_request.updated_at From 489d241c8d68ed527fccb73a1f7e46e9a567c971 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Nov 2016 16:48:04 -0800 Subject: [PATCH 027/386] Incorporate review comments --- lib/bitbucket/page.rb | 3 +-- lib/omniauth/strategies/bitbucket.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb index ad9a2baba36..0e8ce11aa1d 100644 --- a/lib/bitbucket/page.rb +++ b/lib/bitbucket/page.rb @@ -29,8 +29,7 @@ module Bitbucket end def representation_class(type) - class_name = "Bitbucket::Representation::#{type.to_s.camelize}" - class_name.constantize + class_name = Bitbucket::Representation.const_get(type.to_s.camelize) end end end diff --git a/lib/omniauth/strategies/bitbucket.rb b/lib/omniauth/strategies/bitbucket.rb index 2006e7582ce..c5484c59c47 100644 --- a/lib/omniauth/strategies/bitbucket.rb +++ b/lib/omniauth/strategies/bitbucket.rb @@ -32,7 +32,7 @@ module OmniAuth end def primary_email - primary = emails.find{ |i| i['is_primary'] && i['is_confirmed'] } + primary = emails.find { |i| i['is_primary'] && i['is_confirmed'] } primary && primary['email'] || nil end From ea393e6f308e5dcdd5c48433285594db0539b203 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Nov 2016 00:13:17 -0800 Subject: [PATCH 028/386] Import pull request comments --- lib/bitbucket/client.rb | 7 ++ .../representation/pull_request_comment.rb | 39 +++++++++++ lib/gitlab/bitbucket_import/importer.rb | 70 ++++++++++++++++++- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 lib/bitbucket/representation/pull_request_comment.rb diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 0368f388ea4..fce1c898030 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -28,6 +28,13 @@ module Bitbucket Collection.new(paginator) end + def pull_request_comments(repo, pull_request) + relative_path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" + paginator = Paginator.new(connection, relative_path, :pull_request_comment) + + Collection.new(paginator) + end + def repo(name) parsed_response = connection.get("/repositories/#{name}") Representation::Repo.new(parsed_response) diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb new file mode 100644 index 00000000000..94719edbf38 --- /dev/null +++ b/lib/bitbucket/representation/pull_request_comment.rb @@ -0,0 +1,39 @@ +module Bitbucket + module Representation + class PullRequestComment < Comment + def iid + raw['id'] + end + + def file_path + inline.fetch('path', nil) + end + + def old_pos + inline.fetch('from', nil) || 1 + end + + def new_pos + inline.fetch('to', nil) || 1 + end + + def parent_id + raw.fetch('parent', {}).fetch('id', nil) + end + + def inline? + raw.has_key?('inline') + end + + def has_parent? + raw.has_key?('parent') + end + + private + + def inline + raw.fetch('inline', {}) + end + end + end +end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 15cc64dd7f4..94e8062e447 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -82,7 +82,7 @@ module Gitlab description = @formatter.author_line(pull_request.author) description += pull_request.description - project.merge_requests.create( + merge_request = project.merge_requests.create( iid: pull_request.iid, title: pull_request.title, description: description, @@ -98,11 +98,79 @@ module Gitlab created_at: pull_request.created_at, updated_at: pull_request.updated_at ) + + import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue ActiveRecord::RecordInvalid nil end end end + + def import_pull_request_comments(pull_request, merge_request) + comments = client.pull_request_comments(repo, pull_request.iid) + + inline_comments, pr_comments = comments.partition(&:inline?) + + import_inline_comments(inline_comments, pull_request, merge_request) + import_standalone_pr_comments(pr_comments, merge_request) + end + + def import_inline_comments(inline_comments, pull_request, merge_request) + line_code_map = {} + + children, parents = inline_comments.partition(&:has_parent?) + + # The Bitbucket API returns threaded replies as parent-child + # relationships. We assume that the child can appear in any order in + # the JSON. + parents.each do |comment| + line_code_map[comment.iid] = generate_line_code(comment) + end + + children.each do |comment| + line_code_map[comment.iid] = line_code_map.fetch(comment.parent_id, nil) + end + + inline_comments.each do |comment| + begin + attributes = pull_request_comment_attributes(comment) + attributes.merge!( + commit_id: pull_request.source_branch_sha, + line_code: line_code_map.fetch(comment.iid), + type: 'LegacyDiffNote') + + note = merge_request.notes.create!(attributes) + rescue ActiveRecord::RecordInvalid => e + Rails.log.error("Bitbucket importer ERROR: Invalid pull request comment #{e.message}") + nil + end + end + end + + def import_standalone_pr_comments(pr_comments, merge_request) + pr_comments.each do |comment| + begin + merge_request.notes.create!(pull_request_comment_attributes(comment)) + rescue ActiveRecord::RecordInvalid => e + Rails.log.error("Bitbucket importer ERROR: Invalid standalone pull request comment #{e.message}") + nil + end + end + end + + def generate_line_code(pr_comment) + Gitlab::Diff::LineCode.generate(pr_comment.file_path, pr_comment.new_pos, pr_comment.old_pos) + end + + def pull_request_comment_attributes(comment) + { + project: project, + note: comment.note, + author_id: gitlab_user_id(project, comment.author), + created_at: comment.created_at, + updated_at: comment.updated_at + } + end end end end From f25d64d41ae16b96c1381068112717be5b4a1552 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Nov 2016 22:59:59 -0800 Subject: [PATCH 029/386] Remove no longer used BitbucketImport::Client spec --- .../gitlab/bitbucket_import/client_spec.rb | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 spec/lib/gitlab/bitbucket_import/client_spec.rb diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb deleted file mode 100644 index 7543c29bcc4..00000000000 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'spec_helper' - -describe Gitlab::BitbucketImport::Client, lib: true do - include ImportSpecHelper - - let(:token) { '123456' } - let(:secret) { 'secret' } - let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) } - - before do - stub_omniauth_provider('bitbucket') - end - - it 'all OAuth client options are symbols' do - client.consumer.options.keys.each do |key| - expect(key).to be_kind_of(Symbol) - end - end - - context 'issues' do - let(:per_page) { 50 } - let(:count) { 95 } - let(:sample_issues) do - issues = [] - - count.times do |i| - issues << { local_id: i } - end - - issues - end - let(:first_sample_data) { { count: count, issues: sample_issues[0..per_page - 1] } } - let(:second_sample_data) { { count: count, issues: sample_issues[per_page..count] } } - let(:project_id) { 'namespace/repo' } - - it 'retrieves issues over a number of pages' do - stub_request(:get, - "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=0"). - to_return(status: 200, - body: first_sample_data.to_json, - headers: {}) - - stub_request(:get, - "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=50"). - to_return(status: 200, - body: second_sample_data.to_json, - headers: {}) - - issues = client.issues(project_id) - expect(issues.count).to eq(95) - end - end - - context 'project import' do - it 'calls .from_project with no errors' do - project = create(:empty_project) - project.import_url = "ssh://git@bitbucket.org/test/test.git" - project.create_or_update_import_data(credentials: - { user: "git", - password: nil, - bb_session: { bitbucket_access_token: "test", - bitbucket_access_token_secret: "test" } }) - - expect { described_class.from_project(project) }.not_to raise_error - end - end -end From b8bf28348fb903c62e084353896873438f4f0845 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 17 Nov 2016 20:16:22 -0800 Subject: [PATCH 030/386] Rubocop fixes --- lib/bitbucket/page.rb | 2 +- lib/gitlab/bitbucket_import/importer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb index 0e8ce11aa1d..bc51ce7dce2 100644 --- a/lib/bitbucket/page.rb +++ b/lib/bitbucket/page.rb @@ -29,7 +29,7 @@ module Bitbucket end def representation_class(type) - class_name = Bitbucket::Representation.const_get(type.to_s.camelize) + Bitbucket::Representation.const_get(type.to_s.camelize) end end end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 94e8062e447..1f7a691e6dd 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -139,7 +139,7 @@ module Gitlab line_code: line_code_map.fetch(comment.iid), type: 'LegacyDiffNote') - note = merge_request.notes.create!(attributes) + merge_request.notes.create!(attributes) rescue ActiveRecord::RecordInvalid => e Rails.log.error("Bitbucket importer ERROR: Invalid pull request comment #{e.message}") nil From 9e6b25d0bc5c2f88330bb074db242017ea45f90d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 17 Nov 2016 21:30:35 -0800 Subject: [PATCH 031/386] Add support for extracting all pull requests and their raw diffs --- lib/bitbucket/client.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index fce1c898030..0d4cfd600b8 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -22,7 +22,7 @@ module Bitbucket end def pull_requests(repo) - relative_path = "/repositories/#{repo}/pullrequests" + relative_path = "/repositories/#{repo}/pullrequests?state=ALL" paginator = Paginator.new(connection, relative_path, :pull_request) Collection.new(paginator) @@ -35,6 +35,12 @@ module Bitbucket Collection.new(paginator) end + def pull_request_diff(repo, pull_request) + relative_path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" + + connection.get(relative_path) + end + def repo(name) parsed_response = connection.get("/repositories/#{name}") Representation::Repo.new(parsed_response) From 4d7303a98e970c29079cc03a449c71f3cdaa1214 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 Nov 2016 21:35:03 -0800 Subject: [PATCH 032/386] Clean up owner and slug representation --- lib/bitbucket/representation/repo.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb index fe5cda66ab9..b291dfe0441 100644 --- a/lib/bitbucket/representation/repo.rb +++ b/lib/bitbucket/representation/repo.rb @@ -5,10 +5,18 @@ module Bitbucket def initialize(raw) super(raw) + end - if full_name && full_name.split('/').size == 2 - @owner, @slug = full_name.split('/') - end + def owner_and_slug + @owner_and_slug ||= full_name.split('/', 2) + end + + def owner + owner_and_slug.first + end + + def slug + owner_and_slug.last end def clone_url(token = nil) From c7c4d657b427c6fa146319ccc5aa17e87d3d0e0b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 19 Nov 2016 21:44:19 -0800 Subject: [PATCH 033/386] Clean up Bitbucket connection based on review comments --- lib/bitbucket/client.rb | 2 +- lib/bitbucket/connection.rb | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 0d4cfd600b8..33e977d655d 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -1,7 +1,7 @@ module Bitbucket class Client def initialize(options = {}) - @connection = options.fetch(:connection, Connection.new(options)) + @connection = Connection.new(options) end def issues(repo) diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index 201e7e3b808..c375fe16aee 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -7,12 +7,12 @@ module Bitbucket def initialize(options = {}) @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) @base_uri = options.fetch(:base_uri, DEFAULT_BASE_URI) - @query = options.fetch(:query, DEFAULT_QUERY) + @default_query = options.fetch(:query, DEFAULT_QUERY) - @token = options.fetch(:token) - @expires_at = options.fetch(:expires_at) - @expires_in = options.fetch(:expires_in) - @refresh_token = options.fetch(:refresh_token) + @token = options[:token] + @expires_at = options[:expires_at] + @expires_in = options[:expires_in] + @refresh_token = options[:refresh_token] end def client @@ -24,13 +24,13 @@ module Bitbucket end def query(params = {}) - @query.merge!(params) + @default_query.merge!(params) end - def get(path, query = {}) + def get(path, extra_query = {}) refresh! if expired? - response = connection.get(build_url(path), params: @query.merge(query)) + response = connection.get(build_url(path), params: @default_query.merge(extra_query)) response.parsed end From 2747e515c6b06a905512c00af428b1a0aa018569 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 19 Nov 2016 22:39:12 -0800 Subject: [PATCH 034/386] Fix BitbucketImport::ProjectCreator spec --- .../bitbucket_import/project_creator_spec.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb index e1c60e07b4d..bb007949557 100644 --- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -3,12 +3,14 @@ require 'spec_helper' describe Gitlab::BitbucketImport::ProjectCreator, lib: true do let(:user) { create(:user) } let(:repo) do - { - name: 'Vim', - slug: 'vim', - is_private: true, - owner: "asd" - }.with_indifferent_access + double(name: 'Vim', + slug: 'vim', + description: 'Test repo', + is_private: true, + owner: "asd", + full_name: 'Vim repo', + visibility_level: Gitlab::VisibilityLevel::PRIVATE, + clone_url: 'ssh://git@bitbucket.org/asd/vim.git') end let(:namespace){ create(:group, owner: user) } let(:token) { "asdasd12345" } @@ -22,7 +24,7 @@ describe Gitlab::BitbucketImport::ProjectCreator, lib: true do it 'creates project' do allow_any_instance_of(Project).to receive(:add_import_job) - project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, user, access_params) + project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, 'vim', namespace, user, access_params) project = project_creator.execute expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git") From 7ba65d05af190a0aba05bd78463eb9e7d70ca6f7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 20 Nov 2016 21:00:02 -0800 Subject: [PATCH 035/386] Fix Bitbucket callback spec --- .../import/bitbucket_controller_spec.rb | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 1d3c9fbbe2f..11bb190af7e 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -6,11 +6,11 @@ describe Import::BitbucketController do let(:user) { create(:user) } let(:token) { "asdasd12345" } let(:secret) { "sekrettt" } + let(:refresh_token) { SecureRandom.hex(15) } let(:access_params) { { bitbucket_access_token: token, bitbucket_access_token_secret: secret } } def assign_session_tokens - session[:bitbucket_access_token] = token - session[:bitbucket_access_token_secret] = secret + session[:bitbucket_token] = token end before do @@ -24,15 +24,23 @@ describe Import::BitbucketController do end it "updates access token" do - access_token = double(token: token, secret: secret) - allow_any_instance_of(Gitlab::BitbucketImport::Client). + expires_at = Time.now + 1.day + expires_in = 1.day + access_token = double(token: token, + secret: secret, + expires_at: expires_at, + expires_in: expires_in, + refresh_token: refresh_token) + allow_any_instance_of(OAuth2::Client). to receive(:get_token).and_return(access_token) stub_omniauth_provider('bitbucket') get :callback - expect(session[:bitbucket_access_token]).to eq(token) - expect(session[:bitbucket_access_token_secret]).to eq(secret) + expect(session[:bitbucket_token]).to eq(token) + expect(session[:bitbucket_refresh_token]).to eq(refresh_token) + expect(session[:bitbucket_expires_at]).to eq(expires_at) + expect(session[:bitbucket_expires_in]).to eq(expires_in) expect(controller).to redirect_to(status_import_bitbucket_url) end end From af6926283b8c4d8cd9668a8df6a28f2ce35001f6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 20 Nov 2016 21:33:06 -0800 Subject: [PATCH 036/386] Fix Bitbucket status controller spec --- spec/controllers/import/bitbucket_controller_spec.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 11bb190af7e..2fd01c865fb 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -47,14 +47,13 @@ describe Import::BitbucketController do describe "GET status" do before do - @repo = OpenStruct.new(slug: 'vim', owner: 'asd') + @repo = double(slug: 'vim', owner: 'asd', full_name: 'asd/vim', "valid?" => true) assign_session_tokens end it "assigns variables" do @project = create(:project, import_type: 'bitbucket', creator_id: user.id) - client = stub_client(projects: [@repo]) - allow(client).to receive(:incompatible_projects).and_return([]) + allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) get :status @@ -65,7 +64,7 @@ describe Import::BitbucketController do it "does not show already added project" do @project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') - stub_client(projects: [@repo]) + allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo]) get :status From 7953480646b5b129868e4323502a28ce27328d8c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 20 Nov 2016 22:29:45 -0800 Subject: [PATCH 037/386] Fix remaining Bitbucket controller specs --- .../import/bitbucket_controller.rb | 2 +- .../import/bitbucket_controller_spec.rb | 25 ++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 72c90f9daf2..9c97a97a5dd 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -46,7 +46,7 @@ class Import::BitbucketController < Import::BaseController repo_owner = current_user.username if repo_owner == bitbucket_client.user.username @target_namespace = params[:new_namespace].presence || repo_owner - namespace = find_or_create_namespace(@target_namespace, repo_owner) + namespace = find_or_create_namespace(@target_namespace, current_user) if current_user.can?(:create_projects, namespace) @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, namespace, current_user, credentials).execute diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 2fd01c865fb..ce7c0b334ee 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -7,7 +7,7 @@ describe Import::BitbucketController do let(:token) { "asdasd12345" } let(:secret) { "sekrettt" } let(:refresh_token) { SecureRandom.hex(15) } - let(:access_params) { { bitbucket_access_token: token, bitbucket_access_token_secret: secret } } + let(:access_params) { { token: token, expires_at: nil, expires_in: nil, refresh_token: nil } } def assign_session_tokens session[:bitbucket_token] = token @@ -77,19 +77,16 @@ describe Import::BitbucketController do let(:bitbucket_username) { user.username } let(:bitbucket_user) do - { user: { username: bitbucket_username } }.with_indifferent_access + double(username: bitbucket_username) end let(:bitbucket_repo) do - { slug: "vim", owner: bitbucket_username }.with_indifferent_access + double(slug: "vim", owner: bitbucket_username, name: 'vim') end before do - allow(Gitlab::BitbucketImport::KeyAdder). - to receive(:new).with(bitbucket_repo, user, access_params). - and_return(double(execute: true)) - - stub_client(user: bitbucket_user, project: bitbucket_repo) + allow_any_instance_of(Bitbucket::Client).to receive(:repo).and_return(bitbucket_repo) + allow_any_instance_of(Bitbucket::Client).to receive(:user).and_return(bitbucket_user) assign_session_tokens end @@ -97,7 +94,7 @@ describe Import::BitbucketController do context "when the Bitbucket user and GitLab user's usernames match" do it "takes the current user's namespace" do expect(Gitlab::BitbucketImport::ProjectCreator). - to receive(:new).with(bitbucket_repo, user.namespace, user, access_params). + to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params). and_return(double(execute: true)) post :create, format: :js @@ -109,7 +106,7 @@ describe Import::BitbucketController do it "takes the current user's namespace" do expect(Gitlab::BitbucketImport::ProjectCreator). - to receive(:new).with(bitbucket_repo, user.namespace, user, access_params). + to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params). and_return(double(execute: true)) post :create, format: :js @@ -121,7 +118,7 @@ describe Import::BitbucketController do let(:other_username) { "someone_else" } before do - bitbucket_repo["owner"] = other_username + allow(bitbucket_repo).to receive(:owner).and_return(other_username) end context "when a namespace with the Bitbucket user's username already exists" do @@ -130,7 +127,7 @@ describe Import::BitbucketController do context "when the namespace is owned by the GitLab user" do it "takes the existing namespace" do expect(Gitlab::BitbucketImport::ProjectCreator). - to receive(:new).with(bitbucket_repo, existing_namespace, user, access_params). + to receive(:new).with(bitbucket_repo, bitbucket_repo.name, existing_namespace, user, access_params). and_return(double(execute: true)) post :create, format: :js @@ -163,7 +160,7 @@ describe Import::BitbucketController do it "takes the new namespace" do expect(Gitlab::BitbucketImport::ProjectCreator). - to receive(:new).with(bitbucket_repo, an_instance_of(Group), user, access_params). + to receive(:new).with(bitbucket_repo, bitbucket_repo.name, an_instance_of(Group), user, access_params). and_return(double(execute: true)) post :create, format: :js @@ -184,7 +181,7 @@ describe Import::BitbucketController do it "takes the current user's namespace" do expect(Gitlab::BitbucketImport::ProjectCreator). - to receive(:new).with(bitbucket_repo, user.namespace, user, access_params). + to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params). and_return(double(execute: true)) post :create, format: :js From 0b72994b63dbbbddaf0e77629249d92890a6e4a4 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 21 Nov 2016 08:25:47 -0800 Subject: [PATCH 038/386] Simplify Bitbucket::Page implementation --- lib/bitbucket/page.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb index bc51ce7dce2..b91a173b53c 100644 --- a/lib/bitbucket/page.rb +++ b/lib/bitbucket/page.rb @@ -23,7 +23,7 @@ module Bitbucket end def parse_values(raw, representation_class) - return [] if raw['values'].nil? || !raw['values'].is_a?(Array) + return [] unless raw['values'] && raw['values'].is_a?(Array) raw['values'].map { |hash| representation_class.new(hash) } end From 402cc95c1a1df8168467f74e21c6df7d48359714 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 21 Nov 2016 20:49:40 -0800 Subject: [PATCH 039/386] Fix Bitbucket importer spec to pass with 2.0 API --- lib/bitbucket/page.rb | 4 +- lib/gitlab/bitbucket_import/importer.rb | 7 ---- .../gitlab/bitbucket_import/importer_spec.rb | 42 +++++++++++++------ 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb index b91a173b53c..49d083cc66f 100644 --- a/lib/bitbucket/page.rb +++ b/lib/bitbucket/page.rb @@ -22,10 +22,10 @@ module Bitbucket attrs.map { |attr| { attr.to_sym => raw[attr] } }.reduce(&:merge) end - def parse_values(raw, representation_class) + def parse_values(raw, bitbucket_rep_class) return [] unless raw['values'] && raw['values'].is_a?(Array) - raw['values'].map { |hash| representation_class.new(hash) } + raw['values'].map { |hash| bitbucket_rep_class.new(hash) } end def representation_class(type) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 1f7a691e6dd..729b465e861 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -62,13 +62,6 @@ module Gitlab ) end end - - project.issues.create!( - description: body, - title: issue["title"], - state: %w(resolved invalid duplicate wontfix closed).include?(issue["status"]) ? 'closed' : 'opened', - author_id: gitlab_user_id(project, reporter) - ) end rescue ActiveRecord::RecordInvalid nil diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index aa00f32becb..99a65f26f99 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -23,10 +23,14 @@ describe Gitlab::BitbucketImport::Importer, lib: true do statuses.map.with_index do |status, index| issues << { - local_id: index, - status: status, + id: index, + state: status, title: "Issue #{index}", - content: "Some content to issue #{index}" + content: { + raw: "Some content to issue #{index}", + markup: "markdown", + html: "Some content to issue #{index}" + } } end @@ -37,8 +41,8 @@ describe Gitlab::BitbucketImport::Importer, lib: true do let(:data) do { 'bb_session' => { - 'bitbucket_access_token' => "123456", - 'bitbucket_access_token_secret' => "secret" + 'bitbucket_token' => "123456", + 'bitbucket_refresh_token' => "secret" } } end @@ -53,7 +57,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do let(:issues_statuses_sample_data) do { count: sample_issues_statuses.count, - issues: sample_issues_statuses + values: sample_issues_statuses } end @@ -61,26 +65,40 @@ describe Gitlab::BitbucketImport::Importer, lib: true do before do stub_request( :get, - "https://bitbucket.org/api/1.0/repositories/#{project_identifier}" - ).to_return(status: 200, body: { has_issues: true }.to_json) + "https://api.bitbucket.org/2.0/repositories/#{project_identifier}" + ).to_return(status: 200, + headers: {"Content-Type" => "application/json"}, + body: { has_issues: true, full_name: project_identifier }.to_json) stub_request( :get, - "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues?limit=50&sort=utc_created_on&start=0" - ).to_return(status: 200, body: issues_statuses_sample_data.to_json) + "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues?pagelen=50&sort=created_on" + ).to_return(status: 200, + headers: {"Content-Type" => "application/json"}, + body: issues_statuses_sample_data.to_json) sample_issues_statuses.each_with_index do |issue, index| stub_request( :get, - "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues/#{issue[:local_id]}/comments" + "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues/#{issue[:id]}/comments?pagelen=50&sort=created_on" ).to_return( status: 200, - body: [{ author_info: { username: "username" }, utc_created_on: index }].to_json + headers: {"Content-Type" => "application/json"}, + body: { author_info: { username: "username" }, utc_created_on: index }.to_json ) end + + stub_request( + :get, + "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/pullrequests?pagelen=50&sort=created_on&state=ALL" + ).to_return(status: 200, + headers: {"Content-Type" => "application/json"}, + body: {}.to_json) end it 'map statuses to open or closed' do + # HACK: Bitbucket::Representation.const_get('Issue') seems to return Issue without this + Bitbucket::Representation::Issue importer.execute expect(project.issues.where(state: "closed").size).to eq(5) From fc40c3f28a67ecafa58191c0cd6065960dd59c7d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 21 Nov 2016 20:58:46 -0800 Subject: [PATCH 040/386] Fix Rubocop errors with Bitbucket Importer spec --- spec/lib/gitlab/bitbucket_import/importer_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 99a65f26f99..36893751ee0 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -67,14 +67,14 @@ describe Gitlab::BitbucketImport::Importer, lib: true do :get, "https://api.bitbucket.org/2.0/repositories/#{project_identifier}" ).to_return(status: 200, - headers: {"Content-Type" => "application/json"}, + headers: { "Content-Type" => "application/json" }, body: { has_issues: true, full_name: project_identifier }.to_json) stub_request( :get, "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues?pagelen=50&sort=created_on" ).to_return(status: 200, - headers: {"Content-Type" => "application/json"}, + headers: { "Content-Type" => "application/json" }, body: issues_statuses_sample_data.to_json) sample_issues_statuses.each_with_index do |issue, index| @@ -83,22 +83,22 @@ describe Gitlab::BitbucketImport::Importer, lib: true do "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues/#{issue[:id]}/comments?pagelen=50&sort=created_on" ).to_return( status: 200, - headers: {"Content-Type" => "application/json"}, + headers: { "Content-Type" => "application/json" }, body: { author_info: { username: "username" }, utc_created_on: index }.to_json ) end stub_request( - :get, - "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/pullrequests?pagelen=50&sort=created_on&state=ALL" + :get, + "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/pullrequests?pagelen=50&sort=created_on&state=ALL" ).to_return(status: 200, - headers: {"Content-Type" => "application/json"}, + headers: { "Content-Type" => "application/json" }, body: {}.to_json) end it 'map statuses to open or closed' do # HACK: Bitbucket::Representation.const_get('Issue') seems to return Issue without this - Bitbucket::Representation::Issue + Bitbucket::Representation::Issue.new importer.execute expect(project.issues.where(state: "closed").size).to eq(5) From 7a155137a4fd965cb8ff512a9548a7e685b330f5 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 21 Nov 2016 22:06:09 -0800 Subject: [PATCH 041/386] Fix spec for Bitbucket importer --- lib/gitlab/bitbucket_import/importer.rb | 6 +++--- spec/lib/gitlab/bitbucket_import/importer_spec.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 729b465e861..08705afcabb 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -94,7 +94,7 @@ module Gitlab import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue ActiveRecord::RecordInvalid - nil + Rails.log.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request #{e.message}") end end end @@ -134,7 +134,7 @@ module Gitlab merge_request.notes.create!(attributes) rescue ActiveRecord::RecordInvalid => e - Rails.log.error("Bitbucket importer ERROR: Invalid pull request comment #{e.message}") + Rails.log.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request comment #{e.message}") nil end end @@ -145,7 +145,7 @@ module Gitlab begin merge_request.notes.create!(pull_request_comment_attributes(comment)) rescue ActiveRecord::RecordInvalid => e - Rails.log.error("Bitbucket importer ERROR: Invalid standalone pull request comment #{e.message}") + Rails.log.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid standalone pull request comment #{e.message}") nil end end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 36893751ee0..ef4fc9fd08e 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -98,7 +98,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do it 'map statuses to open or closed' do # HACK: Bitbucket::Representation.const_get('Issue') seems to return Issue without this - Bitbucket::Representation::Issue.new + Bitbucket::Representation::Issue.new({}) importer.execute expect(project.issues.where(state: "closed").size).to eq(5) From 1e62a13968cc4351684f919630cd426e20fc022a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Nov 2016 16:55:31 +0100 Subject: [PATCH 042/386] Improve pipeline fixtures --- db/fixtures/development/14_pipelines.rb | 63 +++++++++++++++++-------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb index 08ad3097d34..a019660e5f2 100644 --- a/db/fixtures/development/14_pipelines.rb +++ b/db/fixtures/development/14_pipelines.rb @@ -1,26 +1,50 @@ class Gitlab::Seeder::Pipelines STAGES = %w[build test deploy notify] BUILDS = [ - { name: 'build:linux', stage: 'build', status: :success }, - { name: 'build:osx', stage: 'build', status: :success }, - { name: 'rspec:linux 0 3', stage: 'test', status: :success }, - { name: 'rspec:linux 1 3', stage: 'test', status: :success }, - { name: 'rspec:linux 2 3', stage: 'test', status: :success }, - { name: 'rspec:windows 0 3', stage: 'test', status: :success }, - { name: 'rspec:windows 1 3', stage: 'test', status: :success }, - { name: 'rspec:windows 2 3', stage: 'test', status: :success }, - { name: 'rspec:windows 2 3', stage: 'test', status: :success }, - { name: 'rspec:osx', stage: 'test', status_event: :success }, - { name: 'spinach:linux', stage: 'test', status: :success }, - { name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true}, - { name: 'env:alpha', stage: 'deploy', environment: 'alpha', status: :pending }, - { name: 'env:beta', stage: 'deploy', environment: 'beta', status: :running }, - { name: 'env:gamma', stage: 'deploy', environment: 'gamma', status: :canceled }, - { name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success, options: { environment: { on_stop: 'stop staging' } } }, - { name: 'stop staging', stage: 'deploy', environment: 'staging', when: 'manual', status: :skipped }, - { name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :skipped }, + # build stage + { name: 'build:linux', stage: 'build', status: :success, + queued_at: 10.hour.ago, started_at: 9.hour.ago, finished_at: 8.hour.ago }, + { name: 'build:osx', stage: 'build', status: :success, + queued_at: 10.hour.ago, started_at: 10.hour.ago, finished_at: 9.hour.ago }, + + # test stage + { name: 'rspec:linux 0 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:linux 1 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:linux 2 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:windows 0 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:windows 1 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:windows 2 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:windows 2 3', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'rspec:osx', stage: 'test', status_event: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'spinach:linux', stage: 'test', status: :success, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + { name: 'spinach:osx', stage: 'test', status: :failed, allow_failure: true, + queued_at: 8.hour.ago, started_at: 8.hour.ago, finished_at: 7.hour.ago }, + + # deploy stage + { name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success, + options: { environment: { action: 'start', on_stop: 'stop staging' } }, + queued_at: 7.hour.ago, started_at: 6.hour.ago, finished_at: 4.hour.ago }, + { name: 'stop staging', stage: 'deploy', environment: 'staging', + when: 'manual', status: :skipped }, + { name: 'production', stage: 'deploy', environment: 'production', + when: 'manual', status: :skipped }, + + # notify stage { name: 'slack', stage: 'notify', when: 'manual', status: :created }, ] + EXTERNAL_JOBS = [ + { name: 'jenkins', stage: 'test', status: :success, + queued_at: 7.hour.ago, started_at: 6.hour.ago, finished_at: 4.hour.ago }, + ] def initialize(project) @project = project @@ -30,11 +54,12 @@ class Gitlab::Seeder::Pipelines pipelines.each do |pipeline| begin BUILDS.each { |opts| build_create!(pipeline, opts) } - commit_status_create!(pipeline, name: 'jenkins', stage: 'test', status: :success) + EXTERNAL_JOBS.each { |opts| commit_status_create!(pipeline, opts) } print '.' rescue ActiveRecord::RecordInvalid print 'F' ensure + pipeline.update_duration pipeline.update_status end end From 54221b5a3b9a2489f979944c77298c4adf004984 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 5 Dec 2016 20:34:11 +0200 Subject: [PATCH 043/386] Fix inline comment importing for 1:1 diff type --- .../representation/pull_request_comment.rb | 2 +- lib/gitlab/bitbucket_import/importer.rb | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb index 94719edbf38..38090188919 100644 --- a/lib/bitbucket/representation/pull_request_comment.rb +++ b/lib/bitbucket/representation/pull_request_comment.rb @@ -14,7 +14,7 @@ module Bitbucket end def new_pos - inline.fetch('to', nil) || 1 + inline.fetch('to', nil) || old_pos || 1 end def parent_id diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 08705afcabb..6438c8a52e4 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -81,10 +81,10 @@ module Gitlab description: description, source_project: project, source_branch: pull_request.source_branch_name, - source_branch_sha: pull_request.source_branch_sha, + source_branch_sha: project.repository.rugged.lookup(pull_request.source_branch_sha).oid, target_project: project, target_branch: pull_request.target_branch_name, - target_branch_sha: pull_request.target_branch_sha, + target_branch_sha: project.repository.rugged.lookup(pull_request.target_branch_sha).oid, state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil, @@ -94,7 +94,7 @@ module Gitlab import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue ActiveRecord::RecordInvalid - Rails.log.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request #{e.message}") + Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request #{e.message}") end end end @@ -128,24 +128,36 @@ module Gitlab begin attributes = pull_request_comment_attributes(comment) attributes.merge!( - commit_id: pull_request.source_branch_sha, + position: build_position(merge_request, comment), line_code: line_code_map.fetch(comment.iid), - type: 'LegacyDiffNote') + type: 'DiffNote') merge_request.notes.create!(attributes) rescue ActiveRecord::RecordInvalid => e - Rails.log.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request comment #{e.message}") + Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request comment #{e.message}") nil end end end + def build_position(merge_request, pr_comment) + params = { + diff_refs: merge_request.diff_refs, + old_path: pr_comment.file_path, + new_path: pr_comment.file_path, + old_line: pr_comment.old_pos, + new_line: pr_comment.new_pos + } + + Gitlab::Diff::Position.new(params) + end + def import_standalone_pr_comments(pr_comments, merge_request) pr_comments.each do |comment| begin merge_request.notes.create!(pull_request_comment_attributes(comment)) rescue ActiveRecord::RecordInvalid => e - Rails.log.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid standalone pull request comment #{e.message}") + Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid standalone pull request comment #{e.message}") nil end end From 84f2c219aa33de4890c7681372dd03309f216795 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 6 Dec 2016 13:46:59 +0200 Subject: [PATCH 044/386] Fix importing inline comment for any diff type --- lib/bitbucket/representation/pull_request_comment.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb index 38090188919..c63d749cba7 100644 --- a/lib/bitbucket/representation/pull_request_comment.rb +++ b/lib/bitbucket/representation/pull_request_comment.rb @@ -10,11 +10,11 @@ module Bitbucket end def old_pos - inline.fetch('from', nil) || 1 + inline.fetch('from', nil) end def new_pos - inline.fetch('to', nil) || old_pos || 1 + inline.fetch('to', nil) end def parent_id From ee8433466ee77c1da026842e965b32ebefab6f13 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 6 Dec 2016 17:12:11 +0200 Subject: [PATCH 045/386] Fix importing PRs with not existing branches --- lib/bitbucket/connection.rb | 2 +- lib/gitlab/bitbucket_import/importer.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index c375fe16aee..e28285f119c 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -45,7 +45,7 @@ module Bitbucket @expires_at = response.expires_at @expires_in = response.expires_in @refresh_token = response.refresh_token - @connection = nil + @connection = nil end private diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 6438c8a52e4..34d93542955 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -81,10 +81,10 @@ module Gitlab description: description, source_project: project, source_branch: pull_request.source_branch_name, - source_branch_sha: project.repository.rugged.lookup(pull_request.source_branch_sha).oid, + source_branch_sha: pull_request.source_branch_sha, target_project: project, target_branch: pull_request.target_branch_name, - target_branch_sha: project.repository.rugged.lookup(pull_request.target_branch_sha).oid, + target_branch_sha: pull_request.target_branch_sha, state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil, From 43b7b0ce23d4de7055dc1cdd660b92ff03f4eb1e Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 6 Dec 2016 18:26:24 +0200 Subject: [PATCH 046/386] Fix authorization with BitBucket --- lib/omniauth/strategies/bitbucket.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/omniauth/strategies/bitbucket.rb b/lib/omniauth/strategies/bitbucket.rb index c5484c59c47..475aad5970f 100644 --- a/lib/omniauth/strategies/bitbucket.rb +++ b/lib/omniauth/strategies/bitbucket.rb @@ -16,7 +16,7 @@ module OmniAuth end uid do - raw['username'] + raw_info['username'] end info do From 67b7637e5d7d3cf3e3f5cde6e7f984ece368c48c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 7 Dec 2016 11:33:32 +0200 Subject: [PATCH 047/386] Apply review comments. Iteration 1 --- lib/bitbucket/client.rb | 29 +++++++++---------- lib/bitbucket/connection.rb | 8 ++--- lib/bitbucket/paginator.rb | 2 +- lib/bitbucket/representation/issue.rb | 6 +--- .../representation/pull_request_comment.rb | 2 +- lib/bitbucket/representation/repo.rb | 6 ++-- lib/gitlab/bitbucket_import/importer.rb | 2 +- 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 33e977d655d..9fa44506374 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -5,40 +5,39 @@ module Bitbucket end def issues(repo) - relative_path = "/repositories/#{repo}/issues" - paginator = Paginator.new(connection, relative_path, :issue) + path = "/repositories/#{repo}/issues" + paginator = Paginator.new(connection, path, :issue) Collection.new(paginator) end - def issue_comments(repo, number) - relative_path = "/repositories/#{repo}/issues/#{number}/comments" - paginator = Paginator.new(connection, relative_path, :url) + def issue_comments(repo, issue_id) + path = "/repositories/#{repo}/issues/#{issue_id}/comments" + paginator = Paginator.new(connection, path, :url) Collection.new(paginator).map do |comment_url| - parsed_response = connection.get(comment_url.to_s) - Representation::Comment.new(parsed_response) + Representation::Comment.new(connection.get(comment_url.to_s)) end end def pull_requests(repo) - relative_path = "/repositories/#{repo}/pullrequests?state=ALL" - paginator = Paginator.new(connection, relative_path, :pull_request) + path = "/repositories/#{repo}/pullrequests?state=ALL" + paginator = Paginator.new(connection, path, :pull_request) Collection.new(paginator) end def pull_request_comments(repo, pull_request) - relative_path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" - paginator = Paginator.new(connection, relative_path, :pull_request_comment) + path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" + paginator = Paginator.new(connection, path, :pull_request_comment) Collection.new(paginator) end def pull_request_diff(repo, pull_request) - relative_path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" + path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" - connection.get(relative_path) + connection.get(path) end def repo(name) @@ -47,8 +46,8 @@ module Bitbucket end def repos - relative_path = "/repositories/#{user.username}" - paginator = Paginator.new(connection, relative_path, :repo) + path = "/repositories/#{user.username}" + paginator = Paginator.new(connection, path, :repo) Collection.new(paginator) end diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index e28285f119c..692a596c057 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -5,8 +5,8 @@ module Bitbucket DEFAULT_QUERY = {} def initialize(options = {}) - @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) - @base_uri = options.fetch(:base_uri, DEFAULT_BASE_URI) + @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) + @base_uri = options.fetch(:base_uri, DEFAULT_BASE_URI) @default_query = options.fetch(:query, DEFAULT_QUERY) @token = options[:token] @@ -23,7 +23,7 @@ module Bitbucket @connection ||= OAuth2::AccessToken.new(client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) end - def query(params = {}) + def set_default_query_parameters(params = {}) @default_query.merge!(params) end @@ -63,7 +63,7 @@ module Bitbucket end def provider - Gitlab.config.omniauth.providers.find { |provider| provider.name == 'bitbucket' } + Gitlab::OAuth::Provider.config_for('bitbucket') end def options diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb index a1672d9eaa1..d0e23007ff8 100644 --- a/lib/bitbucket/paginator.rb +++ b/lib/bitbucket/paginator.rb @@ -8,7 +8,7 @@ module Bitbucket @url = url @page = nil - connection.query(pagelen: PAGE_LENGTH, sort: :created_on) + connection.set_default_query_parameters(pagelen: PAGE_LENGTH, sort: :created_on) end def next diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index 48647ad51f6..dc034c19750 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -8,7 +8,7 @@ module Bitbucket end def author - reporter.fetch('username', 'Anonymous') + raw.dig('reporter', 'username') || 'Anonymous' end def description @@ -40,10 +40,6 @@ module Bitbucket def closed? CLOSED_STATUS.include?(raw['state']) end - - def reporter - raw.fetch('reporter', {}) - end end end end diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb index c63d749cba7..ae2b069d6a2 100644 --- a/lib/bitbucket/representation/pull_request_comment.rb +++ b/lib/bitbucket/representation/pull_request_comment.rb @@ -18,7 +18,7 @@ module Bitbucket end def parent_id - raw.fetch('parent', {}).fetch('id', nil) + raw.dig('parent', 'id') end def inline? diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb index b291dfe0441..8969ecd1c19 100644 --- a/lib/bitbucket/representation/repo.rb +++ b/lib/bitbucket/representation/repo.rb @@ -23,7 +23,9 @@ module Bitbucket url = raw['links']['clone'].find { |link| link['name'] == 'https' }.fetch('href') if token.present? - url.sub(/^[^\@]*/, "https://x-token-auth:#{token}") + clone_url = URI::parse(url) + clone_url.user = "x-token-auth:#{token}" + clone_url.to_s else url end @@ -37,7 +39,7 @@ module Bitbucket raw['full_name'] end - def has_issues? + def issues_enabled? raw['has_issues'] end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 34d93542955..0f583b07e93 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -32,7 +32,7 @@ module Gitlab end def import_issues - return unless repo.has_issues? + return unless repo.issues_enabled? client.issues(repo).each do |issue| description = @formatter.author_line(issue.author) From b12d6541835024eb74384551b84bf0e74747d0c3 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 7 Dec 2016 14:00:06 +0200 Subject: [PATCH 048/386] BitBuckpet importer. Refactoring. Iteration 2 --- app/controllers/import/bitbucket_controller.rb | 2 +- lib/bitbucket/client.rb | 6 ++---- lib/bitbucket/page.rb | 6 ++---- lib/bitbucket/paginator.rb | 4 ++-- lib/bitbucket/representation/base.rb | 4 ++++ lib/bitbucket/representation/url.rb | 9 --------- lib/gitlab/bitbucket_import/importer.rb | 7 +++++++ 7 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 lib/bitbucket/representation/url.rb diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 9c97a97a5dd..12716d60e7d 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -62,7 +62,7 @@ class Import::BitbucketController < Import::BaseController end def provider - Gitlab.config.omniauth.providers.find { |provider| provider.name == 'bitbucket' } + Gitlab::OAuth::Provider.config_for('bitbucket') end def options diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 9fa44506374..3457c2c6454 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -13,11 +13,9 @@ module Bitbucket def issue_comments(repo, issue_id) path = "/repositories/#{repo}/issues/#{issue_id}/comments" - paginator = Paginator.new(connection, path, :url) + paginator = Paginator.new(connection, path, :comment) - Collection.new(paginator).map do |comment_url| - Representation::Comment.new(connection.get(comment_url.to_s)) - end + Collection.new(paginator) end def pull_requests(repo) diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb index 49d083cc66f..8f50f67f84d 100644 --- a/lib/bitbucket/page.rb +++ b/lib/bitbucket/page.rb @@ -18,14 +18,12 @@ module Bitbucket private def parse_attrs(raw) - attrs = %w(size page pagelen next previous) - attrs.map { |attr| { attr.to_sym => raw[attr] } }.reduce(&:merge) + raw.slice(*%w(size page pagelen next previous)).symbolize_keys end def parse_values(raw, bitbucket_rep_class) return [] unless raw['values'] && raw['values'].is_a?(Array) - - raw['values'].map { |hash| bitbucket_rep_class.new(hash) } + bitbucket_rep_class.decorate(raw['values']) end def representation_class(type) diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb index d0e23007ff8..37f12328447 100644 --- a/lib/bitbucket/paginator.rb +++ b/lib/bitbucket/paginator.rb @@ -26,12 +26,12 @@ module Bitbucket page.nil? || page.next? end - def page_url + def next_url page.nil? ? url : page.next end def fetch_next_page - parsed_response = connection.get(page_url) + parsed_response = connection.get(next_url) Page.new(parsed_response, type) end end diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb index 7b639492d38..94adaacc9b5 100644 --- a/lib/bitbucket/representation/base.rb +++ b/lib/bitbucket/representation/base.rb @@ -5,6 +5,10 @@ module Bitbucket @raw = raw end + def self.decorate(entries) + entries.map { |entry| new(entry)} + end + private attr_reader :raw diff --git a/lib/bitbucket/representation/url.rb b/lib/bitbucket/representation/url.rb deleted file mode 100644 index 24ae1048013..00000000000 --- a/lib/bitbucket/representation/url.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Bitbucket - module Representation - class Url < Representation::Base - def to_s - raw.dig('links', 'self', 'href') - end - end - end -end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 0f583b07e93..825d43e6589 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -50,6 +50,13 @@ module Gitlab if issue.persisted? client.issue_comments(repo, issue.iid).each do |comment| + # The note can be blank for issue service messages like "Chenged title: ..." + # We would like to import those comments as well but there is no any + # specific parameter that would allow to process them, it's just an empty comment. + # To prevent our importer from just crashing or from creating useless empty comments + # we do this check. + next unless comment.note.present? + note = @formatter.author_line(comment.author) note += comment.note From 98c0eb0f75692b1281adda9bfb75e1fcc12cec6d Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 7 Dec 2016 15:54:32 +0200 Subject: [PATCH 049/386] BitBucket refactoring. Iteration 3 --- lib/bitbucket/client.rb | 26 ++++++++++--------------- lib/bitbucket/collection.rb | 2 +- lib/bitbucket/paginator.rb | 2 +- lib/gitlab/bitbucket_import/importer.rb | 6 +++--- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 3457c2c6454..e23da4556aa 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -6,35 +6,26 @@ module Bitbucket def issues(repo) path = "/repositories/#{repo}/issues" - paginator = Paginator.new(connection, path, :issue) - - Collection.new(paginator) + get_collection(path, :issue) end def issue_comments(repo, issue_id) path = "/repositories/#{repo}/issues/#{issue_id}/comments" - paginator = Paginator.new(connection, path, :comment) - - Collection.new(paginator) + get_collection(path, :comment) end def pull_requests(repo) path = "/repositories/#{repo}/pullrequests?state=ALL" - paginator = Paginator.new(connection, path, :pull_request) - - Collection.new(paginator) + get_collection(path, :pull_request) end def pull_request_comments(repo, pull_request) path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" - paginator = Paginator.new(connection, path, :pull_request_comment) - - Collection.new(paginator) + get_collection(path, :pull_request_comment) end def pull_request_diff(repo, pull_request) path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" - connection.get(path) end @@ -45,9 +36,7 @@ module Bitbucket def repos path = "/repositories/#{user.username}" - paginator = Paginator.new(connection, path, :repo) - - Collection.new(paginator) + get_collection(path, :repo) end def user @@ -60,5 +49,10 @@ module Bitbucket private attr_reader :connection + + def get_collection(path, type) + paginator = Paginator.new(connection, path, type) + Collection.new(paginator) + end end end diff --git a/lib/bitbucket/collection.rb b/lib/bitbucket/collection.rb index 9cc8947417c..3a9379ff680 100644 --- a/lib/bitbucket/collection.rb +++ b/lib/bitbucket/collection.rb @@ -3,7 +3,7 @@ module Bitbucket def initialize(paginator) super() do |yielder| loop do - paginator.next.each { |item| yielder << item } + paginator.items.each { |item| yielder << item } end end diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb index 37f12328447..641a6ed79d6 100644 --- a/lib/bitbucket/paginator.rb +++ b/lib/bitbucket/paginator.rb @@ -11,7 +11,7 @@ module Bitbucket connection.set_default_query_parameters(pagelen: PAGE_LENGTH, sort: :created_on) end - def next + def items raise StopIteration unless has_next_page? @page = fetch_next_page diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 825d43e6589..fba382e6fea 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -50,7 +50,7 @@ module Gitlab if issue.persisted? client.issue_comments(repo, issue.iid).each do |comment| - # The note can be blank for issue service messages like "Chenged title: ..." + # The note can be blank for issue service messages like "Changed title: ..." # We would like to import those comments as well but there is no any # specific parameter that would allow to process them, it's just an empty comment. # To prevent our importer from just crashing or from creating useless empty comments @@ -70,8 +70,8 @@ module Gitlab end end end - rescue ActiveRecord::RecordInvalid - nil + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Couldn't import record properly #{e.message}") end def import_pull_requests From bd3bd9bcea11244c56a0f7b63a6afa6fe439bf01 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 7 Dec 2016 16:16:18 +0200 Subject: [PATCH 050/386] Remove outdated bitbucket_import.rb --- lib/gitlab/bitbucket_import.rb | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 lib/gitlab/bitbucket_import.rb diff --git a/lib/gitlab/bitbucket_import.rb b/lib/gitlab/bitbucket_import.rb deleted file mode 100644 index 7298152e7e9..00000000000 --- a/lib/gitlab/bitbucket_import.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Gitlab - module BitbucketImport - mattr_accessor :public_key - @public_key = nil - end -end From 00cd864237d6c7ec57ecb49d304ca8dfa9e41d31 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 7 Dec 2016 18:04:02 +0200 Subject: [PATCH 051/386] BitBucket importer: import issues with labels --- lib/bitbucket/representation/issue.rb | 4 ++++ lib/gitlab/bitbucket_import/importer.rb | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index dc034c19750..6c8e9a4c244 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -7,6 +7,10 @@ module Bitbucket raw['id'] end + def kind + raw['kind'] + end + def author raw.dig('reporter', 'username') || 'Anonymous' end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index fba382e6fea..8852f5b0f3f 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -1,12 +1,18 @@ module Gitlab module BitbucketImport class Importer + LABELS = [{ title: 'bug', color: '#FF0000'}, + { title: 'enhancement', color: '#428BCA'}, + { title: 'proposal', color: '#69D100'}, + { title: 'task', color: '#7F8C8D'}].freeze + attr_reader :project, :client def initialize(project) @project = project @client = Bitbucket::Client.new(project.import_data.credentials) @formatter = Gitlab::ImportFormatter.new + @labels = {} end def execute @@ -34,10 +40,14 @@ module Gitlab def import_issues return unless repo.issues_enabled? + create_labels + client.issues(repo).each do |issue| description = @formatter.author_line(issue.author) description += issue.description + label_name = issue.kind + issue = project.issues.create( iid: issue.iid, title: issue.title, @@ -48,6 +58,8 @@ module Gitlab updated_at: issue.updated_at ) + assign_label(issue, label_name) + if issue.persisted? client.issue_comments(repo, issue.iid).each do |comment| # The note can be blank for issue service messages like "Changed title: ..." @@ -74,6 +86,16 @@ module Gitlab Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Couldn't import record properly #{e.message}") end + def create_labels + LABELS.each do |label| + @labels[label[:title]] = project.labels.create!(label) + end + end + + def assign_label(issue, label_name) + issue.labels << @labels[label_name] + end + def import_pull_requests pull_requests = client.pull_requests(repo) From c3c9122d1e6aa532ad213394c4758653d4cf3874 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 5 Dec 2016 17:04:36 +0000 Subject: [PATCH 052/386] Adds new hover state Fixes the line that connects the dots Adds style for the badge Add new style for status text Fix badge style Adjust font weight --- .../stylesheets/framework/variables.scss | 7 ++++ app/assets/stylesheets/pages/pipelines.scss | 34 ++++++++++++++----- .../commit/_pipeline_status_group.html.haml | 2 +- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 18716813c48..0591801d259 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -571,3 +571,10 @@ $body-text-shadow: rgba(255,255,255,0.01); */ $ui-dev-kit-example-color: #bbb; $ui-dev-kit-example-border: #ddd; + +/* +Pipeline Graph +*/ +$stage-hover-bg: #eaf3fc; +$stage-hover-border: #d1e7fc; +$stage-bagde-text: #d4d4d4; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 08062b85504..cc8d4dd9544 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -363,15 +363,22 @@ .build { border: 1px solid $border-color; + border-radius: 30px; background-color: $white-light; + color: $gl-text-color; position: relative; padding: 7px 10px 8px; - border-radius: 30px; width: 186px; margin-bottom: 10px; &:hover { - background-color: $gray-lighter; + background-color: $stage-hover-bg; + border: 1px solid $stage-hover-border; + + .ci-status-text, + .dropdown-counter-bagde { + color: $gl-text-color; + } } &.playable { @@ -411,14 +418,14 @@ } .ci-status-text { - width: 135px; + max-width: 110px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - vertical-align: middle; + vertical-align: bottom; display: inline-block; position: relative; - top: -1px; + font-weight: 100; } a { @@ -435,7 +442,7 @@ flex-grow: 1; .ci-status-text { - max-width: 112px; + max-width: 110px; width: auto; } } @@ -480,7 +487,7 @@ } .ci-status-text { - width: 112px; + width: 110px; } .arrow { @@ -520,9 +527,18 @@ } } + .dropdown-counter-bagde { + float: right; + color: $stage-bagde-text; + font-weight: 100; + font-size: 13px; + margin-top: 1px; + margin-right: 2px; + } + svg { vertical-align: middle; - margin-right: 5px; + margin-right: 3px; } // Connect first build in each stage with right horizontal line @@ -531,7 +547,7 @@ content: ''; position: absolute; top: 48%; - right: -48px; + right: -49px; border-top: 2px solid $border-color; width: 48px; height: 1px; diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml index 2b26ad9d6fa..1e91e249fe9 100644 --- a/app/views/projects/commit/_pipeline_status_group.html.haml +++ b/app/views/projects/commit/_pipeline_status_group.html.haml @@ -4,7 +4,7 @@ = ci_icon_for_status(group_status) %span.ci-status-text = name - %span.badge= subject.size + %span.dropdown-counter-bagde= subject.size .dropdown-menu.grouped-pipeline-dropdown .arrow %ul From f9c103c2f314a2f9edbdfb93a26ace597d62e7e6 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 6 Dec 2016 11:56:50 +0000 Subject: [PATCH 053/386] Fix tooltip to show the all name CSS - Changes to look like newest mockups - Simplifies nested elements - Divides nodes from lines Remove is playable from left side Remove nested elements in scss Improve dropdown Focus state Fix scss linter Remove not used css Fix typo --- app/assets/stylesheets/pages/pipelines.scss | 520 ++++++++---------- .../ci/builds/_build_pipeline.html.haml | 6 +- .../commit/_pipeline_status_group.html.haml | 2 +- 3 files changed, 242 insertions(+), 286 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index cc8d4dd9544..cd7df7beda8 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -287,15 +287,40 @@ } // Pipeline visualization +.pipeline-actions { + border-bottom: none; +} -.toggle-pipeline-btn { - background-color: $gray-dark; +.tab-pane { + &.pipelines { + .ci-table { + min-width: 900px; + } - &.graph-collapsed { - background-color: $white-light; + .content-list.pipelines { + overflow: auto; + } + + .stage { + max-width: 100px; + width: 100px; + } + + .pipeline-actions { + min-width: initial; + } + } + + &.builds { + .ci-table { + tr { + height: 71px; + } + } } } +// Pipeline graph .pipeline-graph { width: 100%; background-color: $background-color; @@ -304,52 +329,120 @@ white-space: nowrap; transition: max-height 0.3s, padding 0.3s; - &.graph-collapsed { - max-height: 0; - padding: 0 16px; - } -} - -.pipeline-visualization { - position: relative; - ul { padding: 0; } -} -.stage-column { - display: inline-block; - vertical-align: top; - - &:not(:last-child) { - margin-right: 44px; + a { + text-decoration: none; + color: $gl-text-color-light; } - &.left-margin { - &:not(:first-child) { - margin-left: 44px; + svg { + vertical-align: middle; + margin-right: 3px; + } - .left-connector { - &::before { - content: ''; - position: absolute; - top: 48%; - left: -48px; - border-top: 2px solid $border-color; - width: 48px; - height: 1px; + .stage-column { + display: inline-block; + vertical-align: top; + + &:not(:last-child) { + margin-right: 44px; + } + + &.left-margin { + &:not(:first-child) { + margin-left: 44px; + + .left-connector { + &::before { + content: ''; + position: absolute; + top: 48%; + left: -48px; + border-top: 2px solid $border-color; + width: 48px; + height: 1px; + } } } } - } - &.no-margin { - margin: 0; - } + &.no-margin { + margin: 0; + } - li { - list-style: none; + li { + list-style: none; + } + + &:last-child { + .build { + // Remove right connecting horizontal line from first build in last stage + &:first-child { + &::after { + border: none; + } + } + // Remove right curved connectors from all builds in last stage + &:not(:first-child) { + &::after { + border: none; + } + } + // Remove opposite curve + .curve { + &::before { + display: none; + } + } + } + } + + &:first-child { + .build { + // Remove left curved connectors from all builds in first stage + &:not(:first-child) { + &::before { + border: none; + } + } + // Remove opposite curve + .curve { + &::after { + display: none; + } + } + } + } + + // Curve first child connecting lines in opposite direction + .curve { + display: none; + + &::before, + &::after { + content: ''; + width: 21px; + height: 25px; + position: absolute; + top: -31px; + border-top: 2px solid $border-color; + } + + &::after { + left: -44px; + border-right: 2px solid $border-color; + border-radius: 0 20px; + } + + &::before { + right: -44px; + border-left: 2px solid $border-color; + border-radius: 20px 0 0; + } + } } .stage-name { @@ -365,24 +458,12 @@ border: 1px solid $border-color; border-radius: 30px; background-color: $white-light; - color: $gl-text-color; position: relative; - padding: 7px 10px 8px; + padding: 8px 10px 9px; width: 186px; margin-bottom: 10px; - &:hover { - background-color: $stage-hover-bg; - border: 1px solid $stage-hover-border; - - .ci-status-text, - .dropdown-counter-bagde { - color: $gl-text-color; - } - } - &.playable { - svg { height: 13px; width: 20px; @@ -395,150 +476,56 @@ } } - .build-content { - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - width: 164px; - - .ci-status-icon { - svg { - height: 20px; - width: 20px; - } - } - - .tooltip { - white-space: nowrap; - - .tooltip-inner { - overflow: hidden; - text-overflow: ellipsis; - } - } - - .ci-status-text { - max-width: 110px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: bottom; - display: inline-block; - position: relative; - font-weight: 100; - } - - a { - color: $gl-text-color-light; - text-decoration: none; - } + &:hover { + background-color: $stage-hover-bg; + border: 1px solid $stage-hover-border; + a, + .dropdown-counter-badge, .dropdown-menu-toggle { - background-color: transparent; - border: none; - width: auto; - padding: 0; - color: $gl-text-color-light; - flex-grow: 1; - - .ci-status-text { - max-width: 110px; - width: auto; - } + color: $gl-text-color; } - .grouped-pipeline-dropdown { - padding: 0; - width: 186px; - left: auto; - right: -197px; - top: -9px; + .grouped-pipeline-dropdown a { + color: $gl-text-color-light; - ul { - max-height: 245px; - overflow: auto; - - li:first-child { - padding-top: 8px; - } - - li:last-child { - padding-bottom: 8px; - } - } - - a { + &:hover { color: $gl-text-color; - padding: 7px 8px 8px; - - &:hover { - background-color: $blue-light-transparent; - border-radius: 3px; - - .ci-status-text { - text-decoration: none; - } - } } - - svg { - width: 14px; - height: 14px; - } - - .ci-status-text { - width: 110px; - } - - .arrow { - &::before, - &::after { - content: ''; - display: inline-block; - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; - top: 18px; - } - - &::before { - left: -5px; - margin-top: -6px; - border-width: 7px 5px 7px 0; - border-right-color: $border-color; - } - - &::after { - left: -4px; - margin-top: -9px; - border-width: 10px 7px 10px 0; - border-right-color: $white-light; - } - } - } - - .badge { - background-color: $gray-darker; - color: $gl-text-color-light; - font-weight: normal; - margin-left: $btn-xs-side-margin; } } - .dropdown-counter-bagde { - float: right; - color: $stage-bagde-text; - font-weight: 100; - font-size: 13px; - margin-top: 1px; - margin-right: 2px; + .ci-status-icon svg { + height: 20px; + width: 20px; } - svg { - vertical-align: middle; - margin-right: 3px; + .arrow { + &::before, + &::after { + content: ''; + display: inline-block; + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + top: 18px; + } + + &::before { + left: -5px; + margin-top: -6px; + border-width: 7px 5px 7px 0; + border-right-color: $border-color; + } + + &::after { + left: -4px; + margin-top: -9px; + border-width: 10px 7px 10px 0; + border-right-color: $white-light; + } } // Connect first build in each stage with right horizontal line @@ -595,113 +582,86 @@ } } - &:last-child { - .build { - // Remove right connecting horizontal line from first build in last stage - &:first-child { - &::after { - border: none; - } - } - // Remove right curved connectors from all builds in last stage - &:not(:first-child) { - &::after { - border: none; - } - } - // Remove opposite curve - .curve { - &::before { - display: none; - } + .ci-status-text { + max-width: 110px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: bottom; + display: inline-block; + position: relative; + font-weight: 100; + } + + .dropdown-menu-toggle { + background-color: transparent; + border: none; + padding: 0; + color: $gl-text-color-light; + flex-grow: 1; + + &:focus { + outline: none; + } + + &:hover { + color: $gl-text-color; + + .dropdown-counter-bagde { + color: $gl-text-color; } } } - &:first-child { - .build { - // Remove left curved connectors from all builds in first stage - &:not(:first-child) { - &::before { - border: none; - } - } - // Remove opposite curve - .curve { - &::after { - display: none; - } - } - } + .dropdown-counter-bagde { + float: right; + color: $stage-bagde-text; + font-weight: 100; + font-size: 13px; + margin-top: 1px; + margin-right: 2px; } - // Curve first child connecting lines in opposite direction - .curve { - display: none; + .grouped-pipeline-dropdown { + padding: 0; + width: 191px; + left: auto; + right: -206px; + top: -11px; + box-shadow: 0 1px 5px $black-transparent; - &::before, - &::after { - content: ''; - width: 21px; - height: 25px; - position: absolute; - top: -32px; - border-top: 2px solid $border-color; - } - - &::after { - left: -44px; - border-right: 2px solid $border-color; - border-radius: 0 20px; - } - - &::before { - right: -44px; - border-left: 2px solid $border-color; - border-radius: 20px 0 0; - } - } -} - -.pipeline-actions { - border-bottom: none; -} - -.toggle-pipeline-btn { - - .fa { - color: $dropdown-header-color; - } -} - -.tab-pane { - - &.pipelines { - - .ci-table { - min-width: 900px; - } - - .content-list.pipelines { + ul { + max-height: 245px; overflow: auto; - } - .stage { - max-width: 100px; - width: 100px; - } - - .pipeline-actions { - min-width: initial; - } - } - - &.builds { - - .ci-table { - tr { - height: 71px; + li { + padding-top: 1px; + padding-bottom: 1px; } + + li:first-child { + padding-top: 9px; + } + + li:last-child { + padding-bottom: 9px; + } + } + + a { + color: $gl-text-color-light; + padding: 7px 8px 8px; + + &:hover { + background-color: $stage-hover-bg; + border-radius: 3px; + color: $gl-text-color; + } + } + + .ci-status-icon svg { + height: 18px; + width: 18px; } } } diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml index 423a1282eb2..ac90d3278e2 100644 --- a/app/views/projects/ci/builds/_build_pipeline.html.haml +++ b/app/views/projects/ci/builds/_build_pipeline.html.haml @@ -1,9 +1,5 @@ - is_playable = subject.playable? && can?(current_user, :update_build, @project) -- if is_playable - = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do - = ci_icon_for_status('play') - .ci-status-text= subject.name -- elsif can?(current_user, :read_build, @project) +- if can?(current_user, :read_build, @project) = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} = ci_icon_for_status(subject.status) diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml index 1e91e249fe9..8b782d38193 100644 --- a/app/views/projects/commit/_pipeline_status_group.html.haml +++ b/app/views/projects/commit/_pipeline_status_group.html.haml @@ -4,7 +4,7 @@ = ci_icon_for_status(group_status) %span.ci-status-text = name - %span.dropdown-counter-bagde= subject.size + %span.dropdown-counter-badge= subject.size .dropdown-menu.grouped-pipeline-dropdown .arrow %ul From bd30f75af67240d4069d4a4559faecf4ad7fab5a Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 7 Dec 2016 18:03:40 +0000 Subject: [PATCH 054/386] Adds actions to the nodes Improve CSS for dropdown actions --- .../stylesheets/framework/variables.scss | 2 +- app/assets/stylesheets/pages/pipelines.scss | 84 ++++++++++++++----- .../ci/builds/_build_pipeline.html.haml | 22 +++++ .../commit/_pipeline_status_group.html.haml | 2 +- 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 0591801d259..cfb2ed9321b 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -577,4 +577,4 @@ Pipeline Graph */ $stage-hover-bg: #eaf3fc; $stage-hover-border: #d1e7fc; -$stage-bagde-text: #d4d4d4; +$stage-badge-text: #d4d4d4; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index cd7df7beda8..81cd397ef14 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -463,19 +463,6 @@ width: 186px; margin-bottom: 10px; - &.playable { - svg { - height: 13px; - width: 20px; - position: relative; - top: 1px; - - path { - fill: $layout-link-gray; - } - } - } - &:hover { background-color: $stage-hover-bg; border: 1px solid $stage-hover-border; @@ -607,15 +594,15 @@ &:hover { color: $gl-text-color; - .dropdown-counter-bagde { + .dropdown-counter-badge { color: $gl-text-color; } } } - .dropdown-counter-bagde { + .dropdown-counter-badge { float: right; - color: $stage-bagde-text; + color: $stage-badge-text; font-weight: 100; font-size: 13px; margin-top: 1px; @@ -630,27 +617,49 @@ top: -11px; box-shadow: 0 1px 5px $black-transparent; + a { + display: inline-block; + + &:hover { + background-color: $stage-hover-bg; + } + } + ul { max-height: 245px; overflow: auto; li { - padding-top: 1px; - padding-bottom: 1px; + padding-top: 2px; + margin: 0 5px; } li:first-child { - padding-top: 9px; + padding-top: 6px; } li:last-child { - padding-bottom: 9px; + padding-bottom: 6px; } } - a { + .dropdown-build { color: $gl-text-color-light; - padding: 7px 8px 8px; + + a { + padding: 7px 8px 8px; + } + + a.ci-action-icon-container { + padding: 0; + font-size: 11px; + float: right; + margin-top: 5px; + + i { + font-size: 11px; + } + } &:hover { background-color: $stage-hover-bg; @@ -665,3 +674,34 @@ } } } + +// Action Icons +.ci-action-icon-container { + padding: 0; + + .ci-action-icon-wrapper { + display: inline-block; + float: right; + + i { + color: $stage-badge-text; + border-radius: 100%; + border: 1px solid $stage-badge-text; + text-align: center; + display: table-cell; + vertical-align: middle; + padding: 5px; + font-size: 13px; + + &:hover { + color: $gl-text-color; + background-color: $stage-hover-bg; + border: 1px solid $gl-text-color; + } + } + + .ci-play-icon { + padding: 5px 4px 5px 7px; + } + } +} diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml index ac90d3278e2..41b9265fe5e 100644 --- a/app/views/projects/ci/builds/_build_pipeline.html.haml +++ b/app/views/projects/ci/builds/_build_pipeline.html.haml @@ -1,9 +1,31 @@ - is_playable = subject.playable? && can?(current_user, :update_build, @project) +- can_cancel = subject.active? && can?(current_user, :update_build, @project) +- can_retry = subject.retryable? && can?(current_user, :update_build, @project) +- can_stop = subject.complete? && subject.stops_environment? && can?(current_user, :update_build, @project) + - if can?(current_user, :read_build, @project) = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} = ci_icon_for_status(subject.status) .ci-status-text= subject.name + + - if is_playable + = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Play", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon('play', class: 'ci-play-icon') + - elsif can_cancel + = link_to cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Cancel", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon('ban') + - elsif can_retry + = link_to retry_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Retry", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon('refresh') + - elsif can_stop + = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Stop", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon('stop') + - else %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} = ci_icon_for_status(subject.status) diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml index 8b782d38193..2d198d1b389 100644 --- a/app/views/projects/commit/_pipeline_status_group.html.haml +++ b/app/views/projects/commit/_pipeline_status_group.html.haml @@ -9,5 +9,5 @@ .arrow %ul - subject.each do |status| - %li + %li.dropdown-build = render "projects/#{status.to_partial_path}_pipeline", subject: status From 2320a5c7118084ed999b9fa0966c3315b94edd67 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 7 Dec 2016 20:28:29 +0000 Subject: [PATCH 055/386] Adds new icons for pipeline graph Fix padding Fix hover state of icon --- app/assets/stylesheets/pages/pipelines.scss | 5 +- app/helpers/ci_status_helper.rb | 66 ++++++++++++------- .../icons/_icon_graph_job_cancelled.svg | 1 + .../shared/icons/_icon_graph_job_created.svg | 1 + .../shared/icons/_icon_graph_job_failed.svg | 1 + .../shared/icons/_icon_graph_job_manual.svg | 1 + .../shared/icons/_icon_graph_job_pending.svg | 1 + .../shared/icons/_icon_graph_job_running.svg | 1 + .../shared/icons/_icon_graph_job_skipped.svg | 1 + .../shared/icons/_icon_graph_job_success.svg | 1 + .../shared/icons/_icon_graph_job_warning.svg | 1 + 11 files changed, 56 insertions(+), 24 deletions(-) create mode 100755 app/views/shared/icons/_icon_graph_job_cancelled.svg create mode 100755 app/views/shared/icons/_icon_graph_job_created.svg create mode 100755 app/views/shared/icons/_icon_graph_job_failed.svg create mode 100755 app/views/shared/icons/_icon_graph_job_manual.svg create mode 100755 app/views/shared/icons/_icon_graph_job_pending.svg create mode 100755 app/views/shared/icons/_icon_graph_job_running.svg create mode 100755 app/views/shared/icons/_icon_graph_job_skipped.svg create mode 100755 app/views/shared/icons/_icon_graph_job_success.svg create mode 100755 app/views/shared/icons/_icon_graph_job_warning.svg diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 81cd397ef14..06b078d5345 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -647,7 +647,7 @@ color: $gl-text-color-light; a { - padding: 7px 8px 8px; + padding: 6px 8px 7px; } a.ci-action-icon-container { @@ -692,11 +692,12 @@ vertical-align: middle; padding: 5px; font-size: 13px; + background: $white-light; &:hover { color: $gl-text-color; background-color: $stage-hover-bg; - border: 1px solid $gl-text-color; + border: 1px solid $stage-hover-bg; } } diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 8e19752a8a1..999d279e5b9 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -43,32 +43,54 @@ module CiStatusHelper status.humanize end - def ci_icon_for_status(status) + def ci_icon_for_status(status, graph: nil) if detailed_status?(status) return custom_icon(status.icon) end - icon_name = - case status - when 'success' - 'icon_status_success' - when 'success_with_warnings' - 'icon_status_warning' - when 'failed' - 'icon_status_failed' - when 'pending' - 'icon_status_pending' - when 'running' - 'icon_status_running' - when 'play' - 'icon_play' - when 'created' - 'icon_status_created' - when 'skipped' - 'icon_status_skipped' - else - 'icon_status_canceled' - end + if graph + icon_name = + case status + when 'success' + 'icon_graph_job_success' + when 'success_with_warnings' + 'icon_graph_job_warning' + when 'failed' + 'icon_graph_job_failed' + when 'pending' + 'icon_graph_job_pending' + when 'running' + 'icon_graph_job_running' + when 'created' + 'icon_graph_job_created' + when 'skipped' + 'icon_graph_job_skipped' + else + 'icon_graph_job_canceled' + end + else + icon_name = + case status + when 'success' + 'icon_status_success' + when 'success_with_warnings' + 'icon_status_warning' + when 'failed' + 'icon_status_failed' + when 'pending' + 'icon_status_pending' + when 'running' + 'icon_status_running' + when 'play' + 'icon_play' + when 'created' + 'icon_status_created' + when 'skipped' + 'icon_status_skipped' + else + 'icon_status_canceled' + end + end custom_icon(icon_name) end diff --git a/app/views/shared/icons/_icon_graph_job_cancelled.svg b/app/views/shared/icons/_icon_graph_job_cancelled.svg new file mode 100755 index 00000000000..bd5d04e1cd7 --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_cancelled.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_created.svg b/app/views/shared/icons/_icon_graph_job_created.svg new file mode 100755 index 00000000000..326ad04e017 --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_created.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_failed.svg b/app/views/shared/icons/_icon_graph_job_failed.svg new file mode 100755 index 00000000000..64da5aa31fc --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_failed.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_manual.svg b/app/views/shared/icons/_icon_graph_job_manual.svg new file mode 100755 index 00000000000..c98839f51a9 --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_manual.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_pending.svg b/app/views/shared/icons/_icon_graph_job_pending.svg new file mode 100755 index 00000000000..02d5da407e3 --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_pending.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_running.svg b/app/views/shared/icons/_icon_graph_job_running.svg new file mode 100755 index 00000000000..532f4fee33c --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_running.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_skipped.svg b/app/views/shared/icons/_icon_graph_job_skipped.svg new file mode 100755 index 00000000000..1998dfef9ea --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_skipped.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_success.svg b/app/views/shared/icons/_icon_graph_job_success.svg new file mode 100755 index 00000000000..eed5006bebe --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_success.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_graph_job_warning.svg b/app/views/shared/icons/_icon_graph_job_warning.svg new file mode 100755 index 00000000000..cb785635b7e --- /dev/null +++ b/app/views/shared/icons/_icon_graph_job_warning.svg @@ -0,0 +1 @@ + From b4f18a30fb776e28fac405922cb5dfcfdc8ac5d7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 7 Dec 2016 22:56:47 +0000 Subject: [PATCH 056/386] Adds tests for the status and actions icons rendered in the pipeline graph Fix padding in dropdown --- app/assets/stylesheets/pages/pipelines.scss | 4 -- .../projects/pipelines/pipeline_spec.rb | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 06b078d5345..304a7932a06 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -646,10 +646,6 @@ .dropdown-build { color: $gl-text-color-light; - a { - padding: 6px 8px 7px; - } - a.ci-action-icon-container { padding: 0; font-size: 11px; diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 3350a3aeefc..e21de05ac64 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -38,6 +38,47 @@ describe "Pipelines", feature: true, js: true do expect(page).to have_css('#js-tab-pipeline.active') end + context 'pipeline graph' do + it 'shows a running icon and a cancel action for the running build' do + page.within('.stage-column:first-child .build:first-child') do + expect(page).to have_selector('.ci-status-icon-running') + expect(page).to have_content('deploy') + expect(page).to have_selector('.ci-action-icon-container .fa-ban') + end + end + + it 'shows the success icon and a retry action for the successfull build' do + page.within('.stage-column:nth-child(3)') do + expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_content('build') + expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + end + end + + it 'shows the failed icon and a retry action for the failed build' do + page.within('.stage-column:nth-child(2) .build') do + expect(page).to have_selector('.ci-status-icon-failed') + expect(page).to have_content('test') + expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + end + end + + it 'shows the skipped icon and a play action for the manual build' do + page.within('.stage-column:first-child .build:nth-child(2)') do + expect(page).to have_selector('.ci-status-icon-skipped') + expect(page).to have_content('manual') + expect(page).to have_selector('.ci-action-icon-container .ci-play-icon') + end + end + + it 'shows the success icon for the generic comit status build' do + page.within('.stage-column:nth-child(4) .build') do + expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_content('jenkins') + end + end + end + context 'page tabs' do it 'shows Pipeline and Builds tabs with link' do expect(page).to have_link('Pipeline') From f7335aa5cb1ed368f0583d7dca8fcfa7574d6ad9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 7 Dec 2016 23:09:55 +0000 Subject: [PATCH 057/386] Adds changelog entry --- changelogs/unreleased/22604-manual-actions.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/22604-manual-actions.yml diff --git a/changelogs/unreleased/22604-manual-actions.yml b/changelogs/unreleased/22604-manual-actions.yml new file mode 100644 index 00000000000..7335e597292 --- /dev/null +++ b/changelogs/unreleased/22604-manual-actions.yml @@ -0,0 +1,4 @@ +--- +title: Resolve "Manual actions on pipeline graph" +merge_request: 7931 +author: From 007255ed5833bdd7bec8927d9f197002dc519186 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 09:51:36 +0100 Subject: [PATCH 058/386] Added Ci::Status::Build --- app/models/ci/build.rb | 4 ++ app/models/commit_status.rb | 4 ++ lib/gitlab/ci/status/build/common.rb | 54 +++++++++++++++++++++++++++ lib/gitlab/ci/status/build/factory.rb | 15 ++++++++ lib/gitlab/ci/status/core.rb | 10 +++-- 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 lib/gitlab/ci/status/build/common.rb create mode 100644 lib/gitlab/ci/status/build/factory.rb diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 88c46076df6..0f4c498c266 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -100,6 +100,10 @@ module Ci end end + def detailed_status + Gitlab::Ci::Status::Build::Factory.new(self).fabricate! + end + def manual? self.when == 'manual' end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index cf90475f4d4..fce16174e22 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -131,4 +131,8 @@ class CommitStatus < ActiveRecord::Base def has_trace? false end + + def detailed_status + Gitlab::Ci::Status::Factory.new(self).fabricate! + end end diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb new file mode 100644 index 00000000000..d3d7e03ee3f --- /dev/null +++ b/lib/gitlab/ci/status/build/common.rb @@ -0,0 +1,54 @@ +module Gitlab + module Ci + module Status + module Build + module Common + def has_details? + true + end + + def details_path + namespace_project_build_path(@subject.project.namespace, + @subject.project, + @subject.pipeline) + end + + def action_type + case + when @subject.playable? then :playable + when @subject.active? then :cancel + when @subject.retryable? then :retry + end + end + + def has_action?(current_user) + action_type && can?(current_user, :update_build, @subject) + end + + def action_icon + case action_type + when :playable then 'remove' + when :cancel then 'icon_play' + when :retry then 'repeat' + end + end + + def action_path + case action_type + when :playable + play_namespace_project_build_path(subject.project.namespace, subject.project, subject) + when :cancel + cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject) + when :retry + retry_namespace_project_build_path(subject.project.namespace, subject.project, subject) + end + end + + def action_method + :post + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb new file mode 100644 index 00000000000..dd38e1418b6 --- /dev/null +++ b/lib/gitlab/ci/status/build/factory.rb @@ -0,0 +1,15 @@ +module Gitlab + module Ci + module Status + module Build + class Factory < Status::Factory + private + + def core_status + super.extend(Status::Build::Common) + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index ce4108fdcf2..60c559248aa 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -34,15 +34,15 @@ module Gitlab end def has_details? - raise NotImplementedError + false end def details_path raise NotImplementedError end - def has_action? - raise NotImplementedError + def has_action?(_user = nil) + false end def action_icon @@ -52,6 +52,10 @@ module Gitlab def action_path raise NotImplementedError end + + def action_method + raise NotImplementedError + end end end end From cd4a2270c5ccbdbd9e57e0c625eee2d80357d6be Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 10:40:56 +0100 Subject: [PATCH 059/386] Improve actions --- app/models/ci/build.rb | 4 +++ lib/gitlab/ci/status/build/common.rb | 26 +++++---------- lib/gitlab/ci/status/build/factory.rb | 4 +++ lib/gitlab/ci/status/build/play.rb | 47 +++++++++++++++++++++++++++ lib/gitlab/ci/status/build/stop.rb | 47 +++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 lib/gitlab/ci/status/build/play.rb create mode 100644 lib/gitlab/ci/status/build/stop.rb diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 0f4c498c266..73564dd2aa0 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -127,6 +127,10 @@ module Ci end end + def cancelable? + active? + end + def retryable? project.builds_enabled? && commands.present? && complete? end diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb index d3d7e03ee3f..2bed68d1a11 100644 --- a/lib/gitlab/ci/status/build/common.rb +++ b/lib/gitlab/ci/status/build/common.rb @@ -13,33 +13,23 @@ module Gitlab @subject.pipeline) end - def action_type - case - when @subject.playable? then :playable - when @subject.active? then :cancel - when @subject.retryable? then :retry - end - end - def has_action?(current_user) - action_type && can?(current_user, :update_build, @subject) + (subject.cancelable? || subject.retryable?) && + can?(current_user, :update_build, @subject) end def action_icon - case action_type - when :playable then 'remove' - when :cancel then 'icon_play' - when :retry then 'repeat' + case + when subject.cancelable? then 'icon_play' + when subject.retryable? then 'repeat' end end def action_path - case action_type - when :playable - play_namespace_project_build_path(subject.project.namespace, subject.project, subject) - when :cancel + case + when subject.cancelable? cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject) - when :retry + when subject.retryable? retry_namespace_project_build_path(subject.project.namespace, subject.project, subject) end end diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb index dd38e1418b6..d8a9f53f236 100644 --- a/lib/gitlab/ci/status/build/factory.rb +++ b/lib/gitlab/ci/status/build/factory.rb @@ -5,6 +5,10 @@ module Gitlab class Factory < Status::Factory private + def extended_statuses + [Stop, Play] + end + def core_status super.extend(Status::Build::Common) end diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb new file mode 100644 index 00000000000..581c81d0175 --- /dev/null +++ b/lib/gitlab/ci/status/build/play.rb @@ -0,0 +1,47 @@ +module Gitlab + module Ci + module Status + module Status + class Play < SimpleDelegator + extend Status::Extended + + def text + 'play' + end + + def label + 'play' + end + + def icon + 'icon_status_skipped' + end + + def to_s + 'play' + end + + def has_action?(current_user) + can?(current_user, :update_build, subject) + end + + def action_icon + :play + end + + def action_path + play_namespace_project_build_path(subject.project.namespace, subject.project, subject) + end + + def action_method + :post + end + + def self.matches?(build) + build.playable? && !build.stops_environment? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb new file mode 100644 index 00000000000..261de9695c5 --- /dev/null +++ b/lib/gitlab/ci/status/build/stop.rb @@ -0,0 +1,47 @@ +module Gitlab + module Ci + module Status + module Status + class Play < SimpleDelegator + extend Status::Extended + + def text + 'stop' + end + + def label + 'stop' + end + + def icon + 'icon_status_skipped' + end + + def to_s + 'stop' + end + + def has_action?(current_user) + can?(current_user, :update_build, subject) + end + + def action_icon + :play + end + + def action_path + play_namespace_project_build_path(subject.project.namespace, subject.project, subject) + end + + def action_method + :post + end + + def self.matches?(build) + build.playable? && build.stops_environment? + end + end + end + end + end +end From 82ee1d29fd3806982afe98678e056194059c64ce Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 10:48:08 +0100 Subject: [PATCH 060/386] Introduce `cancelable` and `returnable` [ci skip] --- lib/gitlab/ci/status/build/cancelable.rb | 31 ++++++++++++++++++++++++ lib/gitlab/ci/status/build/common.rb | 25 ------------------- lib/gitlab/ci/status/build/factory.rb | 2 +- lib/gitlab/ci/status/build/play.rb | 10 +------- lib/gitlab/ci/status/build/retryable.rb | 31 ++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 35 deletions(-) create mode 100644 lib/gitlab/ci/status/build/cancelable.rb create mode 100644 lib/gitlab/ci/status/build/retryable.rb diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb new file mode 100644 index 00000000000..bff0464ef0c --- /dev/null +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -0,0 +1,31 @@ +module Gitlab + module Ci + module Status + module Status + class Cancelable < SimpleDelegator + extend Status::Extended + + def has_action?(current_user) + can?(current_user, :update_build, subject) + end + + def action_icon + 'remove' + end + + def action_path + cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject) + end + + def action_method + :post + end + + def self.matches?(build) + build.cancelable? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb index 2bed68d1a11..3e47d7dfd20 100644 --- a/lib/gitlab/ci/status/build/common.rb +++ b/lib/gitlab/ci/status/build/common.rb @@ -12,31 +12,6 @@ module Gitlab @subject.project, @subject.pipeline) end - - def has_action?(current_user) - (subject.cancelable? || subject.retryable?) && - can?(current_user, :update_build, @subject) - end - - def action_icon - case - when subject.cancelable? then 'icon_play' - when subject.retryable? then 'repeat' - end - end - - def action_path - case - when subject.cancelable? - cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject) - when subject.retryable? - retry_namespace_project_build_path(subject.project.namespace, subject.project, subject) - end - end - - def action_method - :post - end end end end diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb index d8a9f53f236..8f420a93954 100644 --- a/lib/gitlab/ci/status/build/factory.rb +++ b/lib/gitlab/ci/status/build/factory.rb @@ -6,7 +6,7 @@ module Gitlab private def extended_statuses - [Stop, Play] + [Stop, Play, Cancelable, Retryable] end def core_status diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 581c81d0175..d295850137b 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -13,20 +13,12 @@ module Gitlab 'play' end - def icon - 'icon_status_skipped' - end - - def to_s - 'play' - end - def has_action?(current_user) can?(current_user, :update_build, subject) end def action_icon - :play + 'play' end def action_path diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb new file mode 100644 index 00000000000..b3c0eedadf8 --- /dev/null +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -0,0 +1,31 @@ +module Gitlab + module Ci + module Status + module Status + class Retryable < SimpleDelegator + extend Status::Extended + + def has_action?(current_user) + can?(current_user, :update_build, subject) + end + + def action_icon + 'repeat' + end + + def action_path + retry_namespace_project_build_path(subject.project.namespace, subject.project, subject) + end + + def action_method + :post + end + + def self.matches?(build) + build.retryable? + end + end + end + end + end +end From 609bc0a0b67354d6e3df0eba11da1acde6a9d033 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 10:52:44 +0100 Subject: [PATCH 061/386] Check permission of details --- lib/gitlab/ci/status/build/common.rb | 4 ++-- lib/gitlab/ci/status/core.rb | 2 +- lib/gitlab/ci/status/pipeline/common.rb | 4 ++-- lib/gitlab/ci/status/stage/common.rb | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb index 3e47d7dfd20..2fb79afa3d3 100644 --- a/lib/gitlab/ci/status/build/common.rb +++ b/lib/gitlab/ci/status/build/common.rb @@ -3,8 +3,8 @@ module Gitlab module Status module Build module Common - def has_details? - true + def has_details?(current_user) + can?(current_user, :read_build, subject) end def details_path diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 60c559248aa..6b47096f811 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -33,7 +33,7 @@ module Gitlab self.class.name.demodulize.downcase.underscore end - def has_details? + def has_details?(_user = nil) false end diff --git a/lib/gitlab/ci/status/pipeline/common.rb b/lib/gitlab/ci/status/pipeline/common.rb index 25e52bec3da..5f79044a496 100644 --- a/lib/gitlab/ci/status/pipeline/common.rb +++ b/lib/gitlab/ci/status/pipeline/common.rb @@ -3,8 +3,8 @@ module Gitlab module Status module Pipeline module Common - def has_details? - true + def has_details?(current_user) + can?(current_user, :read_pipeline, subject) end def details_path diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb index 14c437d2b98..e6ee2f92341 100644 --- a/lib/gitlab/ci/status/stage/common.rb +++ b/lib/gitlab/ci/status/stage/common.rb @@ -3,8 +3,8 @@ module Gitlab module Status module Stage module Common - def has_details? - true + def has_details?(current_user) + can?(current_user, :read_pipeline, subject) end def details_path From 65f3206024778b934171c9d9ece8ab627ba6c4c5 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 11:03:01 +0100 Subject: [PATCH 062/386] Remove ci_status_with_icon helper and replace it with partial [ci skip] --- app/helpers/ci_status_helper.rb | 20 +------------------ app/views/admin/runners/show.html.haml | 2 +- .../ci/status/_icon_with_label.html.haml | 10 ++++++++++ app/views/projects/builds/_header.html.haml | 2 +- app/views/projects/ci/builds/_build.html.haml | 5 +---- .../projects/ci/pipelines/_pipeline.html.haml | 5 +---- .../_generic_commit_status.html.haml | 5 +---- app/views/projects/pipelines/_info.html.haml | 2 +- 8 files changed, 17 insertions(+), 34 deletions(-) create mode 100644 app/views/ci/status/_icon_with_label.html.haml diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 8e19752a8a1..d9f5e01f0dc 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -4,25 +4,7 @@ module CiStatusHelper builds_namespace_project_commit_path(project.namespace, project, pipeline.sha) end - def ci_status_with_icon(status, target = nil) - content = ci_icon_for_status(status) + ci_text_for_status(status) - klass = "ci-status ci-#{status}" - - if target - link_to content, target, class: klass - else - content_tag :span, content, class: klass - end - end - - def ci_text_for_status(status) - if detailed_status?(status) - status.text - else - status - end - end - + # Is used by Commit and Merge Request Widget def ci_label_for_status(status) if detailed_status?(status) return status.label diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 73038164056..fa8be25ffa8 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -91,7 +91,7 @@ %strong ##{build.id} %td.status - = ci_status_with_icon(build.status) + = render "ci/status/icon_with_label", subject: build %td.status - if project diff --git a/app/views/ci/status/_icon_with_label.html.haml b/app/views/ci/status/_icon_with_label.html.haml new file mode 100644 index 00000000000..65a74e88444 --- /dev/null +++ b/app/views/ci/status/_icon_with_label.html.haml @@ -0,0 +1,10 @@ +- details_path = subject.details_path if subject.has_details?(current_user) +- klass = "ci-status ci-#{subject.status}" +- if details_path + = link_to details_path, class: klass do + = custom_icon(status.icon) + = status.text +- else + %span{ class: klass } + = custom_icon(status.icon) + = status.text diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml index f6aa20c4579..5e4e30f08d5 100644 --- a/app/views/projects/builds/_header.html.haml +++ b/app/views/projects/builds/_header.html.haml @@ -1,6 +1,6 @@ .content-block.build-header .header-content - = ci_status_with_icon(@build.status) + = render "ci/status/icon_with_label", subject: build Build %strong ##{@build.id} in pipeline diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 18b3b04154f..6b0cd3e49a0 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -9,10 +9,7 @@ %tr.build.commit{class: ('retried' if retried)} %td.status - - if can?(current_user, :read_build, build) - = ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build)) - - else - = ci_status_with_icon(build.status) + = render "ci/status/icon_with_label", subject: build %td.branch-commit - if can?(current_user, :read_build, build) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index b58dceb58c9..84243e4306d 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -1,13 +1,10 @@ - status = pipeline.status -- detailed_status = pipeline.detailed_status - show_commit = local_assigns.fetch(:show_commit, true) - show_branch = local_assigns.fetch(:show_branch, true) %tr.commit %td.commit-link - = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{detailed_status}" do - = ci_icon_for_status(detailed_status) - = ci_text_for_status(detailed_status) + = render "ci/status/icon_with_label", subject: pipeline %td = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 7f751d9ae2e..69cb1631ee8 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -8,10 +8,7 @@ %tr.generic_commit_status{class: ('retried' if retried)} %td.status - - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url - = ci_status_with_icon(generic_commit_status.status, generic_commit_status.target_url) - - else - = ci_status_with_icon(generic_commit_status.status) + = render "ci/status/icon_with_label", subject: generic_commit_status %td.generic_commit_status-link - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 229bdfb0e8d..f7385184a2b 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,6 +1,6 @@ .page-content-header .header-main-content - = ci_status_with_icon(@pipeline.detailed_status) + = render "ci/status/icon_with_label", subject: @pipeline %strong Pipeline ##{@commit.pipelines.last.id} triggered #{time_ago_with_tooltip(@commit.authored_date)} by = author_avatar(@commit, size: 24) From 89cc2064a2ebfe947d257c9c15c63825edc73bee Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 8 Dec 2016 14:41:10 +0200 Subject: [PATCH 063/386] Update documentation for BitBucket --- doc/integration/bitbucket.md | 99 ++---------------- .../img/bitbucket_oauth_settings_page.png | Bin 30081 -> 30275 bytes 2 files changed, 7 insertions(+), 92 deletions(-) diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 9122dc62e39..9cdb101f457 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -44,14 +44,12 @@ you to use. And grant at least the following permissions: ``` - Account: Email - Repositories: Read, Admin + Account: Email, Read + Repositories: Read + Pull Requests: Read + Issues: Read ``` - >**Note:** - It may seem a little odd to giving GitLab admin permissions to repositories, - but this is needed in order for GitLab to be able to clone the repositories. - ![Bitbucket OAuth settings page](img/bitbucket_oauth_settings_page.png) 1. Select **Save**. @@ -93,7 +91,8 @@ you to use. ```yaml - { name: 'bitbucket', app_id: 'BITBUCKET_APP_KEY', - app_secret: 'BITBUCKET_APP_SECRET' } + app_secret: 'BITBUCKET_APP_SECRET', + url: 'https://bitbucket.org/' } ``` --- @@ -112,91 +111,7 @@ well, the user will be returned to GitLab and will be signed in. ## Bitbucket project import -To allow projects to be imported directly into GitLab, Bitbucket requires two -extra setup steps compared to [GitHub](github.md) and [GitLab.com](gitlab.md). - -Bitbucket doesn't allow OAuth applications to clone repositories over HTTPS, and -instead requires GitLab to use SSH and identify itself using your GitLab -server's SSH key. - -To be able to access repositories on Bitbucket, GitLab will automatically -register your public key with Bitbucket as a deploy key for the repositories to -be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa` which -translates to `/var/opt/gitlab/.ssh/bitbucket_rsa` for Omnibus packages and to -`/home/git/.ssh/bitbucket_rsa` for installations from source. - ---- - -Below are the steps that will allow GitLab to be able to import your projects -from Bitbucket. - -1. Make sure you [have enabled the Bitbucket OAuth support](#bitbucket-omniauth-provider). -1. Create a new SSH key with an **empty passphrase**: - - ```sh - sudo -u git -H ssh-keygen - ``` - - When asked to 'Enter file in which to save the key' enter: - `/var/opt/gitlab/.ssh/bitbucket_rsa` for Omnibus packages or - `/home/git/.ssh/bitbucket_rsa` for installations from source. The name is - important so make sure to get it right. - - > **Warning:** - This key must NOT be associated with ANY existing Bitbucket accounts. If it - is, the import will fail with an `Access denied! Please verify you can add - deploy keys to this repository.` error. - -1. Next, you need to to configure the SSH client to use your new key. Open the - SSH configuration file of the `git` user: - - ``` - # For Omnibus packages - sudo editor /var/opt/gitlab/.ssh/config - - # For installations from source - sudo editor /home/git/.ssh/config - ``` - -1. Add a host configuration for `bitbucket.org`: - - ```sh - Host bitbucket.org - IdentityFile ~/.ssh/bitbucket_rsa - User git - ``` - -1. Save the file and exit. -1. Manually connect to `bitbucket.org` over SSH, while logged in as the `git` - user that GitLab will use: - - ```sh - sudo -u git -H ssh bitbucket.org - ``` - - That step is performed because GitLab needs to connect to Bitbucket over SSH, - in order to add `bitbucket.org` to your GitLab server's known SSH hosts. - -1. Verify the RSA key fingerprint you'll see in the response matches the one - in the [Bitbucket documentation][bitbucket-docs] (the specific IP address - doesn't matter): - - ```sh - The authenticity of host 'bitbucket.org (104.192.143.1)' can't be established. - RSA key fingerprint is SHA256:zzXQOXSRBEiUtuE8AikJYKwbHaxvSc0ojez9YXaGp1A. - Are you sure you want to continue connecting (yes/no)? - ``` - -1. If the fingerprint matches, type `yes` to continue connecting and have - `bitbucket.org` be added to your known SSH hosts. After confirming you should - see a permission denied message. If you see an authentication successful - message you have done something wrong. The key you are using has already been - added to a Bitbucket account and will cause the import script to fail. Ensure - the key you are using CANNOT authenticate with Bitbucket. -1. Restart GitLab to allow it to find the new public key. - -Your GitLab server is now able to connect to Bitbucket over SSH. You should be -able to see the "Import projects from Bitbucket" option on the New Project page +You should be able to see the "Import projects from Bitbucket" option on the New Project page enabled. ## Acknowledgements diff --git a/doc/integration/img/bitbucket_oauth_settings_page.png b/doc/integration/img/bitbucket_oauth_settings_page.png index 8dbee9762d7510f9e553643c61dd52a353ebbc39..24acc6e1f5acee975aba3fcff8df9a4bbfb8f5dd 100644 GIT binary patch literal 30275 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 From 3acf4323797346437f74e6bf5950fcac917ae56c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 8 Dec 2016 13:51:06 +0100 Subject: [PATCH 064/386] Refactor ci status factories to DRY code a little --- lib/gitlab/ci/status/build/factory.rb | 11 ++++----- lib/gitlab/ci/status/extended.rb | 2 +- lib/gitlab/ci/status/factory.rb | 30 ++++++++++++++---------- lib/gitlab/ci/status/pipeline/factory.rb | 8 +++---- lib/gitlab/ci/status/stage/factory.rb | 6 ++--- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb index 8f420a93954..eee9a64120b 100644 --- a/lib/gitlab/ci/status/build/factory.rb +++ b/lib/gitlab/ci/status/build/factory.rb @@ -3,14 +3,13 @@ module Gitlab module Status module Build class Factory < Status::Factory - private - - def extended_statuses - [Stop, Play, Cancelable, Retryable] + def self.extended_statuses + [Status::Build::Stop, Status::Build::Play, + Status::Build::Cancelable, Status::Build::Retryable] end - def core_status - super.extend(Status::Build::Common) + def self.common_helpers + Status::Build::Common end end end diff --git a/lib/gitlab/ci/status/extended.rb b/lib/gitlab/ci/status/extended.rb index 6bfb5d38c1f..93e6eff1c94 100644 --- a/lib/gitlab/ci/status/extended.rb +++ b/lib/gitlab/ci/status/extended.rb @@ -2,7 +2,7 @@ module Gitlab module Ci module Status module Extended - def matches?(_subject) + def matches?(_subject, _user) raise NotImplementedError end end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb index b2f896f2211..944e0fdde2d 100644 --- a/lib/gitlab/ci/status/factory.rb +++ b/lib/gitlab/ci/status/factory.rb @@ -2,10 +2,9 @@ module Gitlab module Ci module Status class Factory - attr_reader :subject - - def initialize(subject) + def initialize(subject, user = nil) @subject = subject + @user = user end def fabricate! @@ -16,27 +15,32 @@ module Gitlab end end + def self.extended_statuses + [] + end + + def self.common_helpers + Module.new + end + private - def subject_status - @subject_status ||= subject.status + def simple_status + @simple_status ||= @subject.status || :created end def core_status Gitlab::Ci::Status - .const_get(subject_status.capitalize) - .new(subject) + .const_get(simple_status.capitalize) + .new(@subject) + .extend(self.class.common_helpers) end def extended_status - @extended ||= extended_statuses.find do |status| - status.matches?(subject) + @extended ||= self.class.extended_statuses.find do |status| + status.matches?(@subject, @user) end end - - def extended_statuses - [] - end end end end diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb index 4ac4ec671d0..16dcb326be9 100644 --- a/lib/gitlab/ci/status/pipeline/factory.rb +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -3,14 +3,12 @@ module Gitlab module Status module Pipeline class Factory < Status::Factory - private - - def extended_statuses + def self.extended_statuses [Pipeline::SuccessWithWarnings] end - def core_status - super.extend(Status::Pipeline::Common) + def self.common_helpers + Status::Pipeline::Common end end end diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb index c6522d5ada1..689a5dd45bc 100644 --- a/lib/gitlab/ci/status/stage/factory.rb +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -3,10 +3,8 @@ module Gitlab module Status module Stage class Factory < Status::Factory - private - - def core_status - super.extend(Status::Stage::Common) + def self.common_helpers + Status::Stage::Common end end end From 9c0a8cb5b6311c217f7c5c98721da78eae09391f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 8 Dec 2016 14:28:49 +0100 Subject: [PATCH 065/386] Incorporate permission checks into new CI statuses [ci skip] --- lib/gitlab/ci/status/build/cancelable.rb | 12 +++++++----- lib/gitlab/ci/status/build/common.rb | 10 +++++----- lib/gitlab/ci/status/build/play.rb | 12 +++++++----- lib/gitlab/ci/status/build/retryable.rb | 12 +++++++----- lib/gitlab/ci/status/build/stop.rb | 12 +++++++----- lib/gitlab/ci/status/core.rb | 13 ++++++------- lib/gitlab/ci/status/extended.rb | 8 ++++++-- lib/gitlab/ci/status/factory.rb | 4 ++-- lib/gitlab/ci/status/pipeline/common.rb | 10 +++++----- .../ci/status/pipeline/success_with_warnings.rb | 4 ++-- lib/gitlab/ci/status/stage/common.rb | 12 ++++++------ 11 files changed, 60 insertions(+), 49 deletions(-) diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb index bff0464ef0c..a8830b04715 100644 --- a/lib/gitlab/ci/status/build/cancelable.rb +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -3,10 +3,10 @@ module Gitlab module Status module Status class Cancelable < SimpleDelegator - extend Status::Extended + include Status::Extended - def has_action?(current_user) - can?(current_user, :update_build, subject) + def has_action? + can?(user, :update_build, subject) end def action_icon @@ -14,14 +14,16 @@ module Gitlab end def action_path - cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject) + cancel_namespace_project_build_path(subject.project.namespace, + subject.project, + subject) end def action_method :post end - def self.matches?(build) + def self.matches?(build, user) build.cancelable? end end diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb index 2fb79afa3d3..2b602f1e247 100644 --- a/lib/gitlab/ci/status/build/common.rb +++ b/lib/gitlab/ci/status/build/common.rb @@ -3,14 +3,14 @@ module Gitlab module Status module Build module Common - def has_details?(current_user) - can?(current_user, :read_build, subject) + def has_details? + can?(user, :read_build, subject) end def details_path - namespace_project_build_path(@subject.project.namespace, - @subject.project, - @subject.pipeline) + namespace_project_build_path(subject.project.namespace, + subject.project, + subject.pipeline) end end end diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index d295850137b..70c08197ea1 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -3,7 +3,7 @@ module Gitlab module Status module Status class Play < SimpleDelegator - extend Status::Extended + include Status::Extended def text 'play' @@ -13,8 +13,8 @@ module Gitlab 'play' end - def has_action?(current_user) - can?(current_user, :update_build, subject) + def has_action? + can?(user, :update_build, subject) end def action_icon @@ -22,14 +22,16 @@ module Gitlab end def action_path - play_namespace_project_build_path(subject.project.namespace, subject.project, subject) + play_namespace_project_build_path(subject.project.namespace, + subject.project, + subject) end def action_method :post end - def self.matches?(build) + def self.matches?(build, user) build.playable? && !build.stops_environment? end end diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb index b3c0eedadf8..3309e8808e1 100644 --- a/lib/gitlab/ci/status/build/retryable.rb +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -3,10 +3,10 @@ module Gitlab module Status module Status class Retryable < SimpleDelegator - extend Status::Extended + include Status::Extended - def has_action?(current_user) - can?(current_user, :update_build, subject) + def has_action? + can?(user, :update_build, subject) end def action_icon @@ -14,14 +14,16 @@ module Gitlab end def action_path - retry_namespace_project_build_path(subject.project.namespace, subject.project, subject) + retry_namespace_project_build_path(subject.project.namespace, + subject.project, + subject) end def action_method :post end - def self.matches?(build) + def self.matches?(build, user) build.retryable? end end diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index 261de9695c5..6fb51890bec 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -3,7 +3,7 @@ module Gitlab module Status module Status class Play < SimpleDelegator - extend Status::Extended + include Status::Extended def text 'stop' @@ -21,8 +21,8 @@ module Gitlab 'stop' end - def has_action?(current_user) - can?(current_user, :update_build, subject) + def has_action? + can?(user, :update_build, subject) end def action_icon @@ -30,14 +30,16 @@ module Gitlab end def action_path - play_namespace_project_build_path(subject.project.namespace, subject.project, subject) + play_namespace_project_build_path(subject.project.namespace, + subject.project, + subject) end def action_method :post end - def self.matches?(build) + def self.matches?(build, user) build.playable? && build.stops_environment? end end diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 6b47096f811..df371363736 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -6,8 +6,11 @@ module Gitlab class Core include Gitlab::Routing.url_helpers - def initialize(subject) + attr_reader :subject, :user + + def initialize(subject, user) @subject = subject + @user = user end def icon @@ -18,10 +21,6 @@ module Gitlab raise NotImplementedError end - def title - "#{@subject.class.name.demodulize}: #{label}" - end - # Deprecation warning: this method is here because we need to maintain # backwards compatibility with legacy statuses. We often do something # like "ci-status ci-status-#{status}" to set CSS class. @@ -33,7 +32,7 @@ module Gitlab self.class.name.demodulize.downcase.underscore end - def has_details?(_user = nil) + def has_details? false end @@ -41,7 +40,7 @@ module Gitlab raise NotImplementedError end - def has_action?(_user = nil) + def has_action? false end diff --git a/lib/gitlab/ci/status/extended.rb b/lib/gitlab/ci/status/extended.rb index 93e6eff1c94..d367c9bda69 100644 --- a/lib/gitlab/ci/status/extended.rb +++ b/lib/gitlab/ci/status/extended.rb @@ -2,8 +2,12 @@ module Gitlab module Ci module Status module Extended - def matches?(_subject, _user) - raise NotImplementedError + extend ActiveSupport::Concern + + class_methods do + def matches?(_subject, _user) + raise NotImplementedError + end end end end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb index 944e0fdde2d..ae9ef895df4 100644 --- a/lib/gitlab/ci/status/factory.rb +++ b/lib/gitlab/ci/status/factory.rb @@ -2,7 +2,7 @@ module Gitlab module Ci module Status class Factory - def initialize(subject, user = nil) + def initialize(subject, user) @subject = subject @user = user end @@ -32,7 +32,7 @@ module Gitlab def core_status Gitlab::Ci::Status .const_get(simple_status.capitalize) - .new(@subject) + .new(@subject, @user) .extend(self.class.common_helpers) end diff --git a/lib/gitlab/ci/status/pipeline/common.rb b/lib/gitlab/ci/status/pipeline/common.rb index 5f79044a496..76bfd18bf40 100644 --- a/lib/gitlab/ci/status/pipeline/common.rb +++ b/lib/gitlab/ci/status/pipeline/common.rb @@ -3,14 +3,14 @@ module Gitlab module Status module Pipeline module Common - def has_details?(current_user) - can?(current_user, :read_pipeline, subject) + def has_details? + can?(user, :read_pipeline, subject) end def details_path - namespace_project_pipeline_path(@subject.project.namespace, - @subject.project, - @subject) + namespace_project_pipeline_path(subject.project.namespace, + subject.project, + subject) end def has_action? diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb index 4b040d60df8..a7c98f9e909 100644 --- a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -3,7 +3,7 @@ module Gitlab module Status module Pipeline class SuccessWithWarnings < SimpleDelegator - extend Status::Extended + include Status::Extended def text 'passed' @@ -21,7 +21,7 @@ module Gitlab 'success_with_warnings' end - def self.matches?(pipeline) + def self.matches?(pipeline, user) pipeline.success? && pipeline.has_warnings? end end diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb index e6ee2f92341..6851ceda317 100644 --- a/lib/gitlab/ci/status/stage/common.rb +++ b/lib/gitlab/ci/status/stage/common.rb @@ -3,15 +3,15 @@ module Gitlab module Status module Stage module Common - def has_details?(current_user) - can?(current_user, :read_pipeline, subject) + def has_details? + can?(user, :read_pipeline, subject) end def details_path - namespace_project_pipeline_path(@subject.project.namespace, - @subject.project, - @subject.pipeline, - anchor: @subject.name) + namespace_project_pipeline_path(subject.project.namespace, + subject.project, + subject.pipeline, + anchor: subject.name) end def has_action? From 7d88399454b5c71c9af84561c858001d1a733c41 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 8 Dec 2016 14:40:21 +0100 Subject: [PATCH 066/386] Fix tests related to detailed statuses and permissions [ci skip] --- spec/lib/gitlab/ci/status/canceled_spec.rb | 4 +++- spec/lib/gitlab/ci/status/created_spec.rb | 8 +++----- spec/lib/gitlab/ci/status/extended_spec.rb | 2 +- spec/lib/gitlab/ci/status/factory_spec.rb | 6 ++++-- spec/lib/gitlab/ci/status/failed_spec.rb | 8 +++----- spec/lib/gitlab/ci/status/pending_spec.rb | 8 +++----- spec/lib/gitlab/ci/status/pipeline/common_spec.rb | 4 +++- spec/lib/gitlab/ci/status/pipeline/factory_spec.rb | 4 +++- .../ci/status/pipeline/success_with_warnings_spec.rb | 2 +- spec/lib/gitlab/ci/status/running_spec.rb | 8 +++----- spec/lib/gitlab/ci/status/skipped_spec.rb | 8 +++----- spec/lib/gitlab/ci/status/stage/common_spec.rb | 8 ++++++-- spec/lib/gitlab/ci/status/stage/factory_spec.rb | 8 ++++++-- spec/lib/gitlab/ci/status/success_spec.rb | 8 +++----- 14 files changed, 45 insertions(+), 41 deletions(-) diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb index 619ecbcba67..eaf974bb953 100644 --- a/spec/lib/gitlab/ci/status/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Canceled do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'canceled' } diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb index 157302c65a8..2ce176a29d6 100644 --- a/spec/lib/gitlab/ci/status/created_spec.rb +++ b/spec/lib/gitlab/ci/status/created_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Created do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'created' } @@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Created do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_created' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: created' } - end end diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb index 120e121aae5..864121dec4b 100644 --- a/spec/lib/gitlab/ci/status/extended_spec.rb +++ b/spec/lib/gitlab/ci/status/extended_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::Ci::Status::Extended do end it 'requires subclass to implement matcher' do - expect { subject.matches?(double) } + expect { subject.matches?(double, double) } .to raise_error(NotImplementedError) end end diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb index d5bd7f7102b..f92a1c149bf 100644 --- a/spec/lib/gitlab/ci/status/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/factory_spec.rb @@ -2,15 +2,17 @@ require 'spec_helper' describe Gitlab::Ci::Status::Factory do subject do - described_class.new(object) + described_class.new(resource, user) end + let(:user) { create(:user) } + let(:status) { subject.fabricate! } context 'when object has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| context "when core status is #{core_status}" do - let(:object) { double(status: core_status) } + let(:resource) { double(status: core_status) } it "fabricates a core status #{core_status}" do expect(status).to be_a( diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb index 0b3cb8168e6..9d527e6a7ef 100644 --- a/spec/lib/gitlab/ci/status/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/failed_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Failed do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'failed' } @@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Failed do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_failed' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: failed' } - end end diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb index 57c901c1202..d03f595d3c7 100644 --- a/spec/lib/gitlab/ci/status/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/pending_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pending do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'pending' } @@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Pending do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_pending' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: pending' } - end end diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb index 21adee3f8e7..4f32ae5d809 100644 --- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -1,11 +1,13 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Common do + let(:user) { create(:user) } let(:pipeline) { create(:ci_pipeline) } subject do Class.new(Gitlab::Ci::Status::Core) - .new(pipeline).extend(described_class) + .new(pipeline, user) + .extend(described_class) end it 'does not have action' do diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index d6243940f2e..c6b2582652d 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -1,8 +1,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Factory do + let(:user) { create(:user) } + subject do - described_class.new(pipeline) + described_class.new(pipeline, user) end let(:status) do diff --git a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb index 02e526e3de2..634f80088d5 100644 --- a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do subject do - described_class.new(double('status')) + described_class.new(double('status'), double('user')) end describe '#test' do diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb index c023f1872cc..9f47090d396 100644 --- a/spec/lib/gitlab/ci/status/running_spec.rb +++ b/spec/lib/gitlab/ci/status/running_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Running do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'running' } @@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Running do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_running' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: running' } - end end diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb index d4f7f4b3b70..94601648a8d 100644 --- a/spec/lib/gitlab/ci/status/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/skipped_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Skipped do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'skipped' } @@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Skipped do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_skipped' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: skipped' } - end end diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index f3259c6f23e..9b7e6777dc1 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -1,12 +1,16 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Common do + let(:user) { create(:user) } let(:pipeline) { create(:ci_empty_pipeline) } - let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') } + + let(:stage) do + build(:ci_stage, pipeline: pipeline, name: 'test') + end subject do Class.new(Gitlab::Ci::Status::Core) - .new(stage).extend(described_class) + .new(stage, user).extend(described_class) end it 'does not have action' do diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 17929665c83..5a281564415 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -1,11 +1,15 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Factory do + let(:user) { create(:user) } let(:pipeline) { create(:ci_empty_pipeline) } - let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') } + + let(:stage) do + build(:ci_stage, pipeline: pipeline, name: 'test') + end subject do - described_class.new(stage) + described_class.new(stage, user) end let(:status) do diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb index 9e261a3aa5f..90f9f615e0d 100644 --- a/spec/lib/gitlab/ci/status/success_spec.rb +++ b/spec/lib/gitlab/ci/status/success_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe Gitlab::Ci::Status::Success do - subject { described_class.new(double('subject')) } + subject do + described_class.new(double('subject'), double('user')) + end describe '#text' do it { expect(subject.label).to eq 'passed' } @@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Success do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_success' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: passed' } - end end From c1db5b91207f4e3f7144c5cb62ce9160cf2e32e9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 8 Dec 2016 14:51:38 +0100 Subject: [PATCH 067/386] Fix some detailed statuses specs related to abilities --- app/models/ability.rb | 6 +++++ lib/gitlab/ci/status/core.rb | 1 + lib/gitlab/ci/status/stage/common.rb | 2 +- spec/lib/gitlab/ci/status/canceled_spec.rb | 4 ---- spec/lib/gitlab/ci/status/extended_spec.rb | 2 +- .../gitlab/ci/status/pipeline/common_spec.rb | 7 +++++- .../gitlab/ci/status/pipeline/factory_spec.rb | 5 ++++ .../pipeline/success_with_warnings_spec.rb | 10 ++++---- .../lib/gitlab/ci/status/stage/common_spec.rb | 23 +++++++++++++++---- .../gitlab/ci/status/stage/factory_spec.rb | 7 +++++- 10 files changed, 49 insertions(+), 18 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index fa8f8bc3a5f..ce461caf686 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,4 +1,10 @@ class Ability + module Allowable + def can?(user, action, subject) + Ability.allowed?(user, action, subject) + end + end + class << self # Given a list of users and a project this method returns the users that can # read the given project. diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index df371363736..7e9f6e35012 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -5,6 +5,7 @@ module Gitlab # class Core include Gitlab::Routing.url_helpers + include Ability::Allowable attr_reader :subject, :user diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb index 6851ceda317..7852f492e1d 100644 --- a/lib/gitlab/ci/status/stage/common.rb +++ b/lib/gitlab/ci/status/stage/common.rb @@ -4,7 +4,7 @@ module Gitlab module Stage module Common def has_details? - can?(user, :read_pipeline, subject) + can?(user, :read_pipeline, subject.pipeline) end def details_path diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb index eaf974bb953..4639278ad45 100644 --- a/spec/lib/gitlab/ci/status/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -16,8 +16,4 @@ describe Gitlab::Ci::Status::Canceled do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_canceled' } end - - describe '#title' do - it { expect(subject.title).to eq 'Double: canceled' } - end end diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb index 864121dec4b..c2d74ca5cde 100644 --- a/spec/lib/gitlab/ci/status/extended_spec.rb +++ b/spec/lib/gitlab/ci/status/extended_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::Ci::Status::Extended do subject do - Class.new.extend(described_class) + Class.new.include(described_class) end it 'requires subclass to implement matcher' do diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb index 4f32ae5d809..2df9d574677 100644 --- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Common do let(:user) { create(:user) } - let(:pipeline) { create(:ci_pipeline) } + let(:project) { create(:empty_project) } + let(:pipeline) { create(:ci_pipeline, project: project) } subject do Class.new(Gitlab::Ci::Status::Core) @@ -10,6 +11,10 @@ describe Gitlab::Ci::Status::Pipeline::Common do .extend(described_class) end + before do + project.team << [user, :developer] + end + it 'does not have action' do expect(subject).not_to have_action end diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index c6b2582652d..d4a2dc7fcc1 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::Factory do let(:user) { create(:user) } + let(:project) { pipeline.project } subject do described_class.new(pipeline, user) @@ -11,6 +12,10 @@ describe Gitlab::Ci::Status::Pipeline::Factory do subject.fabricate! end + before do + project.team << [user, :developer] + end + context 'when pipeline has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| context "when core status is #{core_status}" do diff --git a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb index 634f80088d5..7e3383c307f 100644 --- a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do subject do - described_class.new(double('status'), double('user')) + described_class.new(double('status')) end describe '#test' do @@ -29,13 +29,13 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do end it 'is a correct match' do - expect(described_class.matches?(pipeline)).to eq true + expect(described_class.matches?(pipeline, double)).to eq true end end context 'when pipeline does not have warnings' do it 'does not match' do - expect(described_class.matches?(pipeline)).to eq false + expect(described_class.matches?(pipeline, double)).to eq false end end end @@ -51,13 +51,13 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do end it 'does not match' do - expect(described_class.matches?(pipeline)).to eq false + expect(described_class.matches?(pipeline, double)).to eq false end end context 'when pipeline does not have warnings' do it 'does not match' do - expect(described_class.matches?(pipeline)).to eq false + expect(described_class.matches?(pipeline, double)).to eq false end end end diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index 9b7e6777dc1..8814a7614a0 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Common do let(:user) { create(:user) } - let(:pipeline) { create(:ci_empty_pipeline) } + let(:project) { create(:empty_project) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:stage) do build(:ci_stage, pipeline: pipeline, name: 'test') @@ -17,14 +18,26 @@ describe Gitlab::Ci::Status::Stage::Common do expect(subject).not_to have_action end - it 'has details' do - expect(subject).to have_details - end - it 'links to the pipeline details page' do expect(subject.details_path) .to include "pipelines/#{pipeline.id}" expect(subject.details_path) .to include "##{stage.name}" end + + context 'when user has permission to read pipeline' do + before do + project.team << [user, :master] + end + + it 'has details' do + expect(subject).to have_details + end + end + + context 'when user does not have permission to read pipeline' do + it 'does not have details' do + expect(subject).not_to have_details + end + end end diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 5a281564415..6f8721d30c2 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Factory do let(:user) { create(:user) } - let(:pipeline) { create(:ci_empty_pipeline) } + let(:project) { create(:empty_project) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:stage) do build(:ci_stage, pipeline: pipeline, name: 'test') @@ -16,6 +17,10 @@ describe Gitlab::Ci::Status::Stage::Factory do subject.fabricate! end + before do + project.team << [user, :developer] + end + context 'when stage has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| context "when core status is #{core_status}" do From 5b89ec0dbf76784ccc1e41b66498dd0dc4f05042 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 17:52:24 +0100 Subject: [PATCH 068/386] Fix auto loading of constants for Ci Statuses --- app/models/ci/build.rb | 6 +++--- app/models/ci/pipeline.rb | 4 ++-- app/models/ci/stage.rb | 4 ++-- app/models/commit_status.rb | 4 ++-- app/views/ci/status/_icon_with_label.html.haml | 13 +++++++------ lib/gitlab/ci/status/build/cancelable.rb | 2 +- lib/gitlab/ci/status/build/play.rb | 2 +- lib/gitlab/ci/status/build/retryable.rb | 2 +- lib/gitlab/ci/status/build/stop.rb | 4 ++-- 9 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 73564dd2aa0..65ee327a8e5 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -100,8 +100,8 @@ module Ci end end - def detailed_status - Gitlab::Ci::Status::Build::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Build::Factory.new(self, current_user).fabricate! end def manual? @@ -156,7 +156,7 @@ module Ci end def environment_action - self.options.fetch(:environment, {}).fetch(:action, 'start') + self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options end def outdated_deployment? diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fda8228a1e9..1f33106d358 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -336,8 +336,8 @@ module Ci .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id } end - def detailed_status - Gitlab::Ci::Status::Pipeline::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Pipeline::Factory.new(self, current_user).fabricate! end private diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index d2a37c0a827..be52cce20f1 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -22,8 +22,8 @@ module Ci @status ||= statuses.latest.status end - def detailed_status - Gitlab::Ci::Status::Stage::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Stage::Factory.new(self, current_user).fabricate! end def statuses diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index fce16174e22..6548a7dda2c 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -132,7 +132,7 @@ class CommitStatus < ActiveRecord::Base false end - def detailed_status - Gitlab::Ci::Status::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Factory.new(self, current_user).fabricate! end end diff --git a/app/views/ci/status/_icon_with_label.html.haml b/app/views/ci/status/_icon_with_label.html.haml index 65a74e88444..d3fe332cc78 100644 --- a/app/views/ci/status/_icon_with_label.html.haml +++ b/app/views/ci/status/_icon_with_label.html.haml @@ -1,10 +1,11 @@ -- details_path = subject.details_path if subject.has_details?(current_user) -- klass = "ci-status ci-#{subject.status}" +- detailed_status = subject.detailed_status(current_user) +- details_path = detailed_status.details_path if detailed_status.has_details? +- klass = "ci-status ci-#{detailed_status}" - if details_path = link_to details_path, class: klass do - = custom_icon(status.icon) - = status.text + = custom_icon(detailed_status.icon) + = detailed_status.text - else %span{ class: klass } - = custom_icon(status.icon) - = status.text + = custom_icon(detailed_status.icon) + = detailed_status.text diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb index a8830b04715..88be0cd924b 100644 --- a/lib/gitlab/ci/status/build/cancelable.rb +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -1,7 +1,7 @@ module Gitlab module Ci module Status - module Status + module Build class Cancelable < SimpleDelegator include Status::Extended diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 70c08197ea1..57c7058fe84 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -1,7 +1,7 @@ module Gitlab module Ci module Status - module Status + module Build class Play < SimpleDelegator include Status::Extended diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb index 3309e8808e1..69f2ad1d277 100644 --- a/lib/gitlab/ci/status/build/retryable.rb +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -1,7 +1,7 @@ module Gitlab module Ci module Status - module Status + module Build class Retryable < SimpleDelegator include Status::Extended diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index 6fb51890bec..cd9bd959a7c 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -1,8 +1,8 @@ module Gitlab module Ci module Status - module Status - class Play < SimpleDelegator + module Build + class Stop < SimpleDelegator include Status::Extended def text From f6240862c884a0290f4ffcce9071946c785b4027 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 18:16:23 +0100 Subject: [PATCH 069/386] Rename icon_with_label to icon_with_description --- app/views/admin/runners/show.html.haml | 2 +- ..._with_label.html.haml => _icon_with_description.html.haml} | 1 + app/views/projects/builds/_header.html.haml | 2 +- app/views/projects/ci/builds/_build.html.haml | 2 +- app/views/projects/ci/pipelines/_pipeline.html.haml | 2 +- .../generic_commit_statuses/_generic_commit_status.html.haml | 2 +- app/views/projects/pipelines/_info.html.haml | 2 +- app/views/projects/stage/_graph.html.haml | 4 ++-- app/views/projects/stage/_in_stage_group.html.haml | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) rename app/views/ci/status/{_icon_with_label.html.haml => _icon_with_description.html.haml} (99%) diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index fa8be25ffa8..badeb11b208 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -91,7 +91,7 @@ %strong ##{build.id} %td.status - = render "ci/status/icon_with_label", subject: build + = render "ci/status/icon_with_description", subject: build %td.status - if project diff --git a/app/views/ci/status/_icon_with_label.html.haml b/app/views/ci/status/_icon_with_description.html.haml similarity index 99% rename from app/views/ci/status/_icon_with_label.html.haml rename to app/views/ci/status/_icon_with_description.html.haml index d3fe332cc78..34c923440d0 100644 --- a/app/views/ci/status/_icon_with_label.html.haml +++ b/app/views/ci/status/_icon_with_description.html.haml @@ -1,6 +1,7 @@ - detailed_status = subject.detailed_status(current_user) - details_path = detailed_status.details_path if detailed_status.has_details? - klass = "ci-status ci-#{detailed_status}" + - if details_path = link_to details_path, class: klass do = custom_icon(detailed_status.icon) diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml index 5e4e30f08d5..85d1793ecb9 100644 --- a/app/views/projects/builds/_header.html.haml +++ b/app/views/projects/builds/_header.html.haml @@ -1,6 +1,6 @@ .content-block.build-header .header-content - = render "ci/status/icon_with_label", subject: build + = render "ci/status/icon_with_description", subject: build Build %strong ##{@build.id} in pipeline diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 6b0cd3e49a0..4257bb86859 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -9,7 +9,7 @@ %tr.build.commit{class: ('retried' if retried)} %td.status - = render "ci/status/icon_with_label", subject: build + = render "ci/status/icon_with_description", subject: build %td.branch-commit - if can?(current_user, :read_build, build) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 84243e4306d..6dff955ea3d 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -4,7 +4,7 @@ %tr.commit %td.commit-link - = render "ci/status/icon_with_label", subject: pipeline + = render "ci/status/icon_with_description", subject: pipeline %td = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 69cb1631ee8..1dd07ae1a2a 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -8,7 +8,7 @@ %tr.generic_commit_status{class: ('retried' if retried)} %td.status - = render "ci/status/icon_with_label", subject: generic_commit_status + = render "ci/status/icon_with_description", subject: generic_commit_status %td.generic_commit_status-link - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index f7385184a2b..d05697b4ee3 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,6 +1,6 @@ .page-content-header .header-main-content - = render "ci/status/icon_with_label", subject: @pipeline + = render "ci/status/icon_with_description", subject: @pipeline %strong Pipeline ##{@commit.pipelines.last.id} triggered #{time_ago_with_tooltip(@commit.authored_date)} by = author_avatar(@commit, size: 24) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index d8c87fae5a1..cb845ae7f92 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -15,9 +15,9 @@ %li.build{ class: ("playable" if is_playable) } .curve .build-content - = render "projects/#{status.to_partial_path}_pipeline", subject: status + = render 'ci/status/icon_with_name_and_action', subject: status - else %li.build .curve .dropdown.inline.build-content - = render "projects/stage/in_stage_group", name: group_name, subject: grouped_statuses + = render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index 2d198d1b389..5c9b6549b37 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -10,4 +10,4 @@ %ul - subject.each do |status| %li.dropdown-build - = render "projects/#{status.to_partial_path}_pipeline", subject: status + = render 'ci/status/icon_with_name_and_action', subject: status From 7c6984752fb6557ac754bb13a554f9b76ee39989 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 18:18:30 +0100 Subject: [PATCH 070/386] Add action_class/action_title --- lib/gitlab/ci/status/build/cancelable.rb | 6 +++++- lib/gitlab/ci/status/build/play.rb | 8 ++++++++ lib/gitlab/ci/status/build/retryable.rb | 6 +++++- lib/gitlab/ci/status/build/stop.rb | 6 +++++- lib/gitlab/ci/status/core.rb | 7 +++++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb index 88be0cd924b..a979fe7d573 100644 --- a/lib/gitlab/ci/status/build/cancelable.rb +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -10,7 +10,7 @@ module Gitlab end def action_icon - 'remove' + 'ban' end def action_path @@ -23,6 +23,10 @@ module Gitlab :post end + def action_title + 'Cancel' + end + def self.matches?(build, user) build.cancelable? end diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 57c7058fe84..e3066d40a37 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -17,10 +17,18 @@ module Gitlab can?(user, :update_build, subject) end + def action_title + 'Play' + end + def action_icon 'play' end + def action_class + 'ci-play-icon' + end + def action_path play_namespace_project_build_path(subject.project.namespace, subject.project, diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb index 69f2ad1d277..8e38d6a8523 100644 --- a/lib/gitlab/ci/status/build/retryable.rb +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -10,7 +10,11 @@ module Gitlab end def action_icon - 'repeat' + 'refresh' + end + + def action_title + 'Retry' end def action_path diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index cd9bd959a7c..487fd033960 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -26,7 +26,11 @@ module Gitlab end def action_icon - :play + 'stop' + end + + def action_title + 'Stop' end def action_path diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 7e9f6e35012..dd3a824e486 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -49,6 +49,9 @@ module Gitlab raise NotImplementedError end + def action_class + end + def action_path raise NotImplementedError end @@ -56,6 +59,10 @@ module Gitlab def action_method raise NotImplementedError end + + def action_title + raise NotImplementedError + end end end end From 203c9040571944f573d18db2bd477521b6c76535 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 18:19:17 +0100 Subject: [PATCH 071/386] Add icon_with_name_and_action and use it everywhere --- app/views/ci/status/_icon_with_name.html.haml | 11 +++++++ .../_icon_with_name_and_action.html.haml | 8 +++++ .../ci/builds/_build_pipeline.html.haml | 31 ------------------- .../_generic_commit_status_pipeline.html.haml | 10 ------ 4 files changed, 19 insertions(+), 41 deletions(-) create mode 100644 app/views/ci/status/_icon_with_name.html.haml create mode 100644 app/views/ci/status/_icon_with_name_and_action.html.haml delete mode 100644 app/views/projects/ci/builds/_build_pipeline.html.haml delete mode 100644 app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml diff --git a/app/views/ci/status/_icon_with_name.html.haml b/app/views/ci/status/_icon_with_name.html.haml new file mode 100644 index 00000000000..028e1fe9402 --- /dev/null +++ b/app/views/ci/status/_icon_with_name.html.haml @@ -0,0 +1,11 @@ +- detailed_status = subject.detailed_status(current_user) +- details_path = detailed_status.details_path if detailed_status.has_details? +- klass = "ci-status-icon ci-status-icon-#{detailed_status}" + +- if details_path + = link_to details_path, class: klass, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do + %span{ class: klass }= custom_icon(detailed_status.icon) + .ci-status-text= subject.name +- else + %span{ class: klass }= custom_icon(detailed_status.icon) + .ci-status-text= subject.name diff --git a/app/views/ci/status/_icon_with_name_and_action.html.haml b/app/views/ci/status/_icon_with_name_and_action.html.haml new file mode 100644 index 00000000000..76db3b7f38a --- /dev/null +++ b/app/views/ci/status/_icon_with_name_and_action.html.haml @@ -0,0 +1,8 @@ += render "ci/status/icon_with_name", subject: subject + +- detailed_status = subject.detailed_status(current_user) +- if detailed_status.has_action? + = link_to detailed_status.action_path, method: detailed_status.action_method, + title: "#{subject.name}: #{detailed_status.action_title}", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon(detailed_status.action_icon, class: detailed_status.action_class) diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml deleted file mode 100644 index 41b9265fe5e..00000000000 --- a/app/views/projects/ci/builds/_build_pipeline.html.haml +++ /dev/null @@ -1,31 +0,0 @@ -- is_playable = subject.playable? && can?(current_user, :update_build, @project) -- can_cancel = subject.active? && can?(current_user, :update_build, @project) -- can_retry = subject.retryable? && can?(current_user, :update_build, @project) -- can_stop = subject.complete? && subject.stops_environment? && can?(current_user, :update_build, @project) - -- if can?(current_user, :read_build, @project) - = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - .ci-status-text= subject.name - - - if is_playable - = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Play", class: 'ci-action-icon-container' do - %i.ci-action-icon-wrapper - = icon('play', class: 'ci-play-icon') - - elsif can_cancel - = link_to cancel_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Cancel", class: 'ci-action-icon-container' do - %i.ci-action-icon-wrapper - = icon('ban') - - elsif can_retry - = link_to retry_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Retry", class: 'ci-action-icon-container' do - %i.ci-action-icon-wrapper - = icon('refresh') - - elsif can_stop - = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: "#{subject.name} - Stop", class: 'ci-action-icon-container' do - %i.ci-action-icon-wrapper - = icon('stop') - -- else - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml deleted file mode 100644 index 7b82d913d29..00000000000 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%a{ data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } } - - if subject.target_url - = link_to subject.target_url do - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - %span.ci-status-text= subject.name - - else - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - %span.ci-status-text= subject.name From 6296a1cc5d70e2e32a0a98e903415832ca29f281 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 17:52:24 +0100 Subject: [PATCH 072/386] Fix auto loading of constants for Ci Statuses --- app/models/ci/build.rb | 6 +++--- app/models/ci/pipeline.rb | 4 ++-- app/models/ci/stage.rb | 4 ++-- app/models/commit_status.rb | 4 ++-- app/views/ci/status/_icon_with_label.html.haml | 13 +++++++------ lib/gitlab/ci/status/build/cancelable.rb | 2 +- lib/gitlab/ci/status/build/play.rb | 2 +- lib/gitlab/ci/status/build/retryable.rb | 2 +- lib/gitlab/ci/status/build/stop.rb | 4 ++-- 9 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 73564dd2aa0..65ee327a8e5 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -100,8 +100,8 @@ module Ci end end - def detailed_status - Gitlab::Ci::Status::Build::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Build::Factory.new(self, current_user).fabricate! end def manual? @@ -156,7 +156,7 @@ module Ci end def environment_action - self.options.fetch(:environment, {}).fetch(:action, 'start') + self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options end def outdated_deployment? diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fda8228a1e9..1f33106d358 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -336,8 +336,8 @@ module Ci .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id } end - def detailed_status - Gitlab::Ci::Status::Pipeline::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Pipeline::Factory.new(self, current_user).fabricate! end private diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index d2a37c0a827..be52cce20f1 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -22,8 +22,8 @@ module Ci @status ||= statuses.latest.status end - def detailed_status - Gitlab::Ci::Status::Stage::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Stage::Factory.new(self, current_user).fabricate! end def statuses diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index fce16174e22..6548a7dda2c 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -132,7 +132,7 @@ class CommitStatus < ActiveRecord::Base false end - def detailed_status - Gitlab::Ci::Status::Factory.new(self).fabricate! + def detailed_status(current_user) + Gitlab::Ci::Status::Factory.new(self, current_user).fabricate! end end diff --git a/app/views/ci/status/_icon_with_label.html.haml b/app/views/ci/status/_icon_with_label.html.haml index 65a74e88444..d3fe332cc78 100644 --- a/app/views/ci/status/_icon_with_label.html.haml +++ b/app/views/ci/status/_icon_with_label.html.haml @@ -1,10 +1,11 @@ -- details_path = subject.details_path if subject.has_details?(current_user) -- klass = "ci-status ci-#{subject.status}" +- detailed_status = subject.detailed_status(current_user) +- details_path = detailed_status.details_path if detailed_status.has_details? +- klass = "ci-status ci-#{detailed_status}" - if details_path = link_to details_path, class: klass do - = custom_icon(status.icon) - = status.text + = custom_icon(detailed_status.icon) + = detailed_status.text - else %span{ class: klass } - = custom_icon(status.icon) - = status.text + = custom_icon(detailed_status.icon) + = detailed_status.text diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb index a8830b04715..88be0cd924b 100644 --- a/lib/gitlab/ci/status/build/cancelable.rb +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -1,7 +1,7 @@ module Gitlab module Ci module Status - module Status + module Build class Cancelable < SimpleDelegator include Status::Extended diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 70c08197ea1..57c7058fe84 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -1,7 +1,7 @@ module Gitlab module Ci module Status - module Status + module Build class Play < SimpleDelegator include Status::Extended diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb index 3309e8808e1..69f2ad1d277 100644 --- a/lib/gitlab/ci/status/build/retryable.rb +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -1,7 +1,7 @@ module Gitlab module Ci module Status - module Status + module Build class Retryable < SimpleDelegator include Status::Extended diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index 6fb51890bec..cd9bd959a7c 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -1,8 +1,8 @@ module Gitlab module Ci module Status - module Status - class Play < SimpleDelegator + module Build + class Stop < SimpleDelegator include Status::Extended def text From a30dcc6771ec9244223ddb1de9293fe3fefeedf0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 18:16:23 +0100 Subject: [PATCH 073/386] Rename icon_with_label to icon_with_description --- app/views/admin/runners/show.html.haml | 2 +- ..._with_label.html.haml => _icon_with_description.html.haml} | 1 + app/views/projects/builds/_header.html.haml | 2 +- app/views/projects/ci/builds/_build.html.haml | 2 +- app/views/projects/ci/pipelines/_pipeline.html.haml | 2 +- .../generic_commit_statuses/_generic_commit_status.html.haml | 2 +- app/views/projects/pipelines/_info.html.haml | 2 +- app/views/projects/stage/_graph.html.haml | 4 ++-- 8 files changed, 9 insertions(+), 8 deletions(-) rename app/views/ci/status/{_icon_with_label.html.haml => _icon_with_description.html.haml} (99%) diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index fa8be25ffa8..badeb11b208 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -91,7 +91,7 @@ %strong ##{build.id} %td.status - = render "ci/status/icon_with_label", subject: build + = render "ci/status/icon_with_description", subject: build %td.status - if project diff --git a/app/views/ci/status/_icon_with_label.html.haml b/app/views/ci/status/_icon_with_description.html.haml similarity index 99% rename from app/views/ci/status/_icon_with_label.html.haml rename to app/views/ci/status/_icon_with_description.html.haml index d3fe332cc78..34c923440d0 100644 --- a/app/views/ci/status/_icon_with_label.html.haml +++ b/app/views/ci/status/_icon_with_description.html.haml @@ -1,6 +1,7 @@ - detailed_status = subject.detailed_status(current_user) - details_path = detailed_status.details_path if detailed_status.has_details? - klass = "ci-status ci-#{detailed_status}" + - if details_path = link_to details_path, class: klass do = custom_icon(detailed_status.icon) diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml index 5e4e30f08d5..85d1793ecb9 100644 --- a/app/views/projects/builds/_header.html.haml +++ b/app/views/projects/builds/_header.html.haml @@ -1,6 +1,6 @@ .content-block.build-header .header-content - = render "ci/status/icon_with_label", subject: build + = render "ci/status/icon_with_description", subject: build Build %strong ##{@build.id} in pipeline diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 6b0cd3e49a0..4257bb86859 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -9,7 +9,7 @@ %tr.build.commit{class: ('retried' if retried)} %td.status - = render "ci/status/icon_with_label", subject: build + = render "ci/status/icon_with_description", subject: build %td.branch-commit - if can?(current_user, :read_build, build) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 84243e4306d..6dff955ea3d 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -4,7 +4,7 @@ %tr.commit %td.commit-link - = render "ci/status/icon_with_label", subject: pipeline + = render "ci/status/icon_with_description", subject: pipeline %td = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 69cb1631ee8..1dd07ae1a2a 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -8,7 +8,7 @@ %tr.generic_commit_status{class: ('retried' if retried)} %td.status - = render "ci/status/icon_with_label", subject: generic_commit_status + = render "ci/status/icon_with_description", subject: generic_commit_status %td.generic_commit_status-link - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index f7385184a2b..d05697b4ee3 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,6 +1,6 @@ .page-content-header .header-main-content - = render "ci/status/icon_with_label", subject: @pipeline + = render "ci/status/icon_with_description", subject: @pipeline %strong Pipeline ##{@commit.pipelines.last.id} triggered #{time_ago_with_tooltip(@commit.authored_date)} by = author_avatar(@commit, size: 24) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index d8c87fae5a1..cb845ae7f92 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -15,9 +15,9 @@ %li.build{ class: ("playable" if is_playable) } .curve .build-content - = render "projects/#{status.to_partial_path}_pipeline", subject: status + = render 'ci/status/icon_with_name_and_action', subject: status - else %li.build .curve .dropdown.inline.build-content - = render "projects/stage/in_stage_group", name: group_name, subject: grouped_statuses + = render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses From e8aae61ad07c3fc3d62394ba620c3b8654c29f65 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 18:18:30 +0100 Subject: [PATCH 074/386] Add action_class/action_title --- lib/gitlab/ci/status/build/cancelable.rb | 6 +++++- lib/gitlab/ci/status/build/play.rb | 8 ++++++++ lib/gitlab/ci/status/build/retryable.rb | 6 +++++- lib/gitlab/ci/status/build/stop.rb | 6 +++++- lib/gitlab/ci/status/core.rb | 7 +++++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb index 88be0cd924b..a979fe7d573 100644 --- a/lib/gitlab/ci/status/build/cancelable.rb +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -10,7 +10,7 @@ module Gitlab end def action_icon - 'remove' + 'ban' end def action_path @@ -23,6 +23,10 @@ module Gitlab :post end + def action_title + 'Cancel' + end + def self.matches?(build, user) build.cancelable? end diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 57c7058fe84..e3066d40a37 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -17,10 +17,18 @@ module Gitlab can?(user, :update_build, subject) end + def action_title + 'Play' + end + def action_icon 'play' end + def action_class + 'ci-play-icon' + end + def action_path play_namespace_project_build_path(subject.project.namespace, subject.project, diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb index 69f2ad1d277..8e38d6a8523 100644 --- a/lib/gitlab/ci/status/build/retryable.rb +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -10,7 +10,11 @@ module Gitlab end def action_icon - 'repeat' + 'refresh' + end + + def action_title + 'Retry' end def action_path diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index cd9bd959a7c..487fd033960 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -26,7 +26,11 @@ module Gitlab end def action_icon - :play + 'stop' + end + + def action_title + 'Stop' end def action_path diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 7e9f6e35012..dd3a824e486 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -49,6 +49,9 @@ module Gitlab raise NotImplementedError end + def action_class + end + def action_path raise NotImplementedError end @@ -56,6 +59,10 @@ module Gitlab def action_method raise NotImplementedError end + + def action_title + raise NotImplementedError + end end end end From be1d3376c6525f4ad24a6050d776fc3967e201a1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 18:21:11 +0100 Subject: [PATCH 075/386] Revert some unneeded changes --- app/views/projects/stage/_graph.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index cb845ae7f92..c0ad7b07c77 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -15,7 +15,7 @@ %li.build{ class: ("playable" if is_playable) } .curve .build-content - = render 'ci/status/icon_with_name_and_action', subject: status + = render "projects/#{status.to_partial_path}_pipeline", subject: status - else %li.build .curve From b64cf8405c551e79520e8ce4eef2dc17259c41e3 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 8 Dec 2016 17:58:32 +0000 Subject: [PATCH 076/386] Renders new icons for the pipeline graph --- app/helpers/ci_status_helper.rb | 66 +++++++------------ app/views/ci/status/_icon_with_name.html.haml | 3 +- .../_icon_with_name_and_action.html.haml | 2 +- ...ed.svg => _icon_status_canceled_graph.svg} | 0 ...ted.svg => _icon_status_created_graph.svg} | 0 ...iled.svg => _icon_status_failed_graph.svg} | 0 ...nual.svg => _icon_status_manual_graph.svg} | 0 ...ing.svg => _icon_status_pending_graph.svg} | 0 ...ing.svg => _icon_status_running_graph.svg} | 0 ...ped.svg => _icon_status_skipped_graph.svg} | 0 ...ess.svg => _icon_status_success_graph.svg} | 0 ...ing.svg => _icon_status_warning_graph.svg} | 0 12 files changed, 25 insertions(+), 46 deletions(-) rename app/views/shared/icons/{_icon_graph_job_cancelled.svg => _icon_status_canceled_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_created.svg => _icon_status_created_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_failed.svg => _icon_status_failed_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_manual.svg => _icon_status_manual_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_pending.svg => _icon_status_pending_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_running.svg => _icon_status_running_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_skipped.svg => _icon_status_skipped_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_success.svg => _icon_status_success_graph.svg} (100%) rename app/views/shared/icons/{_icon_graph_job_warning.svg => _icon_status_warning_graph.svg} (100%) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 7ddef2cdd5f..d9f5e01f0dc 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -25,54 +25,32 @@ module CiStatusHelper status.humanize end - def ci_icon_for_status(status, graph: nil) + def ci_icon_for_status(status) if detailed_status?(status) return custom_icon(status.icon) end - if graph - icon_name = - case status - when 'success' - 'icon_graph_job_success' - when 'success_with_warnings' - 'icon_graph_job_warning' - when 'failed' - 'icon_graph_job_failed' - when 'pending' - 'icon_graph_job_pending' - when 'running' - 'icon_graph_job_running' - when 'created' - 'icon_graph_job_created' - when 'skipped' - 'icon_graph_job_skipped' - else - 'icon_graph_job_canceled' - end - else - icon_name = - case status - when 'success' - 'icon_status_success' - when 'success_with_warnings' - 'icon_status_warning' - when 'failed' - 'icon_status_failed' - when 'pending' - 'icon_status_pending' - when 'running' - 'icon_status_running' - when 'play' - 'icon_play' - when 'created' - 'icon_status_created' - when 'skipped' - 'icon_status_skipped' - else - 'icon_status_canceled' - end - end + icon_name = + case status + when 'success' + 'icon_status_success' + when 'success_with_warnings' + 'icon_status_warning' + when 'failed' + 'icon_status_failed' + when 'pending' + 'icon_status_pending' + when 'running' + 'icon_status_running' + when 'play' + 'icon_play' + when 'created' + 'icon_status_created' + when 'skipped' + 'icon_status_skipped' + else + 'icon_status_canceled' + end custom_icon(icon_name) end diff --git a/app/views/ci/status/_icon_with_name.html.haml b/app/views/ci/status/_icon_with_name.html.haml index 028e1fe9402..a467316ef47 100644 --- a/app/views/ci/status/_icon_with_name.html.haml +++ b/app/views/ci/status/_icon_with_name.html.haml @@ -1,10 +1,11 @@ - detailed_status = subject.detailed_status(current_user) - details_path = detailed_status.details_path if detailed_status.has_details? - klass = "ci-status-icon ci-status-icon-#{detailed_status}" +- status_icon = graph ? "#{detailed_status.icon}_graph" : detailed_status.icon - if details_path = link_to details_path, class: klass, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do - %span{ class: klass }= custom_icon(detailed_status.icon) + %span{ class: klass }= custom_icon(status_icon) .ci-status-text= subject.name - else %span{ class: klass }= custom_icon(detailed_status.icon) diff --git a/app/views/ci/status/_icon_with_name_and_action.html.haml b/app/views/ci/status/_icon_with_name_and_action.html.haml index 76db3b7f38a..b912c212534 100644 --- a/app/views/ci/status/_icon_with_name_and_action.html.haml +++ b/app/views/ci/status/_icon_with_name_and_action.html.haml @@ -1,4 +1,4 @@ -= render "ci/status/icon_with_name", subject: subject += render "ci/status/icon_with_name", subject: subject, graph: true - detailed_status = subject.detailed_status(current_user) - if detailed_status.has_action? diff --git a/app/views/shared/icons/_icon_graph_job_cancelled.svg b/app/views/shared/icons/_icon_status_canceled_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_cancelled.svg rename to app/views/shared/icons/_icon_status_canceled_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_created.svg b/app/views/shared/icons/_icon_status_created_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_created.svg rename to app/views/shared/icons/_icon_status_created_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_failed.svg b/app/views/shared/icons/_icon_status_failed_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_failed.svg rename to app/views/shared/icons/_icon_status_failed_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_manual.svg b/app/views/shared/icons/_icon_status_manual_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_manual.svg rename to app/views/shared/icons/_icon_status_manual_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_pending.svg b/app/views/shared/icons/_icon_status_pending_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_pending.svg rename to app/views/shared/icons/_icon_status_pending_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_running.svg b/app/views/shared/icons/_icon_status_running_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_running.svg rename to app/views/shared/icons/_icon_status_running_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_skipped.svg b/app/views/shared/icons/_icon_status_skipped_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_skipped.svg rename to app/views/shared/icons/_icon_status_skipped_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_success.svg b/app/views/shared/icons/_icon_status_success_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_success.svg rename to app/views/shared/icons/_icon_status_success_graph.svg diff --git a/app/views/shared/icons/_icon_graph_job_warning.svg b/app/views/shared/icons/_icon_status_warning_graph.svg similarity index 100% rename from app/views/shared/icons/_icon_graph_job_warning.svg rename to app/views/shared/icons/_icon_status_warning_graph.svg From 1c489296fd43fa45621b2ab6e6982327fb864b56 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 9 Dec 2016 12:25:29 +0100 Subject: [PATCH 077/386] Remove trailing whitespace when generating changelog entry --- bin/changelog | 11 ++++++++--- .../nuke-ugly-spaces-in-changelog-generator.yml | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/nuke-ugly-spaces-in-changelog-generator.yml diff --git a/bin/changelog b/bin/changelog index e07b1ad237a..4c894f8ff5b 100755 --- a/bin/changelog +++ b/bin/changelog @@ -84,12 +84,15 @@ class ChangelogEntry end end + private + def contents - YAML.dump( + yaml_content = YAML.dump( 'title' => title, 'merge_request' => options.merge_request, 'author' => options.author ) + remove_trailing_whitespace(yaml_content) end def write @@ -101,8 +104,6 @@ class ChangelogEntry exec("git commit --amend") end - private - def fail_with(message) $stderr.puts "\e[31merror\e[0m #{message}" exit 1 @@ -160,6 +161,10 @@ class ChangelogEntry def branch_name @branch_name ||= %x{git symbolic-ref --short HEAD}.strip end + + def remove_trailing_whitespace(yaml_content) + yaml_content.gsub(/ +$/, '') + end end if $0 == __FILE__ diff --git a/changelogs/unreleased/nuke-ugly-spaces-in-changelog-generator.yml b/changelogs/unreleased/nuke-ugly-spaces-in-changelog-generator.yml new file mode 100644 index 00000000000..fd173031107 --- /dev/null +++ b/changelogs/unreleased/nuke-ugly-spaces-in-changelog-generator.yml @@ -0,0 +1,4 @@ +--- +title: Remove trailing whitespace when generating changelog entry +merge_request: 7948 +author: From 858ec6048223f2eec8c1386d33e3a6c5827233cb Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Dec 2016 16:59:23 +0200 Subject: [PATCH 078/386] Handling OAuth2 errors --- app/controllers/import/bitbucket_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 12716d60e7d..b9cc6556140 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -2,7 +2,7 @@ class Import::BitbucketController < Import::BaseController before_action :verify_bitbucket_import_enabled before_action :bitbucket_auth, except: :callback - rescue_from OAuth::Error, with: :bitbucket_unauthorized + rescue_from OAuth2::Error, with: :bitbucket_unauthorized rescue_from Bitbucket::Error::Unauthorized, with: :bitbucket_unauthorized def callback From cc30a9f7ed436fd906c1e24a195414f2f84ee61c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Dec 2016 17:25:42 +0200 Subject: [PATCH 079/386] Fix rubocop[ci skip] --- lib/gitlab/bitbucket_import/importer.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 8852f5b0f3f..e00a90da980 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -1,10 +1,10 @@ module Gitlab module BitbucketImport class Importer - LABELS = [{ title: 'bug', color: '#FF0000'}, - { title: 'enhancement', color: '#428BCA'}, - { title: 'proposal', color: '#69D100'}, - { title: 'task', color: '#7F8C8D'}].freeze + LABELS = [{ title: 'bug', color: '#FF0000' }, + { title: 'enhancement', color: '#428BCA' }, + { title: 'proposal', color: '#69D100' }, + { title: 'task', color: '#7F8C8D' }].freeze attr_reader :project, :client From 1e8271b60bcd16b6fcc20d574c9583d593084eef Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 9 Dec 2016 17:24:21 +0000 Subject: [PATCH 080/386] Adds new partial for graph icons. Fix tests --- app/views/ci/status/_graph_icon_with_name.html.haml | 12 ++++++++++++ .../_graph_icon_with_name_and_action.html.haml | 8 ++++++++ app/views/ci/status/_icon_with_name.html.haml | 3 +-- .../ci/status/_icon_with_name_and_action.html.haml | 2 +- app/views/projects/stage/_graph.html.haml | 2 +- app/views/projects/stage/_in_stage_group.html.haml | 2 +- spec/features/projects/pipelines/pipeline_spec.rb | 6 +++--- 7 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 app/views/ci/status/_graph_icon_with_name.html.haml create mode 100644 app/views/ci/status/_graph_icon_with_name_and_action.html.haml diff --git a/app/views/ci/status/_graph_icon_with_name.html.haml b/app/views/ci/status/_graph_icon_with_name.html.haml new file mode 100644 index 00000000000..51037a3bd20 --- /dev/null +++ b/app/views/ci/status/_graph_icon_with_name.html.haml @@ -0,0 +1,12 @@ +- detailed_status = subject.detailed_status(current_user) +- details_path = detailed_status.details_path if detailed_status.has_details? +- klass = "ci-status-icon ci-status-icon-#{detailed_status}" +- graph_status_icon = "#{detailed_status.icon}_graph" + +- if details_path + = link_to details_path, class: klass, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do + %span{ class: klass }= custom_icon(graph_status_icon) + .ci-status-text= subject.name +- else + %span{ class: klass }= custom_icon(graph_status_icon) + .ci-status-text= subject.name diff --git a/app/views/ci/status/_graph_icon_with_name_and_action.html.haml b/app/views/ci/status/_graph_icon_with_name_and_action.html.haml new file mode 100644 index 00000000000..525075ced70 --- /dev/null +++ b/app/views/ci/status/_graph_icon_with_name_and_action.html.haml @@ -0,0 +1,8 @@ += render "ci/status/graph_icon_with_name", subject: subject + +- detailed_status = subject.detailed_status(current_user) +- if detailed_status.has_action? + = link_to detailed_status.action_path, method: detailed_status.action_method, + title: "#{subject.name}: #{detailed_status.action_title}", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon(detailed_status.action_icon, class: detailed_status.action_class) diff --git a/app/views/ci/status/_icon_with_name.html.haml b/app/views/ci/status/_icon_with_name.html.haml index a467316ef47..028e1fe9402 100644 --- a/app/views/ci/status/_icon_with_name.html.haml +++ b/app/views/ci/status/_icon_with_name.html.haml @@ -1,11 +1,10 @@ - detailed_status = subject.detailed_status(current_user) - details_path = detailed_status.details_path if detailed_status.has_details? - klass = "ci-status-icon ci-status-icon-#{detailed_status}" -- status_icon = graph ? "#{detailed_status.icon}_graph" : detailed_status.icon - if details_path = link_to details_path, class: klass, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do - %span{ class: klass }= custom_icon(status_icon) + %span{ class: klass }= custom_icon(detailed_status.icon) .ci-status-text= subject.name - else %span{ class: klass }= custom_icon(detailed_status.icon) diff --git a/app/views/ci/status/_icon_with_name_and_action.html.haml b/app/views/ci/status/_icon_with_name_and_action.html.haml index b912c212534..76db3b7f38a 100644 --- a/app/views/ci/status/_icon_with_name_and_action.html.haml +++ b/app/views/ci/status/_icon_with_name_and_action.html.haml @@ -1,4 +1,4 @@ -= render "ci/status/icon_with_name", subject: subject, graph: true += render "ci/status/icon_with_name", subject: subject - detailed_status = subject.detailed_status(current_user) - if detailed_status.has_action? diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index 745b6d143f4..255091cbfe8 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -14,7 +14,7 @@ %li.build{ class: ("playable" if is_playable) } .curve .build-content - = render 'ci/status/icon_with_name_and_action', subject: status + = render 'ci/status/graph_icon_with_name_and_action', subject: status - else %li.build .curve diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index 5c9b6549b37..70101ccf806 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -10,4 +10,4 @@ %ul - subject.each do |status| %li.dropdown-build - = render 'ci/status/icon_with_name_and_action', subject: status + = render 'ci/status/graph_icon_with_name_and_action', subject: status diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index e21de05ac64..7358931b9f0 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -40,7 +40,7 @@ describe "Pipelines", feature: true, js: true do context 'pipeline graph' do it 'shows a running icon and a cancel action for the running build' do - page.within('.stage-column:first-child .build:first-child') do + page.within('.stage-column:nth-child(2) .build:first-child') do expect(page).to have_selector('.ci-status-icon-running') expect(page).to have_content('deploy') expect(page).to have_selector('.ci-action-icon-container .fa-ban') @@ -56,7 +56,7 @@ describe "Pipelines", feature: true, js: true do end it 'shows the failed icon and a retry action for the failed build' do - page.within('.stage-column:nth-child(2) .build') do + page.within('.stage-column:first-child .build') do expect(page).to have_selector('.ci-status-icon-failed') expect(page).to have_content('test') expect(page).to have_selector('.ci-action-icon-container .fa-refresh') @@ -64,7 +64,7 @@ describe "Pipelines", feature: true, js: true do end it 'shows the skipped icon and a play action for the manual build' do - page.within('.stage-column:first-child .build:nth-child(2)') do + page.within('.stage-column:nth-child(2) .build:nth-child(2)') do expect(page).to have_selector('.ci-status-icon-skipped') expect(page).to have_content('manual') expect(page).to have_selector('.ci-action-icon-container .ci-play-icon') From ff2193a3db558214fab90bb644be6967a03176a0 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Dec 2016 19:40:22 +0200 Subject: [PATCH 081/386] Fix specs --- lib/gitlab/bitbucket_import/importer.rb | 6 +----- spec/lib/gitlab/bitbucket_import/importer_spec.rb | 14 +++++++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index e00a90da980..a0a17333185 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -58,7 +58,7 @@ module Gitlab updated_at: issue.updated_at ) - assign_label(issue, label_name) + issue.labels << @labels[label_name] if issue.persisted? client.issue_comments(repo, issue.iid).each do |comment| @@ -92,10 +92,6 @@ module Gitlab end end - def assign_label(issue, label_name) - issue.labels << @labels[label_name] - end - def import_pull_requests pull_requests = client.pull_requests(repo) diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index ef4fc9fd08e..353312675d6 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -18,6 +18,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do "closed" # undocumented status ] end + let(:sample_issues_statuses) do issues = [] @@ -26,6 +27,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do id: index, state: status, title: "Issue #{index}", + kind: 'bug', content: { raw: "Some content to issue #{index}", markup: "markdown", @@ -38,6 +40,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do end let(:project_identifier) { 'namespace/repo' } + let(:data) do { 'bb_session' => { @@ -46,6 +49,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do } } end + let(:project) do create( :project, @@ -53,7 +57,9 @@ describe Gitlab::BitbucketImport::Importer, lib: true do import_data: ProjectImportData.new(credentials: data) ) end + let(:importer) { Gitlab::BitbucketImport::Importer.new(project) } + let(:issues_statuses_sample_data) do { count: sample_issues_statuses.count, @@ -77,6 +83,12 @@ describe Gitlab::BitbucketImport::Importer, lib: true do headers: { "Content-Type" => "application/json" }, body: issues_statuses_sample_data.to_json) + stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on"). + with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Bearer', 'User-Agent'=>'Faraday v0.9.2'}). + to_return(:status => 200, + :body => "", + :headers => {}) + sample_issues_statuses.each_with_index do |issue, index| stub_request( :get, @@ -97,7 +109,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do end it 'map statuses to open or closed' do - # HACK: Bitbucket::Representation.const_get('Issue') seems to return Issue without this + # HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this Bitbucket::Representation::Issue.new({}) importer.execute From ee10a1cc5157e78384bac16f9ee2cf43d66513df Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Wed, 30 Nov 2016 12:37:54 -0800 Subject: [PATCH 082/386] 20916 Shorten line length of issues and mrs --- app/assets/javascripts/issue.js | 6 ++++++ app/assets/javascripts/merge_request.js | 6 ++++++ app/assets/stylesheets/framework/layout.scss | 4 ++++ app/assets/stylesheets/framework/variables.scss | 3 +++ app/assets/stylesheets/pages/issuable.scss | 13 +++++++++++++ 5 files changed, 32 insertions(+) diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 8540b199aba..56a2b6a5295 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -11,6 +11,7 @@ function Issue() { this.submitNoteForm = bind(this.submitNoteForm, this); // Prevent duplicate event bindings + this.limitContainerWidth(); this.disableTaskList(); if ($('a.btn-close').length) { this.initTaskList(); @@ -21,6 +22,11 @@ this.initCanCreateBranch(); } + Issue.prototype.limitContainerWidth = function() { + var $wrapper = $('.content-wrapper .container-fluid'); + $wrapper.addClass('limit-container-width') + }; + Issue.prototype.initTaskList = function() { $('.detail-page-description .js-task-list-container').taskList('enable'); return $(document).on('tasklist:changed', '.detail-page-description .js-task-list-container', this.updateTaskList); diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index 88c3636be6c..b3a1ec39c13 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -14,6 +14,7 @@ // Options: // action - String, current controller action // + this.limitContainerWidth(); this.opts = opts != null ? opts : {}; this.submitNoteForm = bind(this.submitNoteForm, this); this.$el = $('.merge-request'); @@ -31,6 +32,11 @@ } } + MergeRequest.prototype.limitContainerWidth = function() { + var $wrapper = $('.content-wrapper .container-fluid'); + $wrapper.addClass('limit-container-width') + }; + // Local jQuery finder MergeRequest.prototype.$ = function(selector) { return this.$el.find(selector); diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index dfaf2f7f1d3..66711aa1804 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -26,6 +26,10 @@ body { .container-limited { max-width: $fixed-layout-width; + + &.limit-container-width { + max-width: $limited-layout-width; + } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 18716813c48..55d97b9219c 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -166,6 +166,9 @@ $row-hover-border: #b2d7ff; $progress-color: #c0392b; $header-height: 50px; $fixed-layout-width: 1280px; +$limited-layout-width: 958px; +$line-length-width: 700px; +$gl-avatar-size: 40px; $error-exclamation-point: #e62958; $border-radius-default: 2px; $btn-transparent-color: #8f8f8f; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 90587b9425b..6c35496b846 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -1,3 +1,16 @@ +.container-limited.limit-container-width { + .issue-details { + .description, + .note-body { + p, + ul, + ol, + .code { + max-width: $line-length-width; + } + } + } +} .issuable-details { section { .issuable-discussion { From 258ab29435d4a02ab5ded6c19de597fdf847ef80 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 2 Dec 2016 10:13:04 -0600 Subject: [PATCH 083/386] Add content_class for limited width --- app/assets/javascripts/issue.js | 6 ------ app/assets/javascripts/merge_request.js | 6 ------ app/assets/stylesheets/pages/issuable.scss | 1 + app/views/projects/issues/show.html.haml | 1 + app/views/projects/merge_requests/_show.html.haml | 1 + 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 56a2b6a5295..8540b199aba 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -11,7 +11,6 @@ function Issue() { this.submitNoteForm = bind(this.submitNoteForm, this); // Prevent duplicate event bindings - this.limitContainerWidth(); this.disableTaskList(); if ($('a.btn-close').length) { this.initTaskList(); @@ -22,11 +21,6 @@ this.initCanCreateBranch(); } - Issue.prototype.limitContainerWidth = function() { - var $wrapper = $('.content-wrapper .container-fluid'); - $wrapper.addClass('limit-container-width') - }; - Issue.prototype.initTaskList = function() { $('.detail-page-description .js-task-list-container').taskList('enable'); return $(document).on('tasklist:changed', '.detail-page-description .js-task-list-container', this.updateTaskList); diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index b3a1ec39c13..88c3636be6c 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -14,7 +14,6 @@ // Options: // action - String, current controller action // - this.limitContainerWidth(); this.opts = opts != null ? opts : {}; this.submitNoteForm = bind(this.submitNoteForm, this); this.$el = $('.merge-request'); @@ -32,11 +31,6 @@ } } - MergeRequest.prototype.limitContainerWidth = function() { - var $wrapper = $('.content-wrapper .container-fluid'); - $wrapper.addClass('limit-container-width') - }; - // Local jQuery finder MergeRequest.prototype.$ = function(selector) { return this.$el.find(selector); diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 6c35496b846..2357dd2fe6f 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -11,6 +11,7 @@ } } } + .issuable-details { section { .issuable-discussion { diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index bd629b5c519..981bf640a6b 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,3 +1,4 @@ +- @content_class = "limit-container-width" - page_title "#{@issue.title} (#{@issue.to_reference})", "Issues" - page_description @issue.description - page_card_attributes @issue.card_attributes diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 896f10104fa..0db5548d36e 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,3 +1,4 @@ +- @content_class = "limit-container-width" - page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests" - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes From 091970208e0c8e7aefb6e7dcfafb4c81188c27cf Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 9 Dec 2016 15:17:13 -0800 Subject: [PATCH 084/386] Return repositories to which user is a member, not just owner --- lib/bitbucket/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index e23da4556aa..a9f405e659b 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -35,7 +35,7 @@ module Bitbucket end def repos - path = "/repositories/#{user.username}" + path = "/repositories/#{user.username}?role=member" get_collection(path, :repo) end From 1d7f85aeef624a83f0b225217a23c8f5189cde54 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 9 Dec 2016 15:28:49 -0800 Subject: [PATCH 085/386] Fix query for importing all projects for member --- lib/bitbucket/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index a9f405e659b..5c2ef2a4509 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -35,7 +35,7 @@ module Bitbucket end def repos - path = "/repositories/#{user.username}?role=member" + path = "/repositories?role=member" get_collection(path, :repo) end From a2a16503a8ecfe1b38f6ef8e31c96b53537dfe02 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 12 Dec 2016 12:12:37 +0000 Subject: [PATCH 086/386] Fix vertical alignment of action icon with status icon --- app/assets/stylesheets/pages/pipelines.scss | 44 +++++++++------------ app/views/projects/stage/_graph.html.haml | 3 +- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 304a7932a06..75b127eb7f6 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -654,6 +654,7 @@ i { font-size: 11px; + margin-top: 0; } } @@ -672,33 +673,26 @@ } // Action Icons -.ci-action-icon-container { - padding: 0; +.ci-action-icon-container .ci-action-icon-wrapper { + float: right; + margin-top: -1px; - .ci-action-icon-wrapper { - display: inline-block; - float: right; + i { + color: $stage-badge-text; + border-radius: 100%; + border: 1px solid $stage-badge-text; + padding: 5px 6px; + font-size: 13px; + background: $white-light; - i { - color: $stage-badge-text; - border-radius: 100%; - border: 1px solid $stage-badge-text; - text-align: center; - display: table-cell; - vertical-align: middle; - padding: 5px; - font-size: 13px; - background: $white-light; - - &:hover { - color: $gl-text-color; - background-color: $stage-hover-bg; - border: 1px solid $stage-hover-bg; - } - } - - .ci-play-icon { - padding: 5px 4px 5px 7px; + &:hover { + color: $gl-text-color; + background-color: $stage-hover-bg; + border: 1px solid $stage-hover-bg; } } + + .ci-play-icon { + padding: 5px 5px 5px 7px; + } } diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index 255091cbfe8..cf3050eea63 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -10,8 +10,7 @@ - status_groups.each do |group_name, grouped_statuses| - if grouped_statuses.one? - status = grouped_statuses.first - - is_playable = status.playable? && can?(current_user, :update_build, @project) - %li.build{ class: ("playable" if is_playable) } + %li.build .curve .build-content = render 'ci/status/graph_icon_with_name_and_action', subject: status From eb09395b2b5527e271c8e155ff6403953f72fef6 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 12 Dec 2016 12:58:42 +0000 Subject: [PATCH 087/386] Upgrade NGINX configuration files to add websocket support --- lib/support/nginx/gitlab | 7 +++++++ lib/support/nginx/gitlab-ssl | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index d521de28e8a..2f7c34a3f31 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -20,6 +20,11 @@ upstream gitlab-workhorse { server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0; } +map $http_upgrade $connection_upgrade_gitlab { + default upgrade; + '' close; +} + ## Normal HTTP host server { ## Either remove "default_server" from the listen line below, @@ -53,6 +58,8 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade_gitlab; proxy_pass http://gitlab-workhorse; } diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index bf014b56cf6..5661394058d 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -24,6 +24,11 @@ upstream gitlab-workhorse { server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0; } +map $http_upgrade $connection_upgrade_gitlab_ssl { + default upgrade; + '' close; +} + ## Redirects all HTTP traffic to the HTTPS host server { ## Either remove "default_server" from the listen line below, @@ -98,6 +103,9 @@ server { proxy_set_header X-Forwarded-Ssl on; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade_gitlab_ssl; + proxy_pass http://gitlab-workhorse; } From 314c4746bc24a31efe88b142cd83ab36c3473cc9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 12 Dec 2016 16:16:51 +0200 Subject: [PATCH 088/386] Specs for Bitbucket::Connections and Bitbucket::Collections --- lib/bitbucket/connection.rb | 20 ++++++-------- lib/bitbucket/paginator.rb | 4 +-- spec/lib/bitbucket/collection_spec.rb | 23 ++++++++++++++++ spec/lib/bitbucket/connection_spec.rb | 26 +++++++++++++++++++ .../bitbucket_import/project_creator_spec.rb | 2 ++ 5 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 spec/lib/bitbucket/collection_spec.rb create mode 100644 spec/lib/bitbucket/connection_spec.rb diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index 692a596c057..c150a20761e 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -15,18 +15,6 @@ module Bitbucket @refresh_token = options[:refresh_token] end - def client - @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options) - end - - def connection - @connection ||= OAuth2::AccessToken.new(client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) - end - - def set_default_query_parameters(params = {}) - @default_query.merge!(params) - end - def get(path, extra_query = {}) refresh! if expired? @@ -52,6 +40,14 @@ module Bitbucket attr_reader :expires_at, :expires_in, :refresh_token, :token + def client + @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options) + end + + def connection + @connection ||= OAuth2::AccessToken.new(client, @token, refresh_token: @refresh_token, expires_at: @expires_at, expires_in: @expires_in) + end + def build_url(path) return path if path.starts_with?(root_url) diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb index 641a6ed79d6..b38cd99855c 100644 --- a/lib/bitbucket/paginator.rb +++ b/lib/bitbucket/paginator.rb @@ -7,8 +7,6 @@ module Bitbucket @type = type @url = url @page = nil - - connection.set_default_query_parameters(pagelen: PAGE_LENGTH, sort: :created_on) end def items @@ -31,7 +29,7 @@ module Bitbucket end def fetch_next_page - parsed_response = connection.get(next_url) + parsed_response = connection.get(next_url, { pagelen: PAGE_LENGTH, sort: :created_on }) Page.new(parsed_response, type) end end diff --git a/spec/lib/bitbucket/collection_spec.rb b/spec/lib/bitbucket/collection_spec.rb new file mode 100644 index 00000000000..eeed61b0488 --- /dev/null +++ b/spec/lib/bitbucket/collection_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +# Emulates paginator. It returns 2 pages with results +class TestPaginator + def initialize + @current_page = 0 + end + + def items + @current_page += 1 + + raise StopIteration if @current_page > 2 + + ["result_1_page_#{@current_page}", "result_2_page_#{@current_page}"] + end +end + +describe Bitbucket::Collection do + it "iterates paginator" do + collection = described_class.new(TestPaginator.new) + expect(collection.to_a).to match(["result_1_page_1", "result_2_page_1", "result_1_page_2", "result_2_page_2"]) + end +end diff --git a/spec/lib/bitbucket/connection_spec.rb b/spec/lib/bitbucket/connection_spec.rb new file mode 100644 index 00000000000..5242c6fac34 --- /dev/null +++ b/spec/lib/bitbucket/connection_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Bitbucket::Connection do + describe '#get' do + it 'calls OAuth2::AccessToken::get' do + expect_any_instance_of(OAuth2::AccessToken).to receive(:get).and_return(double(parsed: true)) + connection = described_class.new({}) + connection.get('/users') + end + end + + describe '#expired?' do + it 'calls connection.expired?' do + expect_any_instance_of(OAuth2::AccessToken).to receive(:expired?).and_return(true) + expect(described_class.new({}).expired?).to be_truthy + end + end + + describe '#refresh!' do + it 'calls connection.refresh!' do + response = double(token: nil, expires_at: nil, expires_in: nil, refresh_token: nil) + expect_any_instance_of(OAuth2::AccessToken).to receive(:refresh!).and_return(response) + described_class.new({}).refresh! + end + end +end diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb index bb007949557..b6d052a4612 100644 --- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::BitbucketImport::ProjectCreator, lib: true do let(:user) { create(:user) } + let(:repo) do double(name: 'Vim', slug: 'vim', @@ -12,6 +13,7 @@ describe Gitlab::BitbucketImport::ProjectCreator, lib: true do visibility_level: Gitlab::VisibilityLevel::PRIVATE, clone_url: 'ssh://git@bitbucket.org/asd/vim.git') end + let(:namespace){ create(:group, owner: user) } let(:token) { "asdasd12345" } let(:secret) { "sekrettt" } From 20a6183be6abe356c8e301c90536fa3252214127 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 12 Dec 2016 13:30:50 +0000 Subject: [PATCH 089/386] Update custom hooks docs and set 4.1.0 --- GITLAB_SHELL_VERSION | 2 +- doc/administration/custom_hooks.md | 22 +++++++++++++++------- doc/update/8.14-to-8.15.md | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index c4e41f94594..ee74734aa22 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -4.0.3 +4.1.0 diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md index 06291705702..80e5d80aa41 100644 --- a/doc/administration/custom_hooks.md +++ b/doc/administration/custom_hooks.md @@ -44,22 +44,30 @@ as appropriate. ## Chained hooks support -> [Introduced][93] in GitLab Shell 4.1.0. +> [Introduced][93] in GitLab Shell 4.1.0 and GitLab 8.15. -The hooks could be also placed in `hooks/.d` (global) or `custom_hooks/.d` (per project) -directories supporting chained execution of the hooks. +Hooks can be also placed in `hooks/.d` (global) or +`custom_hooks/.d` (per project) directories supporting chained +execution of the hooks. + +To look in a different directory for the global custom hooks (those in +`hooks/`), set `custom_hooks_dir` in gitlab-shell config. For +Omnibus installations, this can be set in `gitlab.rb`; and in source +installations, this can be set in `gitlab-shell/config.yml`. The hooks are searched and executed in this order: + 1. `.git/hooks/` - symlink to `gitlab-shell/hooks` global dir 1. `.git/hooks/` - executed by `git` itself, this is `gitlab-shell/hooks/` 1. `.git/custom_hooks/` - per project hook (this is already existing behavior) 1. `.git/custom_hooks/.d/*` - per project hooks -1. `.git/hooks/.d/*` - global hooks: all executable files (minus editor backup files) +1. `.git/hooks/.d/*` OR `//*` - global hooks: all executable files (minus editor backup files) -Files in `.d` directories need to be executable and not match the backup file pattern (`*~`). +Files in `.d` directories need to be executable and not match the backup file +pattern (`*~`). -The hooks of the same type are executed in order and execution stops on the first -script exiting with non-zero value. +The hooks of the same type are executed in order and execution stops on the +first script exiting with a non-zero value. ## Custom error messages diff --git a/doc/update/8.14-to-8.15.md b/doc/update/8.14-to-8.15.md index 5556dae2551..4eacab0c890 100644 --- a/doc/update/8.14-to-8.15.md +++ b/doc/update/8.14-to-8.15.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-15-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v4.0.3 +sudo -u git -H git checkout v4.1.0 ``` ### 6. Update gitlab-workhorse From 3a0fecb4924f1a6dbcc3e61041e0cac95ec3b21b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 12 Dec 2016 17:29:25 +0200 Subject: [PATCH 090/386] Spec for bitbucket page --- lib/bitbucket/page.rb | 1 + spec/lib/bitbucket/page_spec.rb | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 spec/lib/bitbucket/page_spec.rb diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb index 8f50f67f84d..2b0a3fe7b1a 100644 --- a/lib/bitbucket/page.rb +++ b/lib/bitbucket/page.rb @@ -23,6 +23,7 @@ module Bitbucket def parse_values(raw, bitbucket_rep_class) return [] unless raw['values'] && raw['values'].is_a?(Array) + bitbucket_rep_class.decorate(raw['values']) end diff --git a/spec/lib/bitbucket/page_spec.rb b/spec/lib/bitbucket/page_spec.rb new file mode 100644 index 00000000000..04d5a0470b1 --- /dev/null +++ b/spec/lib/bitbucket/page_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Bitbucket::Page do + let(:response) { { 'values' => [{ 'username' => 'Ben' }], 'pagelen' => 2, 'next' => '' } } + + before do + # Autoloading hack + Bitbucket::Representation::User.new({}) + end + + describe '#items' do + it 'returns collection of needed objects' do + page = described_class.new(response, :user) + + expect(page.items.first).to be_a(Bitbucket::Representation::User) + expect(page.items.count).to eq(1) + end + end + + describe '#attrs' do + it 'returns attributes' do + page = described_class.new(response, :user) + + expect(page.attrs.keys).to include(:pagelen, :next) + end + end + + describe '#next?' do + it 'returns true' do + page = described_class.new(response, :user) + + expect(page.next?).to be_truthy + end + + it 'returns false' do + response['next'] = nil + page = described_class.new(response, :user) + + expect(page.next?).to be_falsey + end + end + + describe '#next' do + it 'returns next attribute' do + page = described_class.new(response, :user) + + expect(page.next).to eq('') + end + end +end From c45484ba193baa811e50aaa106a2c0ed3721d6e8 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 12 Dec 2016 18:20:23 +0200 Subject: [PATCH 091/386] Spec for Bitbucket::Paginator --- spec/lib/bitbucket/paginator_spec.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 spec/lib/bitbucket/paginator_spec.rb diff --git a/spec/lib/bitbucket/paginator_spec.rb b/spec/lib/bitbucket/paginator_spec.rb new file mode 100644 index 00000000000..2c972da682e --- /dev/null +++ b/spec/lib/bitbucket/paginator_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Bitbucket::Paginator do + let(:last_page) { double(:page, next?: false, items: ['item_2']) } + let(:first_page) { double(:page, next?: true, next: last_page, items: ['item_1']) } + + describe 'items' do + it 'return items and raises StopIteration in the end' do + paginator = described_class.new(nil, nil, nil) + + allow(paginator).to receive(:fetch_next_page).and_return(first_page) + expect(paginator.items).to match(['item_1']) + + allow(paginator).to receive(:fetch_next_page).and_return(last_page) + expect(paginator.items).to match(['item_2']) + + allow(paginator).to receive(:fetch_next_page).and_return(nil) + expect{ paginator.items }.to raise_error(StopIteration) + end + end +end From a2be395401f6320d2722bbd98de0c046d05f0480 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 12 Dec 2016 20:41:55 +0200 Subject: [PATCH 092/386] specs for bitbucket representers --- .../representation/pull_request_comment.rb | 6 +-- .../bitbucket/representation/comment_spec.rb | 22 +++++++++ .../bitbucket/representation/issue_spec.rb | 42 +++++++++++++++++ .../pull_request_comment_spec.rb | 35 ++++++++++++++ .../representation/pull_request_spec.rb | 47 +++++++++++++++++++ .../lib/bitbucket/representation/user_spec.rb | 11 +++++ 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 spec/lib/bitbucket/representation/comment_spec.rb create mode 100644 spec/lib/bitbucket/representation/issue_spec.rb create mode 100644 spec/lib/bitbucket/representation/pull_request_comment_spec.rb create mode 100644 spec/lib/bitbucket/representation/pull_request_spec.rb create mode 100644 spec/lib/bitbucket/representation/user_spec.rb diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb index ae2b069d6a2..4f3809fbcea 100644 --- a/lib/bitbucket/representation/pull_request_comment.rb +++ b/lib/bitbucket/representation/pull_request_comment.rb @@ -6,15 +6,15 @@ module Bitbucket end def file_path - inline.fetch('path', nil) + inline.fetch('path') end def old_pos - inline.fetch('from', nil) + inline.fetch('from') end def new_pos - inline.fetch('to', nil) + inline.fetch('to') end def parent_id diff --git a/spec/lib/bitbucket/representation/comment_spec.rb b/spec/lib/bitbucket/representation/comment_spec.rb new file mode 100644 index 00000000000..5864193cbfc --- /dev/null +++ b/spec/lib/bitbucket/representation/comment_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Bitbucket::Representation::Comment do + describe '#author' do + it { expect(described_class.new('user' => { 'username' => 'Ben' }).author).to eq('Ben') } + it { expect(described_class.new({}).author).to eq('Anonymous') } + end + + describe '#note' do + it { expect(described_class.new('content' => { 'raw' => 'Text' }).note).to eq('Text') } + it { expect(described_class.new({}).note).to be_nil } + end + + describe '#created_at' do + it { expect(described_class.new('created_on' => Date.today).created_at).to eq(Date.today) } + end + + describe '#updated_at' do + it { expect(described_class.new('updated_on' => Date.today).updated_at).to eq(Date.today) } + it { expect(described_class.new('created_on' => Date.today).updated_at).to eq(Date.today) } + end +end diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb new file mode 100644 index 00000000000..56deae63bbc --- /dev/null +++ b/spec/lib/bitbucket/representation/issue_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Bitbucket::Representation::Issue do + describe '#iid' do + it { expect(described_class.new('id' => 1).iid).to eq(1) } + end + + describe '#kind' do + it { expect(described_class.new('kind' => 'bug').kind).to eq('bug') } + end + + describe '#author' do + it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' }}).author).to eq('Ben') } + it { expect(described_class.new({}).author).to eq('Anonymous') } + end + + describe '#description' do + it { expect(described_class.new({ 'content' => { 'raw' => 'Text' }}).description).to eq('Text') } + it { expect(described_class.new({}).description).to be_nil } + end + + describe '#state' do + it { expect(described_class.new({ 'state' => 'invalid' }).state).to eq('closed') } + it { expect(described_class.new({ 'state' => 'wontfix' }).state).to eq('closed') } + it { expect(described_class.new({ 'state' => 'resolved' }).state).to eq('closed') } + it { expect(described_class.new({ 'state' => 'duplicate' }).state).to eq('closed') } + it { expect(described_class.new({ 'state' => 'closed' }).state).to eq('closed') } + it { expect(described_class.new({ 'state' => 'opened' }).state).to eq('opened') } + end + + describe '#title' do + it { expect(described_class.new('title' => 'Issue').title).to eq('Issue') } + end + + describe '#created_at' do + it { expect(described_class.new('created_on' => Date.today).created_at).to eq(Date.today) } + end + + describe '#updated_at' do + it { expect(described_class.new('edited_on' => Date.today).updated_at).to eq(Date.today) } + end +end diff --git a/spec/lib/bitbucket/representation/pull_request_comment_spec.rb b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb new file mode 100644 index 00000000000..8377f0540cd --- /dev/null +++ b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Bitbucket::Representation::PullRequestComment do + describe '#iid' do + it { expect(described_class.new('id' => 1).iid).to eq(1) } + end + + describe '#file_path' do + it { expect(described_class.new('inline' => { 'path' => '/path' }).file_path).to eq('/path') } + end + + describe '#old_pos' do + it { expect(described_class.new('inline' => { 'from' => 3 }).old_pos).to eq(3) } + end + + describe '#new_pos' do + it { expect(described_class.new('inline' => { 'to' => 3 }).new_pos).to eq(3) } + end + + + describe '#parent_id' do + it { expect(described_class.new({ 'parent' => { 'id' => 2 }}).parent_id).to eq(2) } + it { expect(described_class.new({}).parent_id).to be_nil } + end + + describe '#inline?' do + it { expect(described_class.new('inline' => {}).inline?).to be_truthy } + it { expect(described_class.new({}).inline?).to be_falsey } + end + + describe '#has_parent?' do + it { expect(described_class.new('parent' => {}).has_parent?).to be_truthy } + it { expect(described_class.new({}).has_parent?).to be_falsey } + end +end diff --git a/spec/lib/bitbucket/representation/pull_request_spec.rb b/spec/lib/bitbucket/representation/pull_request_spec.rb new file mode 100644 index 00000000000..661422efddf --- /dev/null +++ b/spec/lib/bitbucket/representation/pull_request_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Bitbucket::Representation::PullRequest do + describe '#iid' do + it { expect(described_class.new('id' => 1).iid).to eq(1) } + end + + describe '#author' do + it { expect(described_class.new({ 'author' => { 'username' => 'Ben' }}).author).to eq('Ben') } + it { expect(described_class.new({}).author).to eq('Anonymous') } + end + + describe '#description' do + it { expect(described_class.new({ 'description' => 'Text' }).description).to eq('Text') } + it { expect(described_class.new({}).description).to be_nil } + end + + describe '#state' do + it { expect(described_class.new({ 'state' => 'MERGED' }).state).to eq('merged') } + it { expect(described_class.new({ 'state' => 'DECLINED' }).state).to eq('closed') } + it { expect(described_class.new({}).state).to eq('opened') } + end + + describe '#title' do + it { expect(described_class.new('title' => 'Issue').title).to eq('Issue') } + end + + describe '#source_branch_name' do + it { expect(described_class.new({ source: { branch: { name: 'feature' } } }.with_indifferent_access).source_branch_name).to eq('feature') } + it { expect(described_class.new({ source: {} }.with_indifferent_access).source_branch_name).to be_nil } + end + + describe '#source_branch_sha' do + it { expect(described_class.new({ source: { commit: { hash: 'abcd123' } } }.with_indifferent_access).source_branch_sha).to eq('abcd123') } + it { expect(described_class.new({ source: {} }.with_indifferent_access).source_branch_sha).to be_nil } + end + + describe '#target_branch_name' do + it { expect(described_class.new({ destination: { branch: { name: 'master' } } }.with_indifferent_access).target_branch_name).to eq('master') } + it { expect(described_class.new({ destination: {} }.with_indifferent_access).target_branch_name).to be_nil } + end + + describe '#target_branch_sha' do + it { expect(described_class.new({ destination: { commit: { hash: 'abcd123' } } }.with_indifferent_access).target_branch_sha).to eq('abcd123') } + it { expect(described_class.new({ destination: {} }.with_indifferent_access).target_branch_sha).to be_nil } + end +end diff --git a/spec/lib/bitbucket/representation/user_spec.rb b/spec/lib/bitbucket/representation/user_spec.rb new file mode 100644 index 00000000000..f79ff4edb7b --- /dev/null +++ b/spec/lib/bitbucket/representation/user_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe Bitbucket::Representation::User do + describe '#username' do + it 'returns correct value' do + user = described_class.new('username' => 'Ben') + + expect(user.username).to eq('Ben') + end + end +end From 1de2edc1f14a805afa23c6fde79bdd499af75f33 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 12 Dec 2016 21:09:17 +0000 Subject: [PATCH 093/386] Fix pipeline stroke position --- app/assets/stylesheets/pages/pipelines.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 75b127eb7f6..4a5b0b5922c 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -427,7 +427,7 @@ width: 21px; height: 25px; position: absolute; - top: -31px; + top: -30px; border-top: 2px solid $border-color; } From 0b9df1bd93f9b6b6a5714431d8b815b0225917b5 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 13 Dec 2016 11:30:12 +0000 Subject: [PATCH 094/386] Replace old icons with new ones --- app/views/shared/icons/_icon_status_canceled.svg | 2 +- app/views/shared/icons/_icon_status_canceled_graph.svg | 1 - app/views/shared/icons/_icon_status_created.svg | 2 +- app/views/shared/icons/_icon_status_created_graph.svg | 1 - app/views/shared/icons/_icon_status_failed.svg | 2 +- app/views/shared/icons/_icon_status_failed_graph.svg | 1 - .../{_icon_status_manual_graph.svg => _icon_status_manual.svg} | 0 app/views/shared/icons/_icon_status_pending.svg | 2 +- app/views/shared/icons/_icon_status_pending_graph.svg | 1 - app/views/shared/icons/_icon_status_running.svg | 2 +- app/views/shared/icons/_icon_status_running_graph.svg | 1 - app/views/shared/icons/_icon_status_skipped.svg | 2 +- app/views/shared/icons/_icon_status_skipped_graph.svg | 1 - app/views/shared/icons/_icon_status_success.svg | 2 +- app/views/shared/icons/_icon_status_success_graph.svg | 1 - app/views/shared/icons/_icon_status_warning.svg | 2 +- app/views/shared/icons/_icon_status_warning_graph.svg | 1 - 17 files changed, 8 insertions(+), 16 deletions(-) mode change 100644 => 100755 app/views/shared/icons/_icon_status_canceled.svg delete mode 100755 app/views/shared/icons/_icon_status_canceled_graph.svg mode change 100644 => 100755 app/views/shared/icons/_icon_status_created.svg delete mode 100755 app/views/shared/icons/_icon_status_created_graph.svg mode change 100644 => 100755 app/views/shared/icons/_icon_status_failed.svg delete mode 100755 app/views/shared/icons/_icon_status_failed_graph.svg rename app/views/shared/icons/{_icon_status_manual_graph.svg => _icon_status_manual.svg} (100%) mode change 100644 => 100755 app/views/shared/icons/_icon_status_pending.svg delete mode 100755 app/views/shared/icons/_icon_status_pending_graph.svg mode change 100644 => 100755 app/views/shared/icons/_icon_status_running.svg delete mode 100755 app/views/shared/icons/_icon_status_running_graph.svg mode change 100644 => 100755 app/views/shared/icons/_icon_status_skipped.svg delete mode 100755 app/views/shared/icons/_icon_status_skipped_graph.svg mode change 100644 => 100755 app/views/shared/icons/_icon_status_success.svg delete mode 100755 app/views/shared/icons/_icon_status_success_graph.svg mode change 100644 => 100755 app/views/shared/icons/_icon_status_warning.svg delete mode 100755 app/views/shared/icons/_icon_status_warning_graph.svg diff --git a/app/views/shared/icons/_icon_status_canceled.svg b/app/views/shared/icons/_icon_status_canceled.svg old mode 100644 new mode 100755 index 41a210a8ed9..bd5d04e1cd7 --- a/app/views/shared/icons/_icon_status_canceled.svg +++ b/app/views/shared/icons/_icon_status_canceled.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_canceled_graph.svg b/app/views/shared/icons/_icon_status_canceled_graph.svg deleted file mode 100755 index bd5d04e1cd7..00000000000 --- a/app/views/shared/icons/_icon_status_canceled_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_created.svg b/app/views/shared/icons/_icon_status_created.svg old mode 100644 new mode 100755 index 1f5c3b51b03..326ad04e017 --- a/app/views/shared/icons/_icon_status_created.svg +++ b/app/views/shared/icons/_icon_status_created.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_created_graph.svg b/app/views/shared/icons/_icon_status_created_graph.svg deleted file mode 100755 index 326ad04e017..00000000000 --- a/app/views/shared/icons/_icon_status_created_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_failed.svg b/app/views/shared/icons/_icon_status_failed.svg old mode 100644 new mode 100755 index af267b8938a..64da5aa31fc --- a/app/views/shared/icons/_icon_status_failed.svg +++ b/app/views/shared/icons/_icon_status_failed.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_failed_graph.svg b/app/views/shared/icons/_icon_status_failed_graph.svg deleted file mode 100755 index 64da5aa31fc..00000000000 --- a/app/views/shared/icons/_icon_status_failed_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_manual_graph.svg b/app/views/shared/icons/_icon_status_manual.svg similarity index 100% rename from app/views/shared/icons/_icon_status_manual_graph.svg rename to app/views/shared/icons/_icon_status_manual.svg diff --git a/app/views/shared/icons/_icon_status_pending.svg b/app/views/shared/icons/_icon_status_pending.svg old mode 100644 new mode 100755 index 516231d1b44..02d5da407e3 --- a/app/views/shared/icons/_icon_status_pending.svg +++ b/app/views/shared/icons/_icon_status_pending.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_pending_graph.svg b/app/views/shared/icons/_icon_status_pending_graph.svg deleted file mode 100755 index 02d5da407e3..00000000000 --- a/app/views/shared/icons/_icon_status_pending_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_running.svg b/app/views/shared/icons/_icon_status_running.svg old mode 100644 new mode 100755 index d2618bce200..532f4fee33c --- a/app/views/shared/icons/_icon_status_running.svg +++ b/app/views/shared/icons/_icon_status_running.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_running_graph.svg b/app/views/shared/icons/_icon_status_running_graph.svg deleted file mode 100755 index 532f4fee33c..00000000000 --- a/app/views/shared/icons/_icon_status_running_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg old mode 100644 new mode 100755 index 701f33bcbea..1998dfef9ea --- a/app/views/shared/icons/_icon_status_skipped.svg +++ b/app/views/shared/icons/_icon_status_skipped.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_skipped_graph.svg b/app/views/shared/icons/_icon_status_skipped_graph.svg deleted file mode 100755 index 1998dfef9ea..00000000000 --- a/app/views/shared/icons/_icon_status_skipped_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg old mode 100644 new mode 100755 index b7c21ba6971..eed5006bebe --- a/app/views/shared/icons/_icon_status_success.svg +++ b/app/views/shared/icons/_icon_status_success.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_success_graph.svg b/app/views/shared/icons/_icon_status_success_graph.svg deleted file mode 100755 index eed5006bebe..00000000000 --- a/app/views/shared/icons/_icon_status_success_graph.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/shared/icons/_icon_status_warning.svg b/app/views/shared/icons/_icon_status_warning.svg old mode 100644 new mode 100755 index 9191e0050a6..cb785635b7e --- a/app/views/shared/icons/_icon_status_warning.svg +++ b/app/views/shared/icons/_icon_status_warning.svg @@ -1 +1 @@ - + diff --git a/app/views/shared/icons/_icon_status_warning_graph.svg b/app/views/shared/icons/_icon_status_warning_graph.svg deleted file mode 100755 index cb785635b7e..00000000000 --- a/app/views/shared/icons/_icon_status_warning_graph.svg +++ /dev/null @@ -1 +0,0 @@ - From a06016d00cd363eec7ef748e5fda6459c911eaba Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 13 Dec 2016 11:38:34 +0000 Subject: [PATCH 095/386] Remove unneeded partial --- app/views/ci/status/_graph_icon_with_name.html.haml | 12 ------------ .../_graph_icon_with_name_and_action.html.haml | 8 -------- app/views/projects/stage/_graph.html.haml | 2 +- app/views/projects/stage/_in_stage_group.html.haml | 2 +- 4 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 app/views/ci/status/_graph_icon_with_name.html.haml delete mode 100644 app/views/ci/status/_graph_icon_with_name_and_action.html.haml diff --git a/app/views/ci/status/_graph_icon_with_name.html.haml b/app/views/ci/status/_graph_icon_with_name.html.haml deleted file mode 100644 index 51037a3bd20..00000000000 --- a/app/views/ci/status/_graph_icon_with_name.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- detailed_status = subject.detailed_status(current_user) -- details_path = detailed_status.details_path if detailed_status.has_details? -- klass = "ci-status-icon ci-status-icon-#{detailed_status}" -- graph_status_icon = "#{detailed_status.icon}_graph" - -- if details_path - = link_to details_path, class: klass, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do - %span{ class: klass }= custom_icon(graph_status_icon) - .ci-status-text= subject.name -- else - %span{ class: klass }= custom_icon(graph_status_icon) - .ci-status-text= subject.name diff --git a/app/views/ci/status/_graph_icon_with_name_and_action.html.haml b/app/views/ci/status/_graph_icon_with_name_and_action.html.haml deleted file mode 100644 index 525075ced70..00000000000 --- a/app/views/ci/status/_graph_icon_with_name_and_action.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -= render "ci/status/graph_icon_with_name", subject: subject - -- detailed_status = subject.detailed_status(current_user) -- if detailed_status.has_action? - = link_to detailed_status.action_path, method: detailed_status.action_method, - title: "#{subject.name}: #{detailed_status.action_title}", class: 'ci-action-icon-container' do - %i.ci-action-icon-wrapper - = icon(detailed_status.action_icon, class: detailed_status.action_class) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index cf3050eea63..6d280468262 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -13,7 +13,7 @@ %li.build .curve .build-content - = render 'ci/status/graph_icon_with_name_and_action', subject: status + = render 'ci/status/icon_with_name_and_action', subject: status - else %li.build .curve diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index 70101ccf806..5c9b6549b37 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -10,4 +10,4 @@ %ul - subject.each do |status| %li.dropdown-build - = render 'ci/status/graph_icon_with_name_and_action', subject: status + = render 'ci/status/icon_with_name_and_action', subject: status From 92b0f54ea222ce4c4437a50683c972bacc1fee06 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 13 Dec 2016 11:39:13 +0000 Subject: [PATCH 096/386] Fix graph stroke position --- app/assets/stylesheets/pages/pipelines.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 4a5b0b5922c..15da30dda2b 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -427,7 +427,7 @@ width: 21px; height: 25px; position: absolute; - top: -30px; + top: -32px; border-top: 2px solid $border-color; } From 0057ed1e69bc203d82fd3e8dfa6db7ea6a9b1de7 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 13 Dec 2016 17:59:21 +0200 Subject: [PATCH 097/386] BB importer: Fixed after code review[ci skip] --- lib/bitbucket/paginator.rb | 2 +- spec/lib/bitbucket/collection_spec.rb | 1 + spec/lib/bitbucket/connection_spec.rb | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb index b38cd99855c..135d0d55674 100644 --- a/lib/bitbucket/paginator.rb +++ b/lib/bitbucket/paginator.rb @@ -29,7 +29,7 @@ module Bitbucket end def fetch_next_page - parsed_response = connection.get(next_url, { pagelen: PAGE_LENGTH, sort: :created_on }) + parsed_response = connection.get(next_url, pagelen: PAGE_LENGTH, sort: :created_on) Page.new(parsed_response, type) end end diff --git a/spec/lib/bitbucket/collection_spec.rb b/spec/lib/bitbucket/collection_spec.rb index eeed61b0488..015a7f80e03 100644 --- a/spec/lib/bitbucket/collection_spec.rb +++ b/spec/lib/bitbucket/collection_spec.rb @@ -18,6 +18,7 @@ end describe Bitbucket::Collection do it "iterates paginator" do collection = described_class.new(TestPaginator.new) + expect(collection.to_a).to match(["result_1_page_1", "result_2_page_1", "result_1_page_2", "result_2_page_2"]) end end diff --git a/spec/lib/bitbucket/connection_spec.rb b/spec/lib/bitbucket/connection_spec.rb index 5242c6fac34..6be681a8b47 100644 --- a/spec/lib/bitbucket/connection_spec.rb +++ b/spec/lib/bitbucket/connection_spec.rb @@ -4,7 +4,9 @@ describe Bitbucket::Connection do describe '#get' do it 'calls OAuth2::AccessToken::get' do expect_any_instance_of(OAuth2::AccessToken).to receive(:get).and_return(double(parsed: true)) + connection = described_class.new({}) + connection.get('/users') end end @@ -12,6 +14,7 @@ describe Bitbucket::Connection do describe '#expired?' do it 'calls connection.expired?' do expect_any_instance_of(OAuth2::AccessToken).to receive(:expired?).and_return(true) + expect(described_class.new({}).expired?).to be_truthy end end @@ -19,7 +22,9 @@ describe Bitbucket::Connection do describe '#refresh!' do it 'calls connection.refresh!' do response = double(token: nil, expires_at: nil, expires_in: nil, refresh_token: nil) + expect_any_instance_of(OAuth2::AccessToken).to receive(:refresh!).and_return(response) + described_class.new({}).refresh! end end From e39f024029b46322c1bf24409fd5ce7bfcef2da5 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 13 Dec 2016 21:28:04 +0200 Subject: [PATCH 098/386] BB importer: Adding created_by only when used is not found[ci skip] --- lib/bitbucket/representation/base.rb | 4 ++++ lib/bitbucket/representation/comment.rb | 2 +- lib/bitbucket/representation/issue.rb | 2 +- lib/bitbucket/representation/pull_request.rb | 2 +- lib/bitbucket/representation/user.rb | 6 ++++- lib/gitlab/bitbucket_import/importer.rb | 23 +++++++++++++++----- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb index 94adaacc9b5..fd622d333da 100644 --- a/lib/bitbucket/representation/base.rb +++ b/lib/bitbucket/representation/base.rb @@ -5,6 +5,10 @@ module Bitbucket @raw = raw end + def user_representation(raw) + User.new(raw) + end + def self.decorate(entries) entries.map { |entry| new(entry)} end diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb index 94bc18cbfab..bc40f891cd3 100644 --- a/lib/bitbucket/representation/comment.rb +++ b/lib/bitbucket/representation/comment.rb @@ -2,7 +2,7 @@ module Bitbucket module Representation class Comment < Representation::Base def author - user.fetch('username', 'Anonymous') + user_representation(user) end def note diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index 6c8e9a4c244..90adfa3331a 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -12,7 +12,7 @@ module Bitbucket end def author - raw.dig('reporter', 'username') || 'Anonymous' + user_representation(raw.fetch('reporter', {})) end def description diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb index e7b1f99e9a6..96992003d24 100644 --- a/lib/bitbucket/representation/pull_request.rb +++ b/lib/bitbucket/representation/pull_request.rb @@ -2,7 +2,7 @@ module Bitbucket module Representation class PullRequest < Representation::Base def author - raw.fetch('author', {}).fetch('username', 'Anonymous') + user_representation(raw.fetch('author', {})) end def description diff --git a/lib/bitbucket/representation/user.rb b/lib/bitbucket/representation/user.rb index ba6b7667b49..6025a9f0653 100644 --- a/lib/bitbucket/representation/user.rb +++ b/lib/bitbucket/representation/user.rb @@ -2,7 +2,11 @@ module Bitbucket module Representation class User < Representation::Base def username - raw['username'] + raw['username'] || 'Anonymous' + end + + def uuid + raw['uuid'] end end end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index a0a17333185..519a109c0c8 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -24,15 +24,23 @@ module Gitlab private - def gitlab_user_id(project, bitbucket_id) - if bitbucket_id - user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s) + def gitlab_user_id(project, user) + if user.uuid + user = find_user_by_uuid(user.uuid) (user && user.id) || project.creator_id else project.creator_id end end + def find_user_by_uuid(uuid) + User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", uuid) + end + + def existing_gitlab_user?(user) + user.uuid && find_user_by_uuid(user.uuid) + end + def repo @repo ||= client.repo(project.import_source) end @@ -43,7 +51,8 @@ module Gitlab create_labels client.issues(repo).each do |issue| - description = @formatter.author_line(issue.author) + description = '' + description += @formatter.author_line(issue.author.username) unless existing_gitlab_user?(issue.author) description += issue.description label_name = issue.kind @@ -69,7 +78,8 @@ module Gitlab # we do this check. next unless comment.note.present? - note = @formatter.author_line(comment.author) + note = '' + note += @formatter.author_line(comment.author.username) unless existing_gitlab_user?(comment.author) note += comment.note issue.notes.create!( @@ -97,7 +107,8 @@ module Gitlab pull_requests.each do |pull_request| begin - description = @formatter.author_line(pull_request.author) + description = '' + description += @formatter.author_line(pull_request.author.username) unless existing_gitlab_user?(pull_request.author) description += pull_request.description merge_request = project.merge_requests.create( From f20ea1f5cb354db0afe18e4021e1d2fb439c2e06 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 14 Dec 2016 11:53:29 +0200 Subject: [PATCH 099/386] Fix BB authentication[ci skip] --- lib/omniauth/strategies/bitbucket.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/omniauth/strategies/bitbucket.rb b/lib/omniauth/strategies/bitbucket.rb index 475aad5970f..5a7d67c2390 100644 --- a/lib/omniauth/strategies/bitbucket.rb +++ b/lib/omniauth/strategies/bitbucket.rb @@ -11,10 +11,6 @@ module OmniAuth token_url: 'https://bitbucket.org/site/oauth2/access_token' } - def callback_url - full_host + script_name + callback_path - end - uid do raw_info['username'] end @@ -28,7 +24,7 @@ module OmniAuth end def raw_info - @raw_info ||= access_token.get('user').parsed + @raw_info ||= access_token.get('api/2.0/user').parsed end def primary_email @@ -37,7 +33,7 @@ module OmniAuth end def emails - email_response = access_token.get('user/emails').parsed + email_response = access_token.get('api/2.0/user/emails').parsed @emails ||= email_response && email_response['values'] || nil end end From 8f0cef0b6e5e950efdf3ebfe8f9f846095fff9d9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 14 Dec 2016 12:35:10 +0200 Subject: [PATCH 100/386] BB importer: Refactoring user importing logic[ci skip] --- lib/bitbucket/representation/base.rb | 4 ---- lib/bitbucket/representation/comment.rb | 2 +- lib/bitbucket/representation/issue.rb | 2 +- lib/bitbucket/representation/pull_request.rb | 2 +- lib/bitbucket/representation/user.rb | 6 +----- lib/gitlab/bitbucket_import/importer.rb | 20 ++++++++++---------- 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb index fd622d333da..94adaacc9b5 100644 --- a/lib/bitbucket/representation/base.rb +++ b/lib/bitbucket/representation/base.rb @@ -5,10 +5,6 @@ module Bitbucket @raw = raw end - def user_representation(raw) - User.new(raw) - end - def self.decorate(entries) entries.map { |entry| new(entry)} end diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb index bc40f891cd3..3c75e9368fa 100644 --- a/lib/bitbucket/representation/comment.rb +++ b/lib/bitbucket/representation/comment.rb @@ -2,7 +2,7 @@ module Bitbucket module Representation class Comment < Representation::Base def author - user_representation(user) + user['username'] end def note diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index 90adfa3331a..ffe8a65d839 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -12,7 +12,7 @@ module Bitbucket end def author - user_representation(raw.fetch('reporter', {})) + raw.dig('reporter', 'username') end def description diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb index 96992003d24..e37c9a62c0e 100644 --- a/lib/bitbucket/representation/pull_request.rb +++ b/lib/bitbucket/representation/pull_request.rb @@ -2,7 +2,7 @@ module Bitbucket module Representation class PullRequest < Representation::Base def author - user_representation(raw.fetch('author', {})) + raw.dig('author', 'username') end def description diff --git a/lib/bitbucket/representation/user.rb b/lib/bitbucket/representation/user.rb index 6025a9f0653..ba6b7667b49 100644 --- a/lib/bitbucket/representation/user.rb +++ b/lib/bitbucket/representation/user.rb @@ -2,11 +2,7 @@ module Bitbucket module Representation class User < Representation::Base def username - raw['username'] || 'Anonymous' - end - - def uuid - raw['uuid'] + raw['username'] end end end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 519a109c0c8..b6a0b122cdb 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -24,21 +24,21 @@ module Gitlab private - def gitlab_user_id(project, user) - if user.uuid - user = find_user_by_uuid(user.uuid) + def gitlab_user_id(project, username) + if username + user = find_user(username) (user && user.id) || project.creator_id else project.creator_id end end - def find_user_by_uuid(uuid) - User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", uuid) + def find_user(username) + User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username) end - def existing_gitlab_user?(user) - user.uuid && find_user_by_uuid(user.uuid) + def existing_gitlab_user?(username) + username && find_user(username) end def repo @@ -52,7 +52,7 @@ module Gitlab client.issues(repo).each do |issue| description = '' - description += @formatter.author_line(issue.author.username) unless existing_gitlab_user?(issue.author) + description += @formatter.author_line(issue.author) unless existing_gitlab_user?(issue.author) description += issue.description label_name = issue.kind @@ -79,7 +79,7 @@ module Gitlab next unless comment.note.present? note = '' - note += @formatter.author_line(comment.author.username) unless existing_gitlab_user?(comment.author) + note += @formatter.author_line(comment.author) unless existing_gitlab_user?(comment.author) note += comment.note issue.notes.create!( @@ -108,7 +108,7 @@ module Gitlab pull_requests.each do |pull_request| begin description = '' - description += @formatter.author_line(pull_request.author.username) unless existing_gitlab_user?(pull_request.author) + description += @formatter.author_line(pull_request.author) unless existing_gitlab_user?(pull_request.author) description += pull_request.description merge_request = project.merge_requests.create( From 5886030f08bd41cba22509987fd5b232ec7ec965 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 13 Dec 2016 10:48:47 +0100 Subject: [PATCH 101/386] Update grape to 0.18.0 --- Gemfile | 2 +- Gemfile.lock | 15 +++++++++------ changelogs/unreleased/gem-update-grape.yml | 4 ++++ 3 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/gem-update-grape.yml diff --git a/Gemfile b/Gemfile index 2cc7764e6b8..dec0bea2cb0 100644 --- a/Gemfile +++ b/Gemfile @@ -67,7 +67,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false gem 'github-linguist', '~> 4.7.0', require: 'linguist' # API -gem 'grape', '~> 0.15.0' +gem 'grape', '~> 0.18.0' gem 'grape-entity', '~> 0.6.0' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' diff --git a/Gemfile.lock b/Gemfile.lock index 3de1a7cbf26..4e5b79dd798 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -282,15 +282,15 @@ GEM json multi_json request_store (>= 1.0) - grape (0.15.0) + grape (0.18.0) activesupport builder hashie (>= 2.1.0) multi_json (>= 1.3.2) multi_xml (>= 0.5.2) + mustermann-grape (~> 0.4.0) rack (>= 1.3.0) rack-accept - rack-mount virtus (>= 1.0.0) grape-entity (0.6.0) activesupport @@ -385,6 +385,10 @@ GEM multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) + mustermann (0.4.0) + tool (~> 0.2) + mustermann-grape (0.4.0) + mustermann (= 0.4.0) mysql2 (0.3.20) net-ldap (0.12.1) net-ssh (3.0.1) @@ -489,14 +493,12 @@ GEM pry-rails (0.3.4) pry (>= 0.9.10) pyu-ruby-sasl (0.0.3.3) - rack (1.6.4) + rack (1.6.5) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.4.1) rack rack-cors (0.4.0) - rack-mount (0.8.3) - rack (>= 1.0.0) rack-oauth2 (1.2.3) activesupport (>= 2.3) attr_required (>= 0.0.5) @@ -722,6 +724,7 @@ GEM tilt (2.0.5) timecop (0.8.1) timfel-krb5-auth (0.8.3) + tool (0.2.3) truncato (0.7.8) htmlentities (~> 4.3.1) nokogiri (~> 1.6.1) @@ -840,7 +843,7 @@ DEPENDENCIES gollum-lib (~> 4.2) gollum-rugged_adapter (~> 0.4.2) gon (~> 6.1.0) - grape (~> 0.15.0) + grape (~> 0.18.0) grape-entity (~> 0.6.0) haml_lint (~> 0.18.2) hamlit (~> 2.6.1) diff --git a/changelogs/unreleased/gem-update-grape.yml b/changelogs/unreleased/gem-update-grape.yml new file mode 100644 index 00000000000..46b6702d9fd --- /dev/null +++ b/changelogs/unreleased/gem-update-grape.yml @@ -0,0 +1,4 @@ +--- +title: 'Gem update: Update grape to 0.18.0' +merge_request: +author: Robert Schilling From 6bbe2f118ee17ac8b1d43a77f4020c048c427b77 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 14 Dec 2016 15:18:30 +0200 Subject: [PATCH 102/386] BB importer: More advanced error handling --- lib/gitlab/bitbucket_import/importer.rb | 76 +++++++++++++++---------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index b6a0b122cdb..567f2b314aa 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -6,24 +6,34 @@ module Gitlab { title: 'proposal', color: '#69D100' }, { title: 'task', color: '#7F8C8D' }].freeze - attr_reader :project, :client + attr_reader :project, :client, :errors def initialize(project) @project = project @client = Bitbucket::Client.new(project.import_data.credentials) @formatter = Gitlab::ImportFormatter.new @labels = {} + @errors = [] end def execute import_issues import_pull_requests + handle_errors true end private + def handle_errors + return unless errors.any? + project.update_column(:import_error, { + message: 'The remote data could not be fully imported.', + errors: errors + }.to_json) + end + def gitlab_user_id(project, username) if username user = find_user(username) @@ -51,21 +61,25 @@ module Gitlab create_labels client.issues(repo).each do |issue| - description = '' - description += @formatter.author_line(issue.author) unless existing_gitlab_user?(issue.author) - description += issue.description + begin + description = '' + description += @formatter.author_line(issue.author) unless existing_gitlab_user?(issue.author) + description += issue.description - label_name = issue.kind + label_name = issue.kind - issue = project.issues.create( - iid: issue.iid, - title: issue.title, - description: description, - state: issue.state, - author_id: gitlab_user_id(project, issue.author), - created_at: issue.created_at, - updated_at: issue.updated_at - ) + issue = project.issues.create!( + iid: issue.iid, + title: issue.title, + description: description, + state: issue.state, + author_id: gitlab_user_id(project, issue.author), + created_at: issue.created_at, + updated_at: issue.updated_at + ) + rescue StandardError => e + errors << { type: :issue, iid: issue.iid, errors: e.message } + end issue.labels << @labels[label_name] @@ -82,18 +96,20 @@ module Gitlab note += @formatter.author_line(comment.author) unless existing_gitlab_user?(comment.author) note += comment.note - issue.notes.create!( - project: project, - note: note, - author_id: gitlab_user_id(project, comment.author), - created_at: comment.created_at, - updated_at: comment.updated_at - ) + begin + issue.notes.create!( + project: project, + note: note, + author_id: gitlab_user_id(project, comment.author), + created_at: comment.created_at, + updated_at: comment.updated_at + ) + rescue StandardError => e + errors << { type: :issue_comment, iid: issue.iid, errors: e.message } + end end end end - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Couldn't import record properly #{e.message}") end def create_labels @@ -129,8 +145,8 @@ module Gitlab ) import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? - rescue ActiveRecord::RecordInvalid - Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request #{e.message}") + rescue StandardError => e + errors << { type: :pull_request, iid: pull_request.iid, errors: e.message } end end end @@ -169,9 +185,8 @@ module Gitlab type: 'DiffNote') merge_request.notes.create!(attributes) - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid pull request comment #{e.message}") - nil + rescue StandardError => e + errors << { type: :pull_request, iid: comment.iid, errors: e.message } end end end @@ -192,9 +207,8 @@ module Gitlab pr_comments.each do |comment| begin merge_request.notes.create!(pull_request_comment_attributes(comment)) - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error("Bitbucket importer ERROR in #{project.path_with_namespace}: Invalid standalone pull request comment #{e.message}") - nil + rescue StandardError => e + errors << { type: :pull_request, iid: comment.iid, errors: e.message } end end end From 863146d42ed8fd44ab0c0f11c873fcdbbfcb28fc Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 14:16:28 +0000 Subject: [PATCH 103/386] Remove unused file --- app/views/ci/status/_icon_with_description.html.haml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 app/views/ci/status/_icon_with_description.html.haml diff --git a/app/views/ci/status/_icon_with_description.html.haml b/app/views/ci/status/_icon_with_description.html.haml deleted file mode 100644 index 34c923440d0..00000000000 --- a/app/views/ci/status/_icon_with_description.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- detailed_status = subject.detailed_status(current_user) -- details_path = detailed_status.details_path if detailed_status.has_details? -- klass = "ci-status ci-#{detailed_status}" - -- if details_path - = link_to details_path, class: klass do - = custom_icon(detailed_status.icon) - = detailed_status.text -- else - %span{ class: klass } - = custom_icon(detailed_status.icon) - = detailed_status.text From 4ba48d6b05f66dfccc829db588312109b8b015a1 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 14:17:02 +0000 Subject: [PATCH 104/386] Merge two partials into one. Rename it to match the other partials --- ...e_graph_icon_with_name_and_action.html.haml | 18 ++++++++++++++++++ app/views/ci/status/_icon_with_name.html.haml | 11 ----------- .../_icon_with_name_and_action.html.haml | 8 -------- app/views/projects/stage/_graph.html.haml | 2 +- .../projects/stage/_in_stage_group.html.haml | 2 +- 5 files changed, 20 insertions(+), 21 deletions(-) create mode 100644 app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml delete mode 100644 app/views/ci/status/_icon_with_name.html.haml delete mode 100644 app/views/ci/status/_icon_with_name_and_action.html.haml diff --git a/app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml b/app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml new file mode 100644 index 00000000000..12a55735559 --- /dev/null +++ b/app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml @@ -0,0 +1,18 @@ +- detailed_status = subject.detailed_status(current_user) +- details_path = detailed_status.details_path if detailed_status.has_details? +- klass = "ci-status-icon ci-status-icon-#{detailed_status}" + +- if details_path + = link_to details_path, class: klass, + data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do + %span{ class: klass }= custom_icon(detailed_status.icon) + .ci-status-text= subject.name +- else + %span{ class: klass }= custom_icon(detailed_status.icon) + .ci-status-text= subject.name + +- if detailed_status.has_action? + = link_to detailed_status.action_path, method: detailed_status.action_method, + title: "#{subject.name}: #{detailed_status.action_title}", class: 'ci-action-icon-container' do + %i.ci-action-icon-wrapper + = icon(detailed_status.action_icon, class: detailed_status.action_class) diff --git a/app/views/ci/status/_icon_with_name.html.haml b/app/views/ci/status/_icon_with_name.html.haml deleted file mode 100644 index 028e1fe9402..00000000000 --- a/app/views/ci/status/_icon_with_name.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -- detailed_status = subject.detailed_status(current_user) -- details_path = detailed_status.details_path if detailed_status.has_details? -- klass = "ci-status-icon ci-status-icon-#{detailed_status}" - -- if details_path - = link_to details_path, class: klass, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do - %span{ class: klass }= custom_icon(detailed_status.icon) - .ci-status-text= subject.name -- else - %span{ class: klass }= custom_icon(detailed_status.icon) - .ci-status-text= subject.name diff --git a/app/views/ci/status/_icon_with_name_and_action.html.haml b/app/views/ci/status/_icon_with_name_and_action.html.haml deleted file mode 100644 index 76db3b7f38a..00000000000 --- a/app/views/ci/status/_icon_with_name_and_action.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -= render "ci/status/icon_with_name", subject: subject - -- detailed_status = subject.detailed_status(current_user) -- if detailed_status.has_action? - = link_to detailed_status.action_path, method: detailed_status.action_method, - title: "#{subject.name}: #{detailed_status.action_title}", class: 'ci-action-icon-container' do - %i.ci-action-icon-wrapper - = icon(detailed_status.action_icon, class: detailed_status.action_class) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index 6d280468262..ff86d93f354 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -13,7 +13,7 @@ %li.build .curve .build-content - = render 'ci/status/icon_with_name_and_action', subject: status + = render 'ci/status/bagde_graph_icon_with_name_and_action', subject: status - else %li.build .curve diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index 5c9b6549b37..e4a7613998d 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -10,4 +10,4 @@ %ul - subject.each do |status| %li.dropdown-build - = render 'ci/status/icon_with_name_and_action', subject: status + = render 'ci/status/bagde_graph_icon_with_name_and_action', subject: status From 5b1a38564efdba1850d7195b01146c6b49ffb29a Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 14:19:18 +0000 Subject: [PATCH 105/386] Fix scss error --- app/assets/stylesheets/pages/pipelines.scss | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 23312259f73..26487b2acf9 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -665,14 +665,15 @@ color: $gl-text-color; } - .stage { - max-width: 100px; - width: 100px; - } + .stage { + max-width: 100px; + width: 100px; + } - .ci-status-icon svg { - height: 18px; - width: 18px; + .ci-status-icon svg { + height: 18px; + width: 18px; + } } } } From 268a201cce4da7225841b651336835876be43fc2 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 14:24:25 +0000 Subject: [PATCH 106/386] Rename file according to review --- ...ph_icon_with_name_and_action.html.haml => _graph_badge.haml} | 2 ++ app/views/projects/stage/_graph.html.haml | 2 +- app/views/projects/stage/_in_stage_group.html.haml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) rename app/views/ci/status/{_badge_graph_icon_with_name_and_action.html.haml => _graph_badge.haml} (91%) diff --git a/app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml b/app/views/ci/status/_graph_badge.haml similarity index 91% rename from app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml rename to app/views/ci/status/_graph_badge.haml index 12a55735559..7c6d36217f0 100644 --- a/app/views/ci/status/_badge_graph_icon_with_name_and_action.html.haml +++ b/app/views/ci/status/_graph_badge.haml @@ -1,3 +1,5 @@ +-# Renders the graph node with both the status icon, status name and action icon + - detailed_status = subject.detailed_status(current_user) - details_path = detailed_status.details_path if detailed_status.has_details? - klass = "ci-status-icon ci-status-icon-#{detailed_status}" diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index ff86d93f354..b70b574e687 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -13,7 +13,7 @@ %li.build .curve .build-content - = render 'ci/status/bagde_graph_icon_with_name_and_action', subject: status + = render 'ci/status/graph_badge', subject: status - else %li.build .curve diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index e4a7613998d..b03837d1211 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -10,4 +10,4 @@ %ul - subject.each do |status| %li.dropdown-build - = render 'ci/status/bagde_graph_icon_with_name_and_action', subject: status + = render 'ci/status/graph_badge', subject: status From 281237d7731cfa6669dd650fdfabfe612082d47f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 15:04:26 +0000 Subject: [PATCH 107/386] Changes after review Remove empty line --- app/models/ability.rb | 6 ------ app/views/ci/status/_graph_badge.haml | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index ce461caf686..fa8f8bc3a5f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,10 +1,4 @@ class Ability - module Allowable - def can?(user, action, subject) - Ability.allowed?(user, action, subject) - end - end - class << self # Given a list of users and a project this method returns the users that can # read the given project. diff --git a/app/views/ci/status/_graph_badge.haml b/app/views/ci/status/_graph_badge.haml index 7c6d36217f0..839b4334713 100644 --- a/app/views/ci/status/_graph_badge.haml +++ b/app/views/ci/status/_graph_badge.haml @@ -5,8 +5,7 @@ - klass = "ci-status-icon ci-status-icon-#{detailed_status}" - if details_path - = link_to details_path, class: klass, - data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do + = link_to details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do %span{ class: klass }= custom_icon(detailed_status.icon) .ci-status-text= subject.name - else From e138646aad4a836459999a399cdd9eca1e09847d Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 15:25:38 +0000 Subject: [PATCH 108/386] Fix broken tests --- spec/features/projects/pipelines/pipeline_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 7358931b9f0..5094fcf33e8 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -40,7 +40,7 @@ describe "Pipelines", feature: true, js: true do context 'pipeline graph' do it 'shows a running icon and a cancel action for the running build' do - page.within('.stage-column:nth-child(2) .build:first-child') do + page.within('.stage-column:nth-child(3) .build:first-child') do expect(page).to have_selector('.ci-status-icon-running') expect(page).to have_content('deploy') expect(page).to have_selector('.ci-action-icon-container .fa-ban') @@ -48,7 +48,7 @@ describe "Pipelines", feature: true, js: true do end it 'shows the success icon and a retry action for the successfull build' do - page.within('.stage-column:nth-child(3)') do + page.within('.stage-column:nth-child(2) .build:first-child') do expect(page).to have_selector('.ci-status-icon-success') expect(page).to have_content('build') expect(page).to have_selector('.ci-action-icon-container .fa-refresh') @@ -64,7 +64,7 @@ describe "Pipelines", feature: true, js: true do end it 'shows the skipped icon and a play action for the manual build' do - page.within('.stage-column:nth-child(2) .build:nth-child(2)') do + page.within('.stage-column:nth-child(3) .build:nth-child(2)') do expect(page).to have_selector('.ci-status-icon-skipped') expect(page).to have_content('manual') expect(page).to have_selector('.ci-action-icon-container .ci-play-icon') From e485b3f6ad3c220655e4aa909d93bca7a4ae6afc Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 15 Dec 2016 00:28:55 +0800 Subject: [PATCH 109/386] Give forbidden if project for the build was deleted Closes #25309 --- lib/ci/api/builds.rb | 19 ++++++------------- lib/ci/api/helpers.rb | 7 +++++-- spec/requests/ci/api/builds_spec.rb | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index ed87a2603e8..3c4cfccb19a 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -41,7 +41,7 @@ module Ci put ":id" do authenticate_runner! build = Ci::Build.where(runner_id: current_runner.id).running.find(params[:id]) - forbidden!('Build has been erased!') if build.erased? + authenticate_build!(build, verify_token: false) update_runner_info @@ -71,9 +71,7 @@ module Ci # PATCH /builds/:id/trace.txt patch ":id/trace.txt" do build = Ci::Build.find_by_id(params[:id]) - not_found! unless build - authenticate_build_token!(build) - forbidden!('Build has been erased!') if build.erased? + authenticate_build!(build) error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range') content_range = request.headers['Content-Range'] @@ -104,8 +102,7 @@ module Ci Gitlab::Workhorse.verify_api_request!(headers) not_allowed! unless Gitlab.config.artifacts.enabled build = Ci::Build.find_by_id(params[:id]) - not_found! unless build - authenticate_build_token!(build) + authenticate_build!(build) forbidden!('build is not running') unless build.running? if params[:filesize] @@ -142,10 +139,8 @@ module Ci require_gitlab_workhorse! not_allowed! unless Gitlab.config.artifacts.enabled build = Ci::Build.find_by_id(params[:id]) - not_found! unless build - authenticate_build_token!(build) + authenticate_build!(build) forbidden!('Build is not running!') unless build.running? - forbidden!('Build has been erased!') if build.erased? artifacts_upload_path = ArtifactUploader.artifacts_upload_path artifacts = uploaded_file(:file, artifacts_upload_path) @@ -176,8 +171,7 @@ module Ci # GET /builds/:id/artifacts get ":id/artifacts" do build = Ci::Build.find_by_id(params[:id]) - not_found! unless build - authenticate_build_token!(build) + authenticate_build!(build) artifacts_file = build.artifacts_file unless artifacts_file.file_storage? @@ -202,8 +196,7 @@ module Ci # DELETE /builds/:id/artifacts delete ":id/artifacts" do build = Ci::Build.find_by_id(params[:id]) - not_found! unless build - authenticate_build_token!(build) + authenticate_build!(build) build.erase_artifacts! end diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index e608f5f6cad..0202b3cf8a3 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -13,8 +13,11 @@ module Ci forbidden! unless current_runner end - def authenticate_build_token!(build) - forbidden! unless build_token_valid?(build) + def authenticate_build!(build, verify_token: true) + not_found! unless build + forbidden! if verify_token && !build_token_valid?(build) + forbidden!('Project has been deleted!') unless build.project + forbidden!('Build has been erased!') if build.erased? end def runner_registration_token_valid? diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 80652129928..d61a9afd12e 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -329,6 +329,25 @@ describe Ci::API::Builds do end end end + + context 'when project for the build has been deleted' do + let(:build) do + create(:ci_build, + :pending, + :trace, + runner_id: runner.id, + pipeline: pipeline) + end + + it 'responds with forbidden' do + expect(response.status).to eq 403 + end + + def initial_patch_the_trace + build.project.update(pending_delete: true) + super + end + end end context 'when Runner makes a force-patch' do From a0ba72ee28ff666e651d3a9cdef19a9d35c22616 Mon Sep 17 00:00:00 2001 From: Dimitrie Hoekstra Date: Wed, 14 Dec 2016 17:53:39 +0100 Subject: [PATCH 110/386] css changes @dimitrieh v1 --- .../stylesheets/framework/dropdowns.scss | 2 +- .../stylesheets/framework/variables.scss | 2 +- app/assets/stylesheets/pages/pipelines.scss | 27 ++++++++++++++++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index d5914b900e2..21df80c01f7 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -98,7 +98,7 @@ @extend .dropdown-toggle; padding-right: 20px; position: relative; - width: 160px; + width: 163px; text-overflow: ellipsis; overflow: hidden; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 03fbfa5f1bd..3ed19672ec1 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -578,4 +578,4 @@ Pipeline Graph */ $stage-hover-bg: #eaf3fc; $stage-hover-border: #d1e7fc; -$stage-badge-text: #d4d4d4; +$stage-badge-text: #e5e5e5; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 26487b2acf9..ac18c39dfc4 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -460,7 +460,7 @@ border-radius: 30px; background-color: $white-light; position: relative; - padding: 8px 10px 9px; + padding: 8px 4px 9px 10px; width: 186px; margin-bottom: 10px; @@ -605,7 +605,7 @@ float: right; color: $stage-badge-text; font-weight: 100; - font-size: 13px; + font-size: 15px; margin-top: 1px; margin-right: 2px; } @@ -629,6 +629,7 @@ ul { max-height: 245px; overflow: auto; + margin: 5px 0; li { padding-top: 2px; @@ -665,6 +666,18 @@ color: $gl-text-color; } + .ci-action-icon-container { + i { + width: 25px; + height: 25px; + + &:before{ + top: 1px; + left: 1px; + } + } + } + .stage { max-width: 100px; width: 100px; @@ -681,7 +694,7 @@ // Action Icons .ci-action-icon-container .ci-action-icon-wrapper { float: right; - margin-top: -1px; + margin-top: -4px; i { color: $stage-badge-text; @@ -690,6 +703,14 @@ padding: 5px 6px; font-size: 13px; background: $white-light; + height: 30px; + width: 30px; + + &:before { + position: relative; + top: 3px; + left: 3px; + } &:hover { color: $gl-text-color; From 98a12e535f612339f1af59c9d479b0cdab69df3a Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 17:18:00 +0000 Subject: [PATCH 111/386] Fix extension in file --- app/views/ci/status/{_graph_badge.haml => _graph_badge.html.haml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/views/ci/status/{_graph_badge.haml => _graph_badge.html.haml} (100%) diff --git a/app/views/ci/status/_graph_badge.haml b/app/views/ci/status/_graph_badge.html.haml similarity index 100% rename from app/views/ci/status/_graph_badge.haml rename to app/views/ci/status/_graph_badge.html.haml From acc4b73cb5e3edfed1fa25d461d36b82fe2aacb2 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 17:31:43 +0000 Subject: [PATCH 112/386] Remove duplicate color variable --- app/assets/stylesheets/framework/variables.scss | 1 - app/assets/stylesheets/pages/pipelines.scss | 11 +++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 3ed19672ec1..710b971615b 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -578,4 +578,3 @@ Pipeline Graph */ $stage-hover-bg: #eaf3fc; $stage-hover-border: #d1e7fc; -$stage-badge-text: #e5e5e5; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index ac18c39dfc4..0124940408a 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -603,10 +603,9 @@ .dropdown-counter-badge { float: right; - color: $stage-badge-text; + color: $border-color; font-weight: 100; font-size: 15px; - margin-top: 1px; margin-right: 2px; } @@ -687,6 +686,10 @@ height: 18px; width: 18px; } + + .ci-status-text { + max-width: 95px; + } } } } @@ -697,9 +700,9 @@ margin-top: -4px; i { - color: $stage-badge-text; + color: $border-color; border-radius: 100%; - border: 1px solid $stage-badge-text; + border: 1px solid $border-color; padding: 5px 6px; font-size: 13px; background: $white-light; From c756e62b08f0a639f5550d17339b2938c9c9e096 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 14 Dec 2016 20:19:26 +0200 Subject: [PATCH 113/386] BB importer: fix specs --- spec/lib/bitbucket/connection_spec.rb | 4 ++++ spec/lib/bitbucket/representation/comment_spec.rb | 2 +- spec/lib/bitbucket/representation/issue_spec.rb | 6 +++--- .../bitbucket/representation/pull_request_comment_spec.rb | 3 +-- spec/lib/bitbucket/representation/pull_request_spec.rb | 4 ++-- spec/lib/gitlab/bitbucket_import/importer_spec.rb | 8 ++++---- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/spec/lib/bitbucket/connection_spec.rb b/spec/lib/bitbucket/connection_spec.rb index 6be681a8b47..14faeb231a9 100644 --- a/spec/lib/bitbucket/connection_spec.rb +++ b/spec/lib/bitbucket/connection_spec.rb @@ -1,6 +1,10 @@ require 'spec_helper' describe Bitbucket::Connection do + before do + allow_any_instance_of(described_class).to receive(:provider).and_return(double(app_id: '', app_secret: '')) + end + describe '#get' do it 'calls OAuth2::AccessToken::get' do expect_any_instance_of(OAuth2::AccessToken).to receive(:get).and_return(double(parsed: true)) diff --git a/spec/lib/bitbucket/representation/comment_spec.rb b/spec/lib/bitbucket/representation/comment_spec.rb index 5864193cbfc..fec243a9f96 100644 --- a/spec/lib/bitbucket/representation/comment_spec.rb +++ b/spec/lib/bitbucket/representation/comment_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Bitbucket::Representation::Comment do describe '#author' do it { expect(described_class.new('user' => { 'username' => 'Ben' }).author).to eq('Ben') } - it { expect(described_class.new({}).author).to eq('Anonymous') } + it { expect(described_class.new({}).author).to be_nil } end describe '#note' do diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb index 56deae63bbc..e1f3419c77e 100644 --- a/spec/lib/bitbucket/representation/issue_spec.rb +++ b/spec/lib/bitbucket/representation/issue_spec.rb @@ -10,12 +10,12 @@ describe Bitbucket::Representation::Issue do end describe '#author' do - it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' }}).author).to eq('Ben') } - it { expect(described_class.new({}).author).to eq('Anonymous') } + it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' } }).author).to eq('Ben') } + it { expect(described_class.new({}).author).to be_nil } end describe '#description' do - it { expect(described_class.new({ 'content' => { 'raw' => 'Text' }}).description).to eq('Text') } + it { expect(described_class.new({ 'content' => { 'raw' => 'Text' } }).description).to eq('Text') } it { expect(described_class.new({}).description).to be_nil } end diff --git a/spec/lib/bitbucket/representation/pull_request_comment_spec.rb b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb index 8377f0540cd..673dcf22ce8 100644 --- a/spec/lib/bitbucket/representation/pull_request_comment_spec.rb +++ b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb @@ -17,9 +17,8 @@ describe Bitbucket::Representation::PullRequestComment do it { expect(described_class.new('inline' => { 'to' => 3 }).new_pos).to eq(3) } end - describe '#parent_id' do - it { expect(described_class.new({ 'parent' => { 'id' => 2 }}).parent_id).to eq(2) } + it { expect(described_class.new({ 'parent' => { 'id' => 2 } }).parent_id).to eq(2) } it { expect(described_class.new({}).parent_id).to be_nil } end diff --git a/spec/lib/bitbucket/representation/pull_request_spec.rb b/spec/lib/bitbucket/representation/pull_request_spec.rb index 661422efddf..30453528be4 100644 --- a/spec/lib/bitbucket/representation/pull_request_spec.rb +++ b/spec/lib/bitbucket/representation/pull_request_spec.rb @@ -6,8 +6,8 @@ describe Bitbucket::Representation::PullRequest do end describe '#author' do - it { expect(described_class.new({ 'author' => { 'username' => 'Ben' }}).author).to eq('Ben') } - it { expect(described_class.new({}).author).to eq('Anonymous') } + it { expect(described_class.new({ 'author' => { 'username' => 'Ben' } }).author).to eq('Ben') } + it { expect(described_class.new({}).author).to be_nil } end describe '#description' do diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 353312675d6..53f3c73ade4 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -84,10 +84,10 @@ describe Gitlab::BitbucketImport::Importer, lib: true do body: issues_statuses_sample_data.to_json) stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on"). - with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Bearer', 'User-Agent'=>'Faraday v0.9.2'}). - to_return(:status => 200, - :body => "", - :headers => {}) + with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }). + to_return(status: 200, + body: "", + headers: {}) sample_issues_statuses.each_with_index do |issue, index| stub_request( From e998d6fb59e331cf000fa1c7463f79610b87be7c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 14 Dec 2016 20:06:24 +0100 Subject: [PATCH 114/386] Simplify graph status badge partial and require locals --- app/views/ci/status/_graph_badge.html.haml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml index 839b4334713..a7e8544e7d4 100644 --- a/app/views/ci/status/_graph_badge.html.haml +++ b/app/views/ci/status/_graph_badge.html.haml @@ -1,19 +1,19 @@ -# Renders the graph node with both the status icon, status name and action icon -- detailed_status = subject.detailed_status(current_user) -- details_path = detailed_status.details_path if detailed_status.has_details? -- klass = "ci-status-icon ci-status-icon-#{detailed_status}" +- subject = local_assigns.fetch(:subject) +- status = subject.detailed_status(current_user) +- klass = "ci-status-icon ci-status-icon-#{status}" -- if details_path - = link_to details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{detailed_status}" } do - %span{ class: klass }= custom_icon(detailed_status.icon) +- if status.has_details? + = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status}" } do + %span{ class: klass }= custom_icon(status.icon) .ci-status-text= subject.name - else - %span{ class: klass }= custom_icon(detailed_status.icon) + %span{ class: klass }= custom_icon(status.icon) .ci-status-text= subject.name -- if detailed_status.has_action? - = link_to detailed_status.action_path, method: detailed_status.action_method, - title: "#{subject.name}: #{detailed_status.action_title}", class: 'ci-action-icon-container' do +- if status.has_action? + = link_to status.action_path, method: status.action_method, + title: "#{subject.name}: #{status.action_title}", class: 'ci-action-icon-container' do %i.ci-action-icon-wrapper - = icon(detailed_status.action_icon, class: detailed_status.action_class) + = icon(status.action_icon, class: status.action_class) From 2044f43dc2564f99178b674ede31159a94b35805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Wed, 14 Dec 2016 17:00:57 -0300 Subject: [PATCH 115/386] Update CHANGELOG.md for 8.14.5 [ci skip] --- CHANGELOG.md | 15 +++++++++++++++ ...-project-and-leave-group-should-be-buttons.yml | 5 ----- ...s-settings-hidden-when-builds-are-disabled.yml | 4 ---- changelogs/unreleased/fix-milestone-summary.yml | 4 ---- .../group-members-in-project-members-view.yml | 4 ---- changelogs/unreleased/issue_24020.yml | 4 ---- changelogs/unreleased/issue_25030.yml | 4 ---- changelogs/unreleased/timeago-perf-fix.yml | 4 ---- changelogs/unreleased/unescape-relative-path.yml | 4 ---- 9 files changed, 15 insertions(+), 33 deletions(-) delete mode 100644 changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml delete mode 100644 changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml delete mode 100644 changelogs/unreleased/fix-milestone-summary.yml delete mode 100644 changelogs/unreleased/group-members-in-project-members-view.yml delete mode 100644 changelogs/unreleased/issue_24020.yml delete mode 100644 changelogs/unreleased/issue_25030.yml delete mode 100644 changelogs/unreleased/timeago-perf-fix.yml delete mode 100644 changelogs/unreleased/unescape-relative-path.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index fb13db4dd1c..f1d2f09e34f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.14.5 (2016-12-14) + +- Moved Leave Project and Leave Group buttons to access_request_buttons from the settings dropdown. !7600 +- fix display hook error message. !7775 (basyura) +- Remove wrong '.builds-feature' class from the MR settings fieldset. !7930 +- Avoid escaping relative links in Markdown twice. !7940 (winniehell) +- API: Memoize the current_user so that sudo can work properly. !8017 +- Displays milestone remaining days only when it's present. +- Allow branch names with dots on API endpoint. +- Issue#visible_to_user moved to IssuesFinder to prevent accidental use. +- Shows group members in project members list. +- Encode input when migrating ProcessCommitWorker jobs to prevent migration errors. +- Fixed timeago re-rendering every timeago. +- Fix missing Note access checks by moving Note#search to updated NoteFinder. + ## 8.14.4 (2016-12-08) - Fix diff view permalink highlighting. !7090 diff --git a/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml b/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml deleted file mode 100644 index 99dbe4a32a0..00000000000 --- a/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Moved Leave Project and Leave Group buttons to access_request_buttons from - the settings dropdown -merge_request: 7600 -author: diff --git a/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml b/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml deleted file mode 100644 index a7576e2cbdb..00000000000 --- a/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove wrong '.builds-feature' class from the MR settings fieldset -merge_request: 7930 -author: diff --git a/changelogs/unreleased/fix-milestone-summary.yml b/changelogs/unreleased/fix-milestone-summary.yml deleted file mode 100644 index 3045a15054c..00000000000 --- a/changelogs/unreleased/fix-milestone-summary.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Displays milestone remaining days only when it's present -merge_request: -author: diff --git a/changelogs/unreleased/group-members-in-project-members-view.yml b/changelogs/unreleased/group-members-in-project-members-view.yml deleted file mode 100644 index 415e2b6b1e2..00000000000 --- a/changelogs/unreleased/group-members-in-project-members-view.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Shows group members in project members list -merge_request: -author: diff --git a/changelogs/unreleased/issue_24020.yml b/changelogs/unreleased/issue_24020.yml deleted file mode 100644 index 87310b75296..00000000000 --- a/changelogs/unreleased/issue_24020.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "fix display hook error message" -merge_request: 7775 -author: basyura diff --git a/changelogs/unreleased/issue_25030.yml b/changelogs/unreleased/issue_25030.yml deleted file mode 100644 index e18b8d6a79b..00000000000 --- a/changelogs/unreleased/issue_25030.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow branch names with dots on API endpoint -merge_request: -author: diff --git a/changelogs/unreleased/timeago-perf-fix.yml b/changelogs/unreleased/timeago-perf-fix.yml deleted file mode 100644 index 265e7db29a9..00000000000 --- a/changelogs/unreleased/timeago-perf-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed timeago re-rendering every timeago -merge_request: -author: diff --git a/changelogs/unreleased/unescape-relative-path.yml b/changelogs/unreleased/unescape-relative-path.yml deleted file mode 100644 index 755b0379a16..00000000000 --- a/changelogs/unreleased/unescape-relative-path.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Avoid escaping relative links in Markdown twice -merge_request: 7940 -author: winniehell From 1df85aed4baf85531d6a1e47a518ef3f04c14908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Wed, 14 Dec 2016 17:04:12 -0300 Subject: [PATCH 116/386] Update CHANGELOG.md for 8.13.10 [ci skip] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d2f09e34f..786b0128aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -279,6 +279,13 @@ entry. - Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix 404 when visit /projects page +## 8.13.10 (2016-12-14) + +- API: Memoize the current_user so that sudo can work properly. !8017 +- Filter `authentication_token`, `incoming_email_token` and `runners_token` parameters. +- Issue#visible_to_user moved to IssuesFinder to prevent accidental use. +- Fix missing Note access checks by moving Note#search to updated NoteFinder. + ## 8.13.9 (2016-12-08) - Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615 From bdae2df31065365283720c1e29d9e5e5d495bcf3 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 23:27:26 +0000 Subject: [PATCH 117/386] Fix firefox bug --- app/assets/stylesheets/pages/pipelines.scss | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 0124940408a..ee08d9d3ca0 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -463,6 +463,7 @@ padding: 8px 4px 9px 10px; width: 186px; margin-bottom: 10px; + white-space: normal; &:hover { background-color: $stage-hover-bg; @@ -586,7 +587,7 @@ border: none; padding: 0; color: $gl-text-color-light; - flex-grow: 1; + white-space: normal; &:focus { outline: none; @@ -603,6 +604,7 @@ .dropdown-counter-badge { float: right; + clear: right; color: $border-color; font-weight: 100; font-size: 15px; @@ -651,7 +653,10 @@ padding: 0; font-size: 11px; float: right; - margin-top: 5px; + clear: right; + margin-top: 3px; + display: inline-block; + position: relative; i { font-size: 11px; From 63e5e009b3fb7c85a8471d2e15951bd5d04870b5 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 23:33:25 +0000 Subject: [PATCH 118/386] Changes after review --- app/assets/stylesheets/pages/pipelines.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index ee08d9d3ca0..e879c2495fb 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -484,6 +484,10 @@ } } + .ci-status-icon { + position: relative; + top: 1px; + } .ci-status-icon svg { height: 20px; width: 20px; @@ -588,6 +592,7 @@ padding: 0; color: $gl-text-color-light; white-space: normal; + overflow: visible; &:focus { outline: none; From 4564492a84b91b820ef0bcc69a84563ebeed9cb2 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 00:04:22 +0000 Subject: [PATCH 119/386] Fix dropdown hover --- app/assets/stylesheets/pages/pipelines.scss | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index e879c2495fb..4569b91383f 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -488,6 +488,7 @@ position: relative; top: 1px; } + .ci-status-icon svg { height: 20px; width: 20px; @@ -638,8 +639,11 @@ margin: 5px 0; li { - padding-top: 2px; margin: 0 5px; + padding-left: 0; + padding-bottom: 0; + margin-bottom: 0; + line-height: 1.2; } li:first-child { @@ -658,8 +662,7 @@ padding: 0; font-size: 11px; float: right; - clear: right; - margin-top: 3px; + margin-top: 4px; display: inline-block; position: relative; @@ -699,6 +702,9 @@ .ci-status-text { max-width: 95px; + padding-bottom: 3px; + position: relative; + top: 3px; } } } From 05bc71c856c735fa364e15db62ba5d40cfd7c5ef Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 14 Dec 2016 22:38:40 +0800 Subject: [PATCH 120/386] Convert CI YAML variables keys into strings So that this would be more consistent with the other variables, which all of them are string based. Closes #25554 --- app/models/ci/build.rb | 2 +- changelogs/unreleased/fix-yaml-variables.yml | 4 +++ lib/ci/gitlab_ci_yaml_processor.rb | 2 +- lib/gitlab/serialize/yaml_variables.rb | 30 +++++++++++++++++++ spec/factories/ci/builds.rb | 2 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 24 +++++++-------- .../gitlab/serialize/yaml_variables_spec.rb | 19 ++++++++++++ spec/models/build_spec.rb | 2 +- 8 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 changelogs/unreleased/fix-yaml-variables.yml create mode 100644 lib/gitlab/serialize/yaml_variables.rb create mode 100644 spec/lib/gitlab/serialize/yaml_variables_spec.rb diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index e7cf606a7ae..99f3f4711aa 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -10,7 +10,7 @@ module Ci has_many :deployments, as: :deployable serialize :options - serialize :yaml_variables + serialize :yaml_variables, Gitlab::Serialize::YamlVariables validates :coverage, numericality: true, allow_blank: true validates_presence_of :ref diff --git a/changelogs/unreleased/fix-yaml-variables.yml b/changelogs/unreleased/fix-yaml-variables.yml new file mode 100644 index 00000000000..3abff1e3b08 --- /dev/null +++ b/changelogs/unreleased/fix-yaml-variables.yml @@ -0,0 +1,4 @@ +--- +title: Convert CI YAML variables keys into strings +merge_request: 8088 +author: diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index fef652cb975..8806a506ffa 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -118,7 +118,7 @@ module Ci .merge(job_variables(name)) variables.map do |key, value| - { key: key, value: value, public: true } + { key: key.to_s, value: value.to_s, public: true } end end diff --git a/lib/gitlab/serialize/yaml_variables.rb b/lib/gitlab/serialize/yaml_variables.rb new file mode 100644 index 00000000000..ca44acbd906 --- /dev/null +++ b/lib/gitlab/serialize/yaml_variables.rb @@ -0,0 +1,30 @@ + +module Gitlab + module Serialize + # This serializer could make sure our YAML variables' keys and values + # are always strings. This is more for legacy build data because + # from now on we convert them into strings before saving to database. + module YamlVariables + extend self + + def load(string) + return unless string + + YAML.load(string). + map(&YamlVariables.method(:convert_key_value_to_string)) + end + + def dump(object) + YAML.dump(object) + end + + private + + def convert_key_value_to_string(variable) + variable[:key] = variable[:key].to_s + variable[:value] = variable[:value].to_s + variable + end + end + end +end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 62466c06194..0397d5d4001 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -22,7 +22,7 @@ FactoryGirl.define do yaml_variables do [ - { key: :DB_NAME, value: 'postgres', public: true } + { key: 'DB_NAME', value: 'postgres', public: true } ] end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index ff5dcc06ab3..62d68721574 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -483,7 +483,7 @@ module Ci context 'when global variables are defined' do let(:variables) do - { VAR1: 'value1', VAR2: 'value2' } + { 'VAR1' => 'value1', 'VAR2' => 'value2' } end let(:config) do { @@ -495,18 +495,18 @@ module Ci it 'returns global variables' do expect(subject).to contain_exactly( - { key: :VAR1, value: 'value1', public: true }, - { key: :VAR2, value: 'value2', public: true } + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true } ) end end context 'when job and global variables are defined' do let(:global_variables) do - { VAR1: 'global1', VAR3: 'global3' } + { 'VAR1' => 'global1', 'VAR3' => 'global3' } end let(:job_variables) do - { VAR1: 'value1', VAR2: 'value2' } + { 'VAR1' => 'value1', 'VAR2' => 'value2' } end let(:config) do { @@ -518,9 +518,9 @@ module Ci it 'returns all unique variables' do expect(subject).to contain_exactly( - { key: :VAR3, value: 'global3', public: true }, - { key: :VAR1, value: 'value1', public: true }, - { key: :VAR2, value: 'value2', public: true } + { key: 'VAR3', value: 'global3', public: true }, + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true } ) end end @@ -535,13 +535,13 @@ module Ci context 'when syntax is correct' do let(:variables) do - { VAR1: 'value1', VAR2: 'value2' } + { 'VAR1' => 'value1', 'VAR2' => 'value2' } end it 'returns job variables' do expect(subject).to contain_exactly( - { key: :VAR1, value: 'value1', public: true }, - { key: :VAR2, value: 'value2', public: true } + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true } ) end end @@ -549,7 +549,7 @@ module Ci context 'when syntax is incorrect' do context 'when variables defined but invalid' do let(:variables) do - [ :VAR1, 'value1', :VAR2, 'value2' ] + [ 'VAR1', 'value1', 'VAR2', 'value2' ] end it 'raises error' do diff --git a/spec/lib/gitlab/serialize/yaml_variables_spec.rb b/spec/lib/gitlab/serialize/yaml_variables_spec.rb new file mode 100644 index 00000000000..6d74f8c44d6 --- /dev/null +++ b/spec/lib/gitlab/serialize/yaml_variables_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Gitlab::Serialize::YamlVariables do + subject do + Gitlab::Serialize::YamlVariables.load( + Gitlab::Serialize::YamlVariables.dump(object)) + end + + let(:object) do + [{ key: :key, value: 'value', public: true }, + { key: 'wee', value: 1, public: false }] + end + + it 'converts key and values into strings' do + is_expected.to eq([ + { key: 'key', value: 'value', public: true }, + { key: 'wee', value: '1', public: false }]) + end +end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 7f39aff7639..13928695ccb 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -403,7 +403,7 @@ describe Ci::Build, models: true do }) end let(:variables) do - [{ key: :KEY, value: 'value', public: true }] + [{ key: 'KEY', value: 'value', public: true }] end it { is_expected.to eq(predefined_variables + variables) } From 48fed57b05b0394ca824bed90c670505b3a8a273 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 10:55:34 +0000 Subject: [PATCH 121/386] Fix scss linter error --- app/assets/stylesheets/pages/pipelines.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 4569b91383f..3054f0655db 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -683,7 +683,7 @@ width: 25px; height: 25px; - &:before{ + &::before { top: 1px; left: 1px; } @@ -725,7 +725,7 @@ height: 30px; width: 30px; - &:before { + &::before { position: relative; top: 3px; left: 3px; From b7f297ed8ab9c6bfc6dc5a3c58c9124648b0f60f Mon Sep 17 00:00:00 2001 From: winniehell Date: Tue, 22 Nov 2016 02:14:59 +0100 Subject: [PATCH 122/386] Replace static fixture for abuse_reports_spec (!7644) --- .../unreleased/abuse_report-fixture.yml | 4 ++ spec/javascripts/abuse_reports_spec.js.es6 | 38 ++++++++++--------- .../fixtures/abuse_reports.html.haml | 16 -------- spec/javascripts/fixtures/abuse_reports.rb | 27 +++++++++++++ 4 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 changelogs/unreleased/abuse_report-fixture.yml delete mode 100644 spec/javascripts/fixtures/abuse_reports.html.haml create mode 100644 spec/javascripts/fixtures/abuse_reports.rb diff --git a/changelogs/unreleased/abuse_report-fixture.yml b/changelogs/unreleased/abuse_report-fixture.yml new file mode 100644 index 00000000000..47478a2048b --- /dev/null +++ b/changelogs/unreleased/abuse_report-fixture.yml @@ -0,0 +1,4 @@ +--- +title: Replace static fixture for abuse_reports_spec +merge_request: 7644 +author: winniehell diff --git a/spec/javascripts/abuse_reports_spec.js.es6 b/spec/javascripts/abuse_reports_spec.js.es6 index 9e94c9d1d74..49e56249565 100644 --- a/spec/javascripts/abuse_reports_spec.js.es6 +++ b/spec/javascripts/abuse_reports_spec.js.es6 @@ -1,42 +1,44 @@ -/* eslint-disable space-before-function-paren, no-new, padded-blocks */ - +/*= require lib/utils/text_utility */ /*= require abuse_reports */ -/*= require jquery */ ((global) => { - const FIXTURE = 'abuse_reports.html'; - const MAX_MESSAGE_LENGTH = 500; + describe('Abuse Reports', () => { + const FIXTURE = 'abuse_reports/abuse_reports_list.html.raw'; + const MAX_MESSAGE_LENGTH = 500; - function assertMaxLength($message) { - expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH); - } + let messages; + + const assertMaxLength = $message => expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH); + const findMessage = searchText => messages.filter( + (index, element) => element.innerText.indexOf(searchText) > -1, + ).first(); - describe('Abuse Reports', function() { fixture.preload(FIXTURE); - beforeEach(function() { + beforeEach(function () { fixture.load(FIXTURE); - new global.AbuseReports(); + this.abuseReports = new global.AbuseReports(); + messages = $('.abuse-reports .message'); }); - it('should truncate long messages', function() { - const $longMessage = $('#long'); + + it('should truncate long messages', () => { + const $longMessage = findMessage('LONG MESSAGE'); expect($longMessage.data('original-message')).toEqual(jasmine.anything()); assertMaxLength($longMessage); }); - it('should not truncate short messages', function() { - const $shortMessage = $('#short'); + it('should not truncate short messages', () => { + const $shortMessage = findMessage('SHORT MESSAGE'); expect($shortMessage.data('original-message')).not.toEqual(jasmine.anything()); }); - it('should allow clicking a truncated message to expand and collapse the full message', function() { - const $longMessage = $('#long'); + it('should allow clicking a truncated message to expand and collapse the full message', () => { + const $longMessage = findMessage('LONG MESSAGE'); $longMessage.click(); expect($longMessage.data('original-message').length).toEqual($longMessage.text().length); $longMessage.click(); assertMaxLength($longMessage); }); }); - })(window.gl); diff --git a/spec/javascripts/fixtures/abuse_reports.html.haml b/spec/javascripts/fixtures/abuse_reports.html.haml deleted file mode 100644 index 2ec302abcb7..00000000000 --- a/spec/javascripts/fixtures/abuse_reports.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -.abuse-reports - .message#long - Cat ipsum dolor sit amet, hide head under blanket so no one can see. - Gate keepers of hell eat and than sleep on your face but hunt by meowing - loudly at 5am next to human slave food dispenser cats go for world - domination or chase laser, yet poop on grasses chirp at birds. Cat is love, - cat is life chase after silly colored fish toys around the house climb a - tree, wait for a fireman jump to fireman then scratch his face fall asleep - on the washing machine lies down always hungry so caticus cuteicus. Sit on - human. Spot something, big eyes, big eyes, crouch, shake butt, prepare to - pounce sleep in the bathroom sink hiss at vacuum cleaner hide head under - blanket so no one can see throwup on your pillow. - .message#short - Cat ipsum dolor sit amet, groom yourself 4 hours - checked, have your - beauty sleep 18 hours - checked, be fabulous for the rest of the day - - checked! for shake treat bag. diff --git a/spec/javascripts/fixtures/abuse_reports.rb b/spec/javascripts/fixtures/abuse_reports.rb new file mode 100644 index 00000000000..de673f94d72 --- /dev/null +++ b/spec/javascripts/fixtures/abuse_reports.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Admin::AbuseReportsController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:admin) { create(:admin) } + let!(:abuse_report) { create(:abuse_report) } + let!(:abuse_report_with_short_message) { create(:abuse_report, message: 'SHORT MESSAGE') } + let!(:abuse_report_with_long_message) { create(:abuse_report, message: "LONG MESSAGE\n" * 50) } + + render_views + + before(:all) do + clean_frontend_fixtures('abuse_reports/') + end + + before(:each) do + sign_in(admin) + end + + it 'abuse_reports/abuse_reports_list.html.raw' do |example| + get :index + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end +end From 0ddc5f667e60db6f7f6021dec55a1c9feab852ab Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 15 Dec 2016 12:40:33 +0100 Subject: [PATCH 123/386] add new runner script attempts docs and update .gitlab-ci.yml --- .gitlab-ci.yml | 1 + doc/ci/variables/README.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e522d47d19d..b256e8a2a5f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,6 +15,7 @@ variables: USE_BUNDLE_INSTALL: "true" GIT_DEPTH: "20" PHANTOMJS_VERSION: "2.1.1" + GET_SOURCES_ATTEMPTS: "3" before_script: - source ./scripts/prepare_build.sh diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index e0ff9756868..a3ce44d72c6 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -58,6 +58,9 @@ version of Runner required. | **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | | **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | | **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | +| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a build | +| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a build | +| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a build | | **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the build | | **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the build | From 03a95c7070938e0fde67f2def334c08cf72d9d29 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 11:48:05 +0000 Subject: [PATCH 124/386] Fix tests --- .../projects/pipelines/pipeline_spec.rb | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 5094fcf33e8..80a596d34c9 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -40,42 +40,61 @@ describe "Pipelines", feature: true, js: true do context 'pipeline graph' do it 'shows a running icon and a cancel action for the running build' do - page.within('.stage-column:nth-child(3) .build:first-child') do + title = "#{@running.name} - #{@running.status}" + + page.within("a[data-title='#{title}']") do expect(page).to have_selector('.ci-status-icon-running') expect(page).to have_content('deploy') + end + + page.within("a[data-title='#{title}'] + .ci-action-icon-container") do expect(page).to have_selector('.ci-action-icon-container .fa-ban') end + end it 'shows the success icon and a retry action for the successfull build' do - page.within('.stage-column:nth-child(2) .build:first-child') do + title = "#{@success.name} - #{@success.status}" + + page.within("a[data-title='#{title}']") do expect(page).to have_selector('.ci-status-icon-success') expect(page).to have_content('build') + end + + page.within("a[data-title='#{title}'] + .ci-action-icon-container") do expect(page).to have_selector('.ci-action-icon-container .fa-refresh') end end it 'shows the failed icon and a retry action for the failed build' do - page.within('.stage-column:first-child .build') do + title = "#{@failed.name} - #{@failed.status}" + + page.within("a[data-title='#{title}']") do expect(page).to have_selector('.ci-status-icon-failed') expect(page).to have_content('test') + end + + page.within("a[data-title='#{title}'] + .ci-action-icon-container") do expect(page).to have_selector('.ci-action-icon-container .fa-refresh') end end it 'shows the skipped icon and a play action for the manual build' do - page.within('.stage-column:nth-child(3) .build:nth-child(2)') do + title = "#{@manual.name} - #{@manual.status}" + + page.within("a[data-title='#{title}']") do expect(page).to have_selector('.ci-status-icon-skipped') expect(page).to have_content('manual') - expect(page).to have_selector('.ci-action-icon-container .ci-play-icon') + end + + page.within("a[data-title='#{title}'] + .ci-action-icon-container") do + expect(page).to have_selector('.ci-action-icon-container .fa-play') end end - it 'shows the success icon for the generic comit status build' do - page.within('.stage-column:nth-child(4) .build') do - expect(page).to have_selector('.ci-status-icon-success') - expect(page).to have_content('jenkins') - end + it 'shows the success icon and the generic comit status build' do + expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_content('jenkins') end end From 26628fb91a89bbe4998633eec00d2bd76cfb95c0 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 15 Dec 2016 14:19:28 +0200 Subject: [PATCH 125/386] BB importer: Fixed bug with putting expired token to a project.clone_url --- app/controllers/import/bitbucket_controller.rb | 3 +++ lib/bitbucket/client.rb | 4 ++-- lib/bitbucket/connection.rb | 4 ++-- lib/gitlab/bitbucket_import/project_creator.rb | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index b9cc6556140..8e42cdf415f 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -49,6 +49,9 @@ class Import::BitbucketController < Import::BaseController namespace = find_or_create_namespace(@target_namespace, current_user) if current_user.can?(:create_projects, namespace) + # The token in a session can be expired, we need to get most recent one because + # Bitbucket::Connection class refreshes it. + session[:bitbucket_token] = bitbucket_client.connection.token @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, namespace, current_user, credentials).execute else render 'unauthorized' diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb index 5c2ef2a4509..f8ee7e0f9ae 100644 --- a/lib/bitbucket/client.rb +++ b/lib/bitbucket/client.rb @@ -1,5 +1,7 @@ module Bitbucket class Client + attr_reader :connection + def initialize(options = {}) @connection = Connection.new(options) end @@ -48,8 +50,6 @@ module Bitbucket private - attr_reader :connection - def get_collection(path, type) paginator = Paginator.new(connection, path, type) Collection.new(paginator) diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index c150a20761e..7e55cf4deab 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -4,6 +4,8 @@ module Bitbucket DEFAULT_BASE_URI = 'https://api.bitbucket.org/' DEFAULT_QUERY = {} + attr_reader :expires_at, :expires_in, :refresh_token, :token + def initialize(options = {}) @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) @base_uri = options.fetch(:base_uri, DEFAULT_BASE_URI) @@ -38,8 +40,6 @@ module Bitbucket private - attr_reader :expires_at, :expires_in, :refresh_token, :token - def client @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options) end diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index b34be272af3..eb03882ab26 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -21,7 +21,7 @@ module Gitlab visibility_level: repo.visibility_level, import_type: 'bitbucket', import_source: repo.full_name, - import_url: repo.clone_url(@session_data[:token]), + import_url: repo.clone_url(session_data[:token]), import_data: { credentials: session_data } ).execute end From 141faaacf9119ce5d765efe73c6509030ba078cd Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 25 Nov 2016 17:36:37 -0200 Subject: [PATCH 126/386] Mattermost Notifications Service --- app/models/project.rb | 1 + .../base_message.rb | 2 +- .../build_message.rb | 2 +- .../issue_message.rb | 2 +- .../merge_message.rb | 2 +- .../note_message.rb | 2 +- .../pipeline_message.rb | 2 +- .../push_message.rb | 2 +- .../wiki_page_message.rb | 2 +- app/models/project_services/chat_service.rb | 141 +++++++- .../project_services/mattermost_service.rb | 41 +++ .../mattermost_slash_commands_service.rb | 12 +- app/models/project_services/slack_service.rb | 174 +--------- app/models/service.rb | 1 + changelogs/unreleased/issue_22269.yml | 4 + doc/api/services.md | 38 +- .../img/mattermost_configuration.png | Bin 0 -> 73502 bytes doc/project_services/mattermost.md | 45 +++ doc/project_services/project_services.md | 3 +- doc/project_services/slack.md | 4 +- spec/lib/gitlab/import_export/all_models.yml | 1 + .../build_message_spec.rb | 4 +- .../issue_message_spec.rb | 4 +- .../merge_message_spec.rb | 4 +- .../note_message_spec.rb | 10 +- .../pipeline_message_spec.rb | 4 +- .../push_message_spec.rb | 4 +- .../wiki_page_message_spec.rb | 2 +- .../project_services/chat_service_spec.rb | 11 +- .../mattermost_service_spec.rb | 5 + .../project_services/slack_service_spec.rb | 324 +---------------- spec/models/project_spec.rb | 1 + .../slack_mattermost_shared_examples.rb | 328 ++++++++++++++++++ 33 files changed, 658 insertions(+), 524 deletions(-) rename app/models/project_services/{slack_service => chat_message}/base_message.rb (96%) rename app/models/project_services/{slack_service => chat_message}/build_message.rb (98%) rename app/models/project_services/{slack_service => chat_message}/issue_message.rb (98%) rename app/models/project_services/{slack_service => chat_message}/merge_message.rb (98%) rename app/models/project_services/{slack_service => chat_message}/note_message.rb (99%) rename app/models/project_services/{slack_service => chat_message}/pipeline_message.rb (98%) rename app/models/project_services/{slack_service => chat_message}/push_message.rb (99%) rename app/models/project_services/{slack_service => chat_message}/wiki_page_message.rb (98%) create mode 100644 app/models/project_services/mattermost_service.rb create mode 100644 changelogs/unreleased/issue_22269.yml create mode 100644 doc/project_services/img/mattermost_configuration.png create mode 100644 doc/project_services/mattermost.md rename spec/models/project_services/{slack_service => chat_message}/build_message_spec.rb (94%) rename spec/models/project_services/{slack_service => chat_message}/issue_message_spec.rb (93%) rename spec/models/project_services/{slack_service => chat_message}/merge_message_spec.rb (92%) rename spec/models/project_services/{slack_service => chat_message}/note_message_spec.rb (92%) rename spec/models/project_services/{slack_service => chat_message}/pipeline_message_spec.rb (94%) rename spec/models/project_services/{slack_service => chat_message}/push_message_spec.rb (95%) rename spec/models/project_services/{slack_service => chat_message}/wiki_page_message_spec.rb (97%) create mode 100644 spec/models/project_services/mattermost_service_spec.rb create mode 100644 spec/support/slack_mattermost_shared_examples.rb diff --git a/app/models/project.rb b/app/models/project.rb index 2c726cfc5df..19c2d24212d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -95,6 +95,7 @@ class Project < ActiveRecord::Base has_one :asana_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy has_one :mattermost_slash_commands_service, dependent: :destroy + has_one :mattermost_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/chat_message/base_message.rb similarity index 96% rename from app/models/project_services/slack_service/base_message.rb rename to app/models/project_services/chat_message/base_message.rb index f1182824687..a03605d01fb 100644 --- a/app/models/project_services/slack_service/base_message.rb +++ b/app/models/project_services/chat_message/base_message.rb @@ -1,6 +1,6 @@ require 'slack-notifier' -class SlackService +module ChatMessage class BaseMessage def initialize(params) raise NotImplementedError diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/chat_message/build_message.rb similarity index 98% rename from app/models/project_services/slack_service/build_message.rb rename to app/models/project_services/chat_message/build_message.rb index 0fca4267bad..53e35cb21bf 100644 --- a/app/models/project_services/slack_service/build_message.rb +++ b/app/models/project_services/chat_message/build_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class BuildMessage < BaseMessage attr_reader :sha attr_reader :ref_type diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb similarity index 98% rename from app/models/project_services/slack_service/issue_message.rb rename to app/models/project_services/chat_message/issue_message.rb index cd87a79d0c6..14fd64e5332 100644 --- a/app/models/project_services/slack_service/issue_message.rb +++ b/app/models/project_services/chat_message/issue_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class IssueMessage < BaseMessage attr_reader :user_name attr_reader :title diff --git a/app/models/project_services/slack_service/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb similarity index 98% rename from app/models/project_services/slack_service/merge_message.rb rename to app/models/project_services/chat_message/merge_message.rb index b7615c96068..ab5e8b24167 100644 --- a/app/models/project_services/slack_service/merge_message.rb +++ b/app/models/project_services/chat_message/merge_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class MergeMessage < BaseMessage attr_reader :user_name attr_reader :project_name diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/chat_message/note_message.rb similarity index 99% rename from app/models/project_services/slack_service/note_message.rb rename to app/models/project_services/chat_message/note_message.rb index 797c5937f09..ca1d7207034 100644 --- a/app/models/project_services/slack_service/note_message.rb +++ b/app/models/project_services/chat_message/note_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class NoteMessage < BaseMessage attr_reader :message attr_reader :user_name diff --git a/app/models/project_services/slack_service/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb similarity index 98% rename from app/models/project_services/slack_service/pipeline_message.rb rename to app/models/project_services/chat_message/pipeline_message.rb index b6355fc4171..210027565a8 100644 --- a/app/models/project_services/slack_service/pipeline_message.rb +++ b/app/models/project_services/chat_message/pipeline_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class PipelineMessage < BaseMessage attr_reader :ref_type, :ref, :status, :project_name, :project_url, :user_name, :duration, :pipeline_id diff --git a/app/models/project_services/slack_service/push_message.rb b/app/models/project_services/chat_message/push_message.rb similarity index 99% rename from app/models/project_services/slack_service/push_message.rb rename to app/models/project_services/chat_message/push_message.rb index b26f3e9ddce..2d73b71ec37 100644 --- a/app/models/project_services/slack_service/push_message.rb +++ b/app/models/project_services/chat_message/push_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class PushMessage < BaseMessage attr_reader :after attr_reader :before diff --git a/app/models/project_services/slack_service/wiki_page_message.rb b/app/models/project_services/chat_message/wiki_page_message.rb similarity index 98% rename from app/models/project_services/slack_service/wiki_page_message.rb rename to app/models/project_services/chat_message/wiki_page_message.rb index 160ca3ac115..134083e4504 100644 --- a/app/models/project_services/slack_service/wiki_page_message.rb +++ b/app/models/project_services/chat_message/wiki_page_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class WikiPageMessage < BaseMessage attr_reader :user_name attr_reader :title diff --git a/app/models/project_services/chat_service.rb b/app/models/project_services/chat_service.rb index d36beff5fa6..8ac049ba939 100644 --- a/app/models/project_services/chat_service.rb +++ b/app/models/project_services/chat_service.rb @@ -1,21 +1,148 @@ # Base class for Chat services # This class is not meant to be used directly, but only to inherrit from. class ChatService < Service + include ChatMessage + default_value_for :category, 'chat' - has_many :chat_names, foreign_key: :service_id + prop_accessor :webhook, :username, :channel + boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines - def valid_token?(token) - self.respond_to?(:token) && - self.token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + validates :webhook, presence: true, url: true, if: :activated? + + def initialize_properties + # Custom serialized properties initialization + self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } + + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + self.notify_only_broken_pipelines = true + end + end + + def can_test? + valid? end def supported_events - [] + %w[push issue confidential_issue merge_request note tag_push + build pipeline wiki_page] end - def trigger(params) + def execute(data) + return unless supported_events.include?(data[:object_kind]) + return unless webhook.present? + + object_kind = data[:object_kind] + + data = data.merge( + project_url: project_url, + project_name: project_name + ) + + # WebHook events often have an 'update' event that follows a 'open' or + # 'close' action. Ignore update events for now to prevent duplicate + # messages from arriving. + + message = get_message(object_kind, data) + + return false unless message + + opt = {} + + opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel + opt[:username] = username if username + + notifier = Slack::Notifier.new(webhook, opt) + notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) + + true + end + + def event_channel_names + supported_events.map { |event| event_channel_name(event) } + end + + def event_field(event) + fields.find { |field| field[:name] == event_channel_name(event) } + end + + def global_fields + fields.reject { |field| field[:name].end_with?('channel') } + end + + def default_channel raise NotImplementedError end + + private + + def get_message(object_kind, data) + case object_kind + when "push", "tag_push" + PushMessage.new(data) + when "issue" + IssueMessage.new(data) unless is_update?(data) + when "merge_request" + MergeMessage.new(data) unless is_update?(data) + when "note" + NoteMessage.new(data) + when "build" + BuildMessage.new(data) if should_build_be_notified?(data) + when "pipeline" + PipelineMessage.new(data) if should_pipeline_be_notified?(data) + when "wiki_page" + WikiPageMessage.new(data) + end + end + + def get_channel_field(event) + field_name = event_channel_name(event) + self.public_send(field_name) + end + + def build_event_channels + supported_events.reduce([]) do |channels, event| + channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel } + end + end + + def event_channel_name(event) + "#{event}_channel" + end + + def project_name + project.name_with_namespace.gsub(/\s/, '') + end + + def project_url + project.web_url + end + + def is_update?(data) + data[:object_attributes][:action] == 'update' + end + + def should_build_be_notified?(data) + case data[:commit][:status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end + + def should_pipeline_be_notified?(data) + case data[:object_attributes][:status] + when 'success' + !notify_only_broken_pipelines? + when 'failed' + true + else + false + end + end end diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb new file mode 100644 index 00000000000..9d61c251a32 --- /dev/null +++ b/app/models/project_services/mattermost_service.rb @@ -0,0 +1,41 @@ +class MattermostService < ChatService + def title + 'Mattermost notifications' + end + + def description + 'Receive event notifications in Mattermost' + end + + def to_param + 'mattermost' + end + + def help + 'This service sends notifications about projects events to Mattermost channels.
+ To set up this service: +
    +
  1. Enable incoming webhooks in your Mattermost installation.
  2. +
  3. Add an incoming webhook in your Mattermost team. The default channel can be overridden for each event.
  4. +
  5. Paste the webhook URL into the field bellow.
  6. +
  7. Select events below to enable notifications. The channel and username are optional.
  8. +
' + end + + def fields + default_fields + build_event_channels + end + + def default_fields + [ + { type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' }, + { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + ] + end + + def default_channel + "#town-square" + end +end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 33431f41dc2..3993dfbda17 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -1,8 +1,18 @@ -class MattermostSlashCommandsService < ChatService +class MattermostSlashCommandsService < Service include TriggersHelper prop_accessor :token + def valid_token?(token) + self.respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + end + + def supported_events + [] + end + def can_test? false end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index e1b937817f4..0df1743c4ba 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -1,25 +1,10 @@ -class SlackService < Service - prop_accessor :webhook, :username, :channel - boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines - validates :webhook, presence: true, url: true, if: :activated? - - def initialize_properties - # Custom serialized properties initialization - self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } - - if properties.nil? - self.properties = {} - self.notify_only_broken_builds = true - self.notify_only_broken_pipelines = true - end - end - +class SlackService < ChatService def title - 'Slack' + 'Slack notifications' end def description - 'A team communication tool for the 21st century' + 'Receive event notifications in Slack' end def to_param @@ -27,150 +12,29 @@ class SlackService < Service end def help - 'This service sends notifications to your Slack channel.
- To setup this Service you need to create a new "Incoming webhook" in your Slack integration panel, - and enter the Webhook URL below.' + 'This service sends notifications about projects events to Slack channels.
+ To setup this service: +
    +
  1. Add an incoming webhook in your Slack team. The default channel can be overridden for each event.
  2. +
  3. Paste the Webhook URL into the field below.
  4. +
  5. Select events below to enable notifications. The channel and username are optional.
  6. +
' end def fields - default_fields = - [ - { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, - { type: 'text', name: 'username', placeholder: 'username' }, - { type: 'text', name: 'channel', placeholder: "#general" }, - { type: 'checkbox', name: 'notify_only_broken_builds' }, - { type: 'checkbox', name: 'notify_only_broken_pipelines' }, - ] - default_fields + build_event_channels end - def supported_events - %w[push issue confidential_issue merge_request note tag_push - build pipeline wiki_page] + def default_fields + [ + { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, + { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + ] end - def execute(data) - return unless supported_events.include?(data[:object_kind]) - return unless webhook.present? - - object_kind = data[:object_kind] - - data = data.merge( - project_url: project_url, - project_name: project_name - ) - - # WebHook events often have an 'update' event that follows a 'open' or - # 'close' action. Ignore update events for now to prevent duplicate - # messages from arriving. - - message = get_message(object_kind, data) - - if message - opt = {} - - event_channel = get_channel_field(object_kind) || channel - - opt[:channel] = event_channel if event_channel - opt[:username] = username if username - - notifier = Slack::Notifier.new(webhook, opt) - notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) - - true - else - false - end - end - - def event_channel_names - supported_events.map { |event| event_channel_name(event) } - end - - def event_field(event) - fields.find { |field| field[:name] == event_channel_name(event) } - end - - def global_fields - fields.reject { |field| field[:name].end_with?('channel') } - end - - private - - def get_message(object_kind, data) - case object_kind - when "push", "tag_push" - PushMessage.new(data) - when "issue" - IssueMessage.new(data) unless is_update?(data) - when "merge_request" - MergeMessage.new(data) unless is_update?(data) - when "note" - NoteMessage.new(data) - when "build" - BuildMessage.new(data) if should_build_be_notified?(data) - when "pipeline" - PipelineMessage.new(data) if should_pipeline_be_notified?(data) - when "wiki_page" - WikiPageMessage.new(data) - end - end - - def get_channel_field(event) - field_name = event_channel_name(event) - self.public_send(field_name) - end - - def build_event_channels - supported_events.reduce([]) do |channels, event| - channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" } - end - end - - def event_channel_name(event) - "#{event}_channel" - end - - def project_name - project.name_with_namespace.gsub(/\s/, '') - end - - def project_url - project.web_url - end - - def is_update?(data) - data[:object_attributes][:action] == 'update' - end - - def should_build_be_notified?(data) - case data[:commit][:status] - when 'success' - !notify_only_broken_builds? - when 'failed' - true - else - false - end - end - - def should_pipeline_be_notified?(data) - case data[:object_attributes][:status] - when 'success' - !notify_only_broken_pipelines? - when 'failed' - true - else - false - end + def default_channel + "#general" end end - -require "slack_service/issue_message" -require "slack_service/push_message" -require "slack_service/merge_message" -require "slack_service/note_message" -require "slack_service/build_message" -require "slack_service/pipeline_message" -require "slack_service/wiki_page_message" diff --git a/app/models/service.rb b/app/models/service.rb index e49a8fa2904..8e58f2a1925 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -220,6 +220,7 @@ class Service < ActiveRecord::Base pivotaltracker pushover redmine + mattermost slack teamcity ] diff --git a/changelogs/unreleased/issue_22269.yml b/changelogs/unreleased/issue_22269.yml new file mode 100644 index 00000000000..6b7164aff77 --- /dev/null +++ b/changelogs/unreleased/issue_22269.yml @@ -0,0 +1,4 @@ +--- +title: Create mattermost service +merge_request: +author: diff --git a/doc/api/services.md b/doc/api/services.md index 3dad953cd1e..1466b8189b0 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -703,9 +703,9 @@ Get Redmine service settings for a project. GET /projects/:id/services/redmine ``` -## Slack +## Slack notifications -A team communication tool for the 21st century +Receive event notifications in Slack ### Create/Edit Slack service @@ -737,6 +737,40 @@ Get Slack service settings for a project. GET /projects/:id/services/slack ``` +## Mattermost notifications + +Receive event notifications in Mattermost + +### Create/Edit Mattermost notifications service + +Set Mattermost service for a project. + +``` +PUT /projects/:id/services/mattermost +``` + +Parameters: + +- `webhook` (**required**) - https://mattermost.example/hooks/1298aff... +- `username` (optional) - username +- `channel` (optional) - #channel + +### Delete Mattermost notifications service + +Delete Mattermost Notifications service for a project. + +``` +DELETE /projects/:id/services/mattermost +``` + +### Get Mattermost notifications service settings + +Get Mattermost notifications service settings for a project. + +``` +GET /projects/:id/services/mattermost +``` + ## JetBrains TeamCity CI A continuous integration and build server diff --git a/doc/project_services/img/mattermost_configuration.png b/doc/project_services/img/mattermost_configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..3c5ff5ee317ff47596210461924bdd635faeaab1 GIT binary patch literal 73502 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 Lg%*qGe*Av`0AO)E literal 0 HcmV?d00001 diff --git a/doc/project_services/mattermost.md b/doc/project_services/mattermost.md new file mode 100644 index 00000000000..fbc7dfeee6d --- /dev/null +++ b/doc/project_services/mattermost.md @@ -0,0 +1,45 @@ +# Mattermost Notifications Service + +## On Mattermost + +To enable Mattermost integration you must create an incoming webhook integration: + +1. Sign in to your Mattermost instance +1. Visit incoming webhooks, that will be something like: https://mattermost.example/your_team_name/integrations/incoming_webhooks/add +1. Choose a display name, description and channel, those can be overridden on GitLab +1. Save it, copy the **Webhook URL**, we'll need this later for GitLab. + +There might be some cases that Incoming Webhooks are blocked by admin, ask your mattermost admin to enable +it on https://mattermost.example/admin_console/integrations/custom. + +Display name override is not enabled by default, you need to ask your admin to enable it on that same section. + +## On GitLab + +After you set up Mattermost, it's time to set up GitLab. + +Go to your project's **Settings > Services > Mattermost Notifications** and you will see a +checkbox with the following events that can be triggered: + +- Push +- Issue +- Merge request +- Note +- Tag push +- Build +- Wiki page + +Bellow each of these event checkboxes, you will have an input field to insert +which Mattermost channel you want to send that event message, with `#town-square` +being the default. The hash sign is optional. + +At the end, fill in your Mattermost details: + +| Field | Description | +| ----- | ----------- | +| **Webhook** | The incoming webhooks which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo... | +| **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. | +| **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. | + + +![Mattermost configuration](img/mattermost_configuration.png) diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md index a7bcd186a8c..0f398874b8f 100644 --- a/doc/project_services/project_services.md +++ b/doc/project_services/project_services.md @@ -44,10 +44,11 @@ further configuration instructions and details. Contributions are welcome. | JetBrains TeamCity CI | A continuous integration and build server | | [Kubernetes](kubernetes.md) | A containerized deployment service | | [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands | +| [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost | +| [Slack Notifications](slack.md) | Receive event notifications in Slack | | PivotalTracker | Project Management Software (Source Commits Endpoint) | | Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop | | [Redmine](redmine.md) | Redmine issue tracker | -| [Slack](slack.md) | A team communication tool for the 21st century | ## Services Templates diff --git a/doc/project_services/slack.md b/doc/project_services/slack.md index 3cfe77c9f85..0b682b43810 100644 --- a/doc/project_services/slack.md +++ b/doc/project_services/slack.md @@ -1,4 +1,4 @@ -# Slack Service +# Slack Notifications Service ## On Slack @@ -15,7 +15,7 @@ Slack: After you set up Slack, it's time to set up GitLab. -Go to your project's **Settings > Services > Slack** and you will see a +Go to your project's **Settings > Services > Slack Notifications** and you will see a checkbox with the following events that can be triggered: - Push diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index c4ee838b7c9..068137f6255 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -137,6 +137,7 @@ project: - asana_service - gemnasium_service - slack_service +- mattermost_service - buildkite_service - bamboo_service - teamcity_service diff --git a/spec/models/project_services/slack_service/build_message_spec.rb b/spec/models/project_services/chat_message/build_message_spec.rb similarity index 94% rename from spec/models/project_services/slack_service/build_message_spec.rb rename to spec/models/project_services/chat_message/build_message_spec.rb index 452f4e2782c..b71d153f814 100644 --- a/spec/models/project_services/slack_service/build_message_spec.rb +++ b/spec/models/project_services/chat_message/build_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackService::BuildMessage do - subject { SlackService::BuildMessage.new(args) } +describe ChatMessage::BuildMessage do + subject { described_class.new(args) } let(:args) do { diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb similarity index 93% rename from spec/models/project_services/slack_service/issue_message_spec.rb rename to spec/models/project_services/chat_message/issue_message_spec.rb index 98c36ec088d..ebe0ead4408 100644 --- a/spec/models/project_services/slack_service/issue_message_spec.rb +++ b/spec/models/project_services/chat_message/issue_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackService::IssueMessage, models: true do - subject { SlackService::IssueMessage.new(args) } +describe ChatMessage::IssueMessage, models: true do + subject { described_class.new(args) } let(:args) do { diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb similarity index 92% rename from spec/models/project_services/slack_service/merge_message_spec.rb rename to spec/models/project_services/chat_message/merge_message_spec.rb index c5c052d9af1..07c414c6ca4 100644 --- a/spec/models/project_services/slack_service/merge_message_spec.rb +++ b/spec/models/project_services/chat_message/merge_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackService::MergeMessage, models: true do - subject { SlackService::MergeMessage.new(args) } +describe ChatMessage::MergeMessage, models: true do + subject { described_class.new(args) } let(:args) do { diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb similarity index 92% rename from spec/models/project_services/slack_service/note_message_spec.rb rename to spec/models/project_services/chat_message/note_message_spec.rb index 97f818125d3..31936da40a2 100644 --- a/spec/models/project_services/slack_service/note_message_spec.rb +++ b/spec/models/project_services/chat_message/note_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackService::NoteMessage, models: true do +describe ChatMessage::NoteMessage, models: true do let(:color) { '#345' } before do @@ -36,7 +36,7 @@ describe SlackService::NoteMessage, models: true do end it 'returns a message regarding notes on commits' do - message = SlackService::NoteMessage.new(@args) + message = described_class.new(@args) expect(message.pretext).to eq("test.user in : " \ "*Added a commit message*") @@ -62,7 +62,7 @@ describe SlackService::NoteMessage, models: true do end it 'returns a message regarding notes on a merge request' do - message = SlackService::NoteMessage.new(@args) + message = described_class.new(@args) expect(message.pretext).to eq("test.user in : " \ "*merge request title*") @@ -88,7 +88,7 @@ describe SlackService::NoteMessage, models: true do end it 'returns a message regarding notes on an issue' do - message = SlackService::NoteMessage.new(@args) + message = described_class.new(@args) expect(message.pretext).to eq( "test.user in : " \ @@ -114,7 +114,7 @@ describe SlackService::NoteMessage, models: true do end it 'returns a message regarding notes on a project snippet' do - message = SlackService::NoteMessage.new(@args) + message = described_class.new(@args) expect(message.pretext).to eq("test.user in : " \ "*snippet title*") diff --git a/spec/models/project_services/slack_service/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb similarity index 94% rename from spec/models/project_services/slack_service/pipeline_message_spec.rb rename to spec/models/project_services/chat_message/pipeline_message_spec.rb index 4098500122f..eca71db07b6 100644 --- a/spec/models/project_services/slack_service/pipeline_message_spec.rb +++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackService::PipelineMessage do - subject { SlackService::PipelineMessage.new(args) } +describe ChatMessage::PipelineMessage do + subject { described_class.new(args) } let(:user) { { name: 'hacker' } } let(:args) do diff --git a/spec/models/project_services/slack_service/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb similarity index 95% rename from spec/models/project_services/slack_service/push_message_spec.rb rename to spec/models/project_services/chat_message/push_message_spec.rb index 17cd05e24f1..b781c4505db 100644 --- a/spec/models/project_services/slack_service/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackService::PushMessage, models: true do - subject { SlackService::PushMessage.new(args) } +describe ChatMessage::PushMessage, models: true do + subject { described_class.new(args) } let(:args) do { diff --git a/spec/models/project_services/slack_service/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb similarity index 97% rename from spec/models/project_services/slack_service/wiki_page_message_spec.rb rename to spec/models/project_services/chat_message/wiki_page_message_spec.rb index 093911598b0..94c04dc0865 100644 --- a/spec/models/project_services/slack_service/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackService::WikiPageMessage, models: true do +describe ChatMessage::WikiPageMessage, models: true do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_service_spec.rb b/spec/models/project_services/chat_service_spec.rb index c6a45a3e1be..e6314a43501 100644 --- a/spec/models/project_services/chat_service_spec.rb +++ b/spec/models/project_services/chat_service_spec.rb @@ -2,14 +2,7 @@ require 'spec_helper' describe ChatService, models: true do describe "Associations" do - it { is_expected.to have_many :chat_names } - end - - describe '#valid_token?' do - subject { described_class.new } - - it 'is false as it has no token' do - expect(subject.valid_token?('wer')).to be_falsey - end + before { allow(subject).to receive(:activated?).and_return(true) } + it { is_expected.to validate_presence_of :webhook } end end diff --git a/spec/models/project_services/mattermost_service_spec.rb b/spec/models/project_services/mattermost_service_spec.rb new file mode 100644 index 00000000000..1e5b4c715c3 --- /dev/null +++ b/spec/models/project_services/mattermost_service_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe MattermostService, models: true do + it_behaves_like "slack or mattermost" +end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index c07a70a8069..4928391fd7e 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -1,327 +1,5 @@ require 'spec_helper' describe SlackService, models: true do - let(:slack) { SlackService.new } - let(:webhook_url) { 'https://example.gitlab.com/' } - - describe "Associations" do - it { is_expected.to belong_to :project } - it { is_expected.to have_one :service_hook } - end - - describe 'Validations' do - context 'when service is active' do - before { subject.active = true } - - it { is_expected.to validate_presence_of(:webhook) } - it_behaves_like 'issue tracker service URL attribute', :webhook - end - - context 'when service is inactive' do - before { subject.active = false } - - it { is_expected.not_to validate_presence_of(:webhook) } - end - end - - describe "Execute" do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:username) { 'slack_username' } - let(:channel) { 'slack_channel' } - - let(:push_sample_data) do - Gitlab::DataBuilder::Push.build_sample(project, user) - end - - before do - allow(slack).to receive_messages( - project: project, - project_id: project.id, - service_hook: true, - webhook: webhook_url - ) - - WebMock.stub_request(:post, webhook_url) - - opts = { - title: 'Awesome issue', - description: 'please fix' - } - - issue_service = Issues::CreateService.new(project, user, opts) - @issue = issue_service.execute - @issues_sample_data = issue_service.hook_data(@issue, 'open') - - opts = { - title: 'Awesome merge_request', - description: 'please fix', - source_branch: 'feature', - target_branch: 'master' - } - merge_service = MergeRequests::CreateService.new(project, - user, opts) - @merge_request = merge_service.execute - @merge_sample_data = merge_service.hook_data(@merge_request, - 'open') - - opts = { - title: "Awesome wiki_page", - content: "Some text describing some thing or another", - format: "md", - message: "user created page: Awesome wiki_page" - } - - wiki_page_service = WikiPages::CreateService.new(project, user, opts) - @wiki_page = wiki_page_service.execute - @wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create') - end - - it "calls Slack API for push events" do - slack.execute(push_sample_data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - - it "calls Slack API for issue events" do - slack.execute(@issues_sample_data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - - it "calls Slack API for merge requests events" do - slack.execute(@merge_sample_data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - - it "calls Slack API for wiki page events" do - slack.execute(@wiki_page_sample_data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - - it 'uses the username as an option for slack when configured' do - allow(slack).to receive(:username).and_return(username) - expect(Slack::Notifier).to receive(:new). - with(webhook_url, username: username). - and_return( - double(:slack_service).as_null_object - ) - - slack.execute(push_sample_data) - end - - it 'uses the channel as an option when it is configured' do - allow(slack).to receive(:channel).and_return(channel) - expect(Slack::Notifier).to receive(:new). - with(webhook_url, channel: channel). - and_return( - double(:slack_service).as_null_object - ) - slack.execute(push_sample_data) - end - - context "event channels" do - it "uses the right channel for push event" do - slack.update_attributes(push_channel: "random") - - expect(Slack::Notifier).to receive(:new). - with(webhook_url, channel: "random"). - and_return( - double(:slack_service).as_null_object - ) - - slack.execute(push_sample_data) - end - - it "uses the right channel for merge request event" do - slack.update_attributes(merge_request_channel: "random") - - expect(Slack::Notifier).to receive(:new). - with(webhook_url, channel: "random"). - and_return( - double(:slack_service).as_null_object - ) - - slack.execute(@merge_sample_data) - end - - it "uses the right channel for issue event" do - slack.update_attributes(issue_channel: "random") - - expect(Slack::Notifier).to receive(:new). - with(webhook_url, channel: "random"). - and_return( - double(:slack_service).as_null_object - ) - - slack.execute(@issues_sample_data) - end - - it "uses the right channel for wiki event" do - slack.update_attributes(wiki_page_channel: "random") - - expect(Slack::Notifier).to receive(:new). - with(webhook_url, channel: "random"). - and_return( - double(:slack_service).as_null_object - ) - - slack.execute(@wiki_page_sample_data) - end - - context "note event" do - let(:issue_note) do - create(:note_on_issue, project: project, note: "issue note") - end - - it "uses the right channel" do - slack.update_attributes(note_channel: "random") - - note_data = Gitlab::DataBuilder::Note.build(issue_note, user) - - expect(Slack::Notifier).to receive(:new). - with(webhook_url, channel: "random"). - and_return( - double(:slack_service).as_null_object - ) - - slack.execute(note_data) - end - end - end - end - - describe "Note events" do - let(:user) { create(:user) } - let(:project) { create(:project, creator_id: user.id) } - - before do - allow(slack).to receive_messages( - project: project, - project_id: project.id, - service_hook: true, - webhook: webhook_url - ) - - WebMock.stub_request(:post, webhook_url) - end - - context 'when commit comment event executed' do - let(:commit_note) do - create(:note_on_commit, author: user, - project: project, - commit_id: project.repository.commit.id, - note: 'a comment on a commit') - end - - it "calls Slack API for commit comment events" do - data = Gitlab::DataBuilder::Note.build(commit_note, user) - slack.execute(data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end - - context 'when merge request comment event executed' do - let(:merge_request_note) do - create(:note_on_merge_request, project: project, - note: "merge request note") - end - - it "calls Slack API for merge request comment events" do - data = Gitlab::DataBuilder::Note.build(merge_request_note, user) - slack.execute(data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end - - context 'when issue comment event executed' do - let(:issue_note) do - create(:note_on_issue, project: project, note: "issue note") - end - - it "calls Slack API for issue comment events" do - data = Gitlab::DataBuilder::Note.build(issue_note, user) - slack.execute(data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end - - context 'when snippet comment event executed' do - let(:snippet_note) do - create(:note_on_project_snippet, project: project, - note: "snippet note") - end - - it "calls Slack API for snippet comment events" do - data = Gitlab::DataBuilder::Note.build(snippet_note, user) - slack.execute(data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end - end - - describe 'Pipeline events' do - let(:user) { create(:user) } - let(:project) { create(:project) } - - let(:pipeline) do - create(:ci_pipeline, - project: project, status: status, - sha: project.commit.sha, ref: project.default_branch) - end - - before do - allow(slack).to receive_messages( - project: project, - service_hook: true, - webhook: webhook_url - ) - end - - shared_examples 'call Slack API' do - before do - WebMock.stub_request(:post, webhook_url) - end - - it 'calls Slack API for pipeline events' do - data = Gitlab::DataBuilder::Pipeline.build(pipeline) - slack.execute(data) - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end - - context 'with failed pipeline' do - let(:status) { 'failed' } - - it_behaves_like 'call Slack API' - end - - context 'with succeeded pipeline' do - let(:status) { 'success' } - - context 'with default to notify_only_broken_pipelines' do - it 'does not call Slack API for pipeline events' do - data = Gitlab::DataBuilder::Pipeline.build(pipeline) - result = slack.execute(data) - - expect(result).to be_falsy - end - end - - context 'with setting notify_only_broken_pipelines to false' do - before do - slack.notify_only_broken_pipelines = false - end - - it_behaves_like 'call Slack API' - end - end - end + it_behaves_like "slack or mattermost" end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 21ff238841e..1d8e42202ea 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -23,6 +23,7 @@ describe Project, models: true do it { is_expected.to have_many(:chat_services) } it { is_expected.to have_one(:forked_project_link).dependent(:destroy) } it { is_expected.to have_one(:slack_service).dependent(:destroy) } + it { is_expected.to have_one(:mattermost_service).dependent(:destroy) } it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } it { is_expected.to have_many(:boards).dependent(:destroy) } diff --git a/spec/support/slack_mattermost_shared_examples.rb b/spec/support/slack_mattermost_shared_examples.rb new file mode 100644 index 00000000000..56d4965f74d --- /dev/null +++ b/spec/support/slack_mattermost_shared_examples.rb @@ -0,0 +1,328 @@ +Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f } + +RSpec.shared_examples 'slack or mattermost' do + let(:chat_service) { described_class.new } + let(:webhook_url) { 'https://example.gitlab.com/' } + + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:webhook) } + it_behaves_like 'issue tracker service URL attribute', :webhook + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:webhook) } + end + end + + describe "#execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:username) { 'slack_username' } + let(:channel) { 'slack_channel' } + + let(:push_sample_data) do + Gitlab::DataBuilder::Push.build_sample(project, user) + end + + before do + allow(chat_service).to receive_messages( + project: project, + project_id: project.id, + service_hook: true, + webhook: webhook_url + ) + + WebMock.stub_request(:post, webhook_url) + + opts = { + title: 'Awesome issue', + description: 'please fix' + } + + issue_service = Issues::CreateService.new(project, user, opts) + @issue = issue_service.execute + @issues_sample_data = issue_service.hook_data(@issue, 'open') + + opts = { + title: 'Awesome merge_request', + description: 'please fix', + source_branch: 'feature', + target_branch: 'master' + } + merge_service = MergeRequests::CreateService.new(project, + user, opts) + @merge_request = merge_service.execute + @merge_sample_data = merge_service.hook_data(@merge_request, + 'open') + + opts = { + title: "Awesome wiki_page", + content: "Some text describing some thing or another", + format: "md", + message: "user created page: Awesome wiki_page" + } + + wiki_page_service = WikiPages::CreateService.new(project, user, opts) + @wiki_page = wiki_page_service.execute + @wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create') + end + + it "calls Slack/Mattermost API for push events" do + chat_service.execute(push_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "calls Slack/Mattermost API for issue events" do + chat_service.execute(@issues_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "calls Slack/Mattermost API for merge requests events" do + chat_service.execute(@merge_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "calls Slack/Mattermost API for wiki page events" do + chat_service.execute(@wiki_page_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it 'uses the username as an option for slack when configured' do + allow(chat_service).to receive(:username).and_return(username) + + expect(Slack::Notifier).to receive(:new). + with(webhook_url, username: username, channel: chat_service.default_channel). + and_return( + double(:slack_service).as_null_object + ) + + chat_service.execute(push_sample_data) + end + + it 'uses the channel as an option when it is configured' do + allow(chat_service).to receive(:channel).and_return(channel) + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: channel). + and_return( + double(:slack_service).as_null_object + ) + chat_service.execute(push_sample_data) + end + + context "event channels" do + it "uses the right channel for push event" do + chat_service.update_attributes(push_channel: "random") + + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: "random"). + and_return( + double(:slack_service).as_null_object + ) + + chat_service.execute(push_sample_data) + end + + it "uses the right channel for merge request event" do + chat_service.update_attributes(merge_request_channel: "random") + + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: "random"). + and_return( + double(:slack_service).as_null_object + ) + + chat_service.execute(@merge_sample_data) + end + + it "uses the right channel for issue event" do + chat_service.update_attributes(issue_channel: "random") + + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: "random"). + and_return( + double(:slack_service).as_null_object + ) + + chat_service.execute(@issues_sample_data) + end + + it "uses the right channel for wiki event" do + chat_service.update_attributes(wiki_page_channel: "random") + + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: "random"). + and_return( + double(:slack_service).as_null_object + ) + + chat_service.execute(@wiki_page_sample_data) + end + + context "note event" do + let(:issue_note) do + create(:note_on_issue, project: project, note: "issue note") + end + + it "uses the right channel" do + chat_service.update_attributes(note_channel: "random") + + note_data = Gitlab::DataBuilder::Note.build(issue_note, user) + + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: "random"). + and_return( + double(:slack_service).as_null_object + ) + + chat_service.execute(note_data) + end + end + end + end + + describe "Note events" do + let(:user) { create(:user) } + let(:project) { create(:project, creator_id: user.id) } + + before do + allow(chat_service).to receive_messages( + project: project, + project_id: project.id, + service_hook: true, + webhook: webhook_url + ) + + WebMock.stub_request(:post, webhook_url) + end + + context 'when commit comment event executed' do + let(:commit_note) do + create(:note_on_commit, author: user, + project: project, + commit_id: project.repository.commit.id, + note: 'a comment on a commit') + end + + it "calls Slack/Mattermost API for commit comment events" do + data = Gitlab::DataBuilder::Note.build(commit_note, user) + chat_service.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + end + + context 'when merge request comment event executed' do + let(:merge_request_note) do + create(:note_on_merge_request, project: project, + note: "merge request note") + end + + it "calls Slack API for merge request comment events" do + data = Gitlab::DataBuilder::Note.build(merge_request_note, user) + chat_service.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + end + + context 'when issue comment event executed' do + let(:issue_note) do + create(:note_on_issue, project: project, note: "issue note") + end + + it "calls Slack API for issue comment events" do + data = Gitlab::DataBuilder::Note.build(issue_note, user) + chat_service.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + end + + context 'when snippet comment event executed' do + let(:snippet_note) do + create(:note_on_project_snippet, project: project, + note: "snippet note") + end + + it "calls Slack API for snippet comment events" do + data = Gitlab::DataBuilder::Note.build(snippet_note, user) + chat_service.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + end + end + + describe 'Pipeline events' do + let(:user) { create(:user) } + let(:project) { create(:project) } + + let(:pipeline) do + create(:ci_pipeline, + project: project, status: status, + sha: project.commit.sha, ref: project.default_branch) + end + + before do + allow(chat_service).to receive_messages( + project: project, + service_hook: true, + webhook: webhook_url + ) + end + + shared_examples 'call Slack/Mattermost API' do + before do + WebMock.stub_request(:post, webhook_url) + end + + it 'calls Slack/Mattermost API for pipeline events' do + data = Gitlab::DataBuilder::Pipeline.build(pipeline) + chat_service.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + end + + context 'with failed pipeline' do + let(:status) { 'failed' } + + it_behaves_like 'call Slack/Mattermost API' + end + + context 'with succeeded pipeline' do + let(:status) { 'success' } + + context 'with default to notify_only_broken_pipelines' do + it 'does not call Slack/Mattermost API for pipeline events' do + data = Gitlab::DataBuilder::Pipeline.build(pipeline) + result = chat_service.execute(data) + + expect(result).to be_falsy + end + end + + context 'with setting notify_only_broken_pipelines to false' do + before do + chat_service.notify_only_broken_pipelines = false + end + + it_behaves_like 'call Slack/Mattermost API' + end + end + end +end From e682e2f888fd84deefc7b4b028d00a55bdd1c3a5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 15 Dec 2016 22:55:23 +0800 Subject: [PATCH 127/386] Strictly check the type loaded from YAML Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20062470 Eventually we should move to SafeYAML, but requiring that would impact all other `YAML.load` which is bad. For this particular case, I think we could just check it strictly. --- lib/gitlab/serialize/yaml_variables.rb | 27 ++++++++++++++++--- .../gitlab/serialize/yaml_variables_spec.rb | 21 +++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/serialize/yaml_variables.rb b/lib/gitlab/serialize/yaml_variables.rb index ca44acbd906..68ca50ed60e 100644 --- a/lib/gitlab/serialize/yaml_variables.rb +++ b/lib/gitlab/serialize/yaml_variables.rb @@ -10,15 +10,36 @@ module Gitlab def load(string) return unless string - YAML.load(string). - map(&YamlVariables.method(:convert_key_value_to_string)) + object = YAML.load(string) + + # We don't need to verify the object once we're using SafeYAML + if YamlVariables.verify_object(object) + YamlVariables.convert_object(object) + else + [] + end end def dump(object) YAML.dump(object) end - private + def verify_object(object) + YamlVariables.verify_type(object, Array) && + object.all? { |obj| YamlVariables.verify_type(obj, Hash) } + end + + # We use three ways to check if the class is exactly the one we want, + # rather than some subclass or duck typing class. + def verify_type(object, klass) + object.kind_of?(klass) && + object.class == klass && + klass === object + end + + def convert_object(object) + object.map(&YamlVariables.method(:convert_key_value_to_string)) + end def convert_key_value_to_string(variable) variable[:key] = variable[:key].to_s diff --git a/spec/lib/gitlab/serialize/yaml_variables_spec.rb b/spec/lib/gitlab/serialize/yaml_variables_spec.rb index 6d74f8c44d6..41aea95dfdb 100644 --- a/spec/lib/gitlab/serialize/yaml_variables_spec.rb +++ b/spec/lib/gitlab/serialize/yaml_variables_spec.rb @@ -16,4 +16,25 @@ describe Gitlab::Serialize::YamlVariables do { key: 'key', value: 'value', public: true }, { key: 'wee', value: '1', public: false }]) end + + context 'with a subclass of Array' do + let(:object) do + Kaminari::PaginatableArray.new << 'I am evil' + end + + it 'ignores it' do + is_expected.to eq([]) + end + end + + context 'with the array containing subclasses of Hash' do + let(:object) do + [ActiveSupport::OrderedOptions.new( + key: 'key', value: 'value', public: true)] + end + + it 'ignores it' do + is_expected.to eq([]) + end + end end From 1356e40f22c555f676777ed9385a12b09c19fdce Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 13 Oct 2016 04:33:09 +0100 Subject: [PATCH 128/386] Changed autocomplete_sources into an action that returns a single 'at' type of sources at a time Finished up autocomplete_sources action and added frontend to fetch data only when its needed Added wait_for_ajax to specs Fixed builds and improved the setup/destroy lifecycle Changed global namespace and DRYed up loading logic Added safety for accidentally loading data twice Removed destroy as its not necessary and is messing with click events from a blur race condition Created AutocompleteSourcesController and updated routes Fixed @undefined from tabbing before load ends Disable tabSelectsMatch until we have loaded data Review changes --- .../javascripts/gfm_auto_complete.js.es6 | 233 +++++++++--------- app/assets/javascripts/gl_form.js | 2 +- app/assets/javascripts/issuable_form.js | 2 +- .../autocomplete_sources_controller.rb | 48 ++++ app/controllers/projects_controller.rb | 33 --- .../layouts/_init_auto_complete.html.haml | 14 +- .../18435-autocomplete-is-not-performant.yml | 4 + config/routes/project.rb | 13 +- .../participants_autocomplete_spec.rb | 7 +- .../projects/gfm_autocomplete_load_spec.rb | 4 +- spec/routing/project_routing_spec.rb | 19 +- 11 files changed, 210 insertions(+), 169 deletions(-) create mode 100644 app/controllers/projects/autocomplete_sources_controller.rb create mode 100644 changelogs/unreleased/18435-autocomplete-is-not-performant.yml diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 245383438d1..dda061a556b 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -2,19 +2,28 @@ // Creates the variables for setting up GFM auto-completion (function() { - if (window.GitLab == null) { - window.GitLab = {}; + if (window.gl == null) { + window.gl = {}; } function sanitize(str) { return str.replace(/<(?:.|\n)*?>/gm, ''); } - window.GitLab.GfmAutoComplete = { - dataLoading: false, - dataLoaded: false, + window.gl.GfmAutoComplete = { + dataSources: {}, + defaultLoadingData: ['loading'], cachedData: {}, - dataSource: '', + isLoadingData: {}, + atTypeMap: { + ':': 'emojis', + '@': 'members', + '#': 'issues', + '!': 'mergeRequests', + '~': 'labels', + '%': 'milestones', + '/': 'commands' + }, // Emoji Emoji: { template: '
  • ${name} ${name}
  • ' @@ -35,33 +44,31 @@ template: '
  • ${title}
  • ' }, Loading: { - template: '
  • Loading...
  • ' + template: '
  • Loading...
  • ' }, DefaultOptions: { sorter: function(query, items, searchKey) { - // Highlight first item only if at least one char was typed - this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0; - if ((items[0].name != null) && items[0].name === 'loading') { + if (gl.GfmAutoComplete.isLoading(items)) { return items; } return $.fn.atwho["default"].callbacks.sorter(query, items, searchKey); }, filter: function(query, data, searchKey) { - if (data[0] === 'loading') { + if (gl.GfmAutoComplete.isLoading(data)) { + gl.GfmAutoComplete.togglePreventSelection.call(this, true); + gl.GfmAutoComplete.fetchData(this.$inputor, this.at); return data; + } else { + gl.GfmAutoComplete.togglePreventSelection.call(this, false); + return $.fn.atwho["default"].callbacks.filter(query, data, searchKey); } - return $.fn.atwho["default"].callbacks.filter(query, data, searchKey); }, beforeInsert: function(value) { if (value && !this.setting.skipSpecialCharacterTest) { var withoutAt = value.substring(1); if (withoutAt && /[^\w\d]/.test(withoutAt)) value = value.charAt() + '"' + withoutAt + '"'; } - if (!window.GitLab.GfmAutoComplete.dataLoaded) { - return this.at; - } else { - return value; - } + return value; }, matcher: function (flag, subtext) { // The below is taken from At.js source @@ -85,69 +92,46 @@ } } }, - setup: _.debounce(function(input) { + setup: function(input) { // Add GFM auto-completion to all input fields, that accept GFM input. this.input = input || $('.js-gfm-input'); - // destroy previous instances - this.destroyAtWho(); - // set up instances - this.setupAtWho(); - - if (this.dataSource && !this.dataLoading && !this.cachedData) { - this.dataLoading = true; - return this.fetchData(this.dataSource) - .done((data) => { - this.dataLoading = false; - this.loadData(data); - }); - }; - - if (this.cachedData != null) { - return this.loadData(this.cachedData); - } - }, 1000), - setupAtWho: function() { + this.setupLifecycle(); + }, + setupLifecycle() { + this.input.each((i, input) => { + const $input = $(input); + $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); + }); + }, + setupAtWho: function($input) { // Emoji - this.input.atwho({ + $input.atwho({ at: ':', - displayTpl: (function(_this) { - return function(value) { - if (value.path != null) { - return _this.Emoji.template; - } else { - return _this.Loading.template; - } - }; - })(this), + displayTpl: function(value) { + return value.path != null ? this.Emoji.template : this.Loading.template; + }.bind(this), insertTpl: ':${name}:', - data: ['loading'], startWithSpace: false, skipSpecialCharacterTest: true, + data: this.defaultLoadingData, callbacks: { sorter: this.DefaultOptions.sorter, - filter: this.DefaultOptions.filter, beforeInsert: this.DefaultOptions.beforeInsert, - matcher: this.DefaultOptions.matcher + filter: this.DefaultOptions.filter } }); // Team Members - this.input.atwho({ + $input.atwho({ at: '@', - displayTpl: (function(_this) { - return function(value) { - if (value.username != null) { - return _this.Members.template; - } else { - return _this.Loading.template; - } - }; - })(this), + displayTpl: function(value) { + return value.username != null ? this.Members.template : this.Loading.template; + }.bind(this), insertTpl: '${atwho-at}${username}', searchKey: 'search', - data: ['loading'], startWithSpace: false, alwaysHighlightFirst: true, skipSpecialCharacterTest: true, + data: this.defaultLoadingData, callbacks: { sorter: this.DefaultOptions.sorter, filter: this.DefaultOptions.filter, @@ -178,20 +162,14 @@ } } }); - this.input.atwho({ + $input.atwho({ at: '#', alias: 'issues', searchKey: 'search', - displayTpl: (function(_this) { - return function(value) { - if (value.title != null) { - return _this.Issues.template; - } else { - return _this.Loading.template; - } - }; - })(this), - data: ['loading'], + displayTpl: function(value) { + return value.title != null ? this.Issues.template : this.Loading.template; + }.bind(this), + data: this.defaultLoadingData, insertTpl: '${atwho-at}${id}', startWithSpace: false, callbacks: { @@ -213,26 +191,21 @@ } } }); - this.input.atwho({ + $input.atwho({ at: '%', alias: 'milestones', searchKey: 'search', - displayTpl: (function(_this) { - return function(value) { - if (value.title != null) { - return _this.Milestones.template; - } else { - return _this.Loading.template; - } - }; - })(this), insertTpl: '${atwho-at}${title}', - data: ['loading'], + displayTpl: function(value) { + return value.title != null ? this.Milestones.template : this.Loading.template; + }.bind(this), startWithSpace: false, + data: this.defaultLoadingData, callbacks: { matcher: this.DefaultOptions.matcher, sorter: this.DefaultOptions.sorter, beforeInsert: this.DefaultOptions.beforeInsert, + filter: this.DefaultOptions.filter, beforeSave: function(milestones) { return $.map(milestones, function(m) { if (m.title == null) { @@ -247,21 +220,15 @@ } } }); - this.input.atwho({ + $input.atwho({ at: '!', alias: 'mergerequests', searchKey: 'search', - displayTpl: (function(_this) { - return function(value) { - if (value.title != null) { - return _this.Issues.template; - } else { - return _this.Loading.template; - } - }; - })(this), - data: ['loading'], startWithSpace: false, + displayTpl: function(value) { + return value.title != null ? this.Issues.template : this.Loading.template; + }.bind(this), + data: this.defaultLoadingData, insertTpl: '${atwho-at}${id}', callbacks: { sorter: this.DefaultOptions.sorter, @@ -282,18 +249,31 @@ } } }); - this.input.atwho({ + $input.atwho({ at: '~', alias: 'labels', searchKey: 'search', - displayTpl: this.Labels.template, + data: this.defaultLoadingData, + displayTpl: function(value) { + return this.isLoading(value) ? this.Loading.template : this.Labels.template; + }.bind(this), insertTpl: '${atwho-at}${title}', startWithSpace: false, callbacks: { matcher: this.DefaultOptions.matcher, sorter: this.DefaultOptions.sorter, beforeInsert: this.DefaultOptions.beforeInsert, + filter: this.DefaultOptions.filter, beforeSave: function(merges) { + if (gl.GfmAutoComplete.isLoading(merges)) return merges; + var sanitizeLabelTitle; + sanitizeLabelTitle = function(title) { + if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) { + return "\"" + (sanitize(title)) + "\""; + } else { + return sanitize(title); + } + }; return $.map(merges, function(m) { return { title: sanitize(m.title), @@ -305,12 +285,14 @@ } }); // We don't instantiate the slash commands autocomplete for note and issue/MR edit forms - this.input.filter('[data-supports-slash-commands="true"]').atwho({ + $input.filter('[data-supports-slash-commands="true"]').atwho({ at: '/', alias: 'commands', searchKey: 'search', skipSpecialCharacterTest: true, + data: this.defaultLoadingData, displayTpl: function(value) { + if (this.isLoading(value)) return this.Loading.template; var tpl = '
  • /${name}'; if (value.aliases.length > 0) { tpl += ' (or /<%- aliases.join(", /") %>)'; @@ -323,7 +305,7 @@ } tpl += '
  • '; return _.template(tpl)(value); - }, + }.bind(this), insertTpl: function(value) { var tpl = "/${name} "; var reference_prefix = null; @@ -341,6 +323,7 @@ filter: this.DefaultOptions.filter, beforeInsert: this.DefaultOptions.beforeInsert, beforeSave: function(commands) { + if (gl.GfmAutoComplete.isLoading(commands)) return commands; return $.map(commands, function(c) { var search = c.name; if (c.aliases.length > 0) { @@ -368,32 +351,40 @@ }); return; }, - destroyAtWho: function() { - return this.input.atwho('destroy'); + fetchData: function($input, at) { + if (this.isLoadingData[at]) return; + this.isLoadingData[at] = true; + if (this.cachedData[at]) { + this.loadData($input, at, this.cachedData[at]); + } else { + $.getJSON(this.dataSources[this.atTypeMap[at]], (data) => { + this.loadData($input, at, data); + }).fail(() => { this.isLoadingData[at] = false; }); + } }, - fetchData: function(dataSource) { - return $.getJSON(dataSource); - }, - loadData: function(data) { - this.cachedData = data; - this.dataLoaded = true; - // load members - this.input.atwho('load', '@', data.members); - // load issues - this.input.atwho('load', 'issues', data.issues); - // load milestones - this.input.atwho('load', 'milestones', data.milestones); - // load merge requests - this.input.atwho('load', 'mergerequests', data.mergerequests); - // load emojis - this.input.atwho('load', ':', data.emojis); - // load labels - this.input.atwho('load', '~', data.labels); - // load commands - this.input.atwho('load', '/', data.commands); + loadData: function($input, at, data) { + this.isLoadingData[at] = false; + this.cachedData[at] = data; + $input.atwho('load', at, data); // This trigger at.js again // otherwise we would be stuck with loading until the user types - return $(':focus').trigger('keyup'); + return $input.trigger('keyup'); + }, + isLoading(data) { + if (!data) return false; + if (Array.isArray(data)) data = data[0]; + return data === this.defaultLoadingData[0] || data.name === this.defaultLoadingData[0]; + }, + togglePreventSelection(isPrevented = !!this.setting.tabSelectsMatch) { + this.setting.tabSelectsMatch = !isPrevented; + this.setting.spaceSelectsMatch = !isPrevented; + const eventListenerAction = `${isPrevented ? 'add' : 'remove'}EventListener`; + this.$inputor[0][eventListenerAction]('keydown', gl.GfmAutoComplete.preventSpaceTabEnter); + }, + preventSpaceTabEnter(e) { + const key = e.which || e.keyCode; + const preventables = [9, 13, 32]; + if (preventables.indexOf(key) > -1) e.preventDefault(); } }; diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index 56a33eeaad5..7dc2d13e5d8 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -30,7 +30,7 @@ this.form.addClass('gfm-form'); // remove notify commit author checkbox for non-commit notes gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); - GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); + gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); new DropzoneInput(this.form); autosize(this.textarea); // form and textarea event listeners diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 2f3cad13cc0..1c4086517fe 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -19,7 +19,7 @@ this.renderWipExplanation = bind(this.renderWipExplanation, this); this.resetAutosave = bind(this.resetAutosave, this); this.handleSubmit = bind(this.handleSubmit, this); - GitLab.GfmAutoComplete.setup(); + gl.GfmAutoComplete.setup(); new UsersSelect(); new ZenMode(); this.titleField = this.form.find("input[name*='[title]']"); diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb new file mode 100644 index 00000000000..d9dfa534669 --- /dev/null +++ b/app/controllers/projects/autocomplete_sources_controller.rb @@ -0,0 +1,48 @@ +class Projects::AutocompleteSourcesController < Projects::ApplicationController + before_action :load_autocomplete_service, except: [:emojis, :members] + + def emojis + render json: Gitlab::AwardEmoji.urls + end + + def members + render json: ::Projects::ParticipantsService.new(@project, current_user).execute(noteable) + end + + def issues + render json: @autocomplete_service.issues + end + + def merge_requests + render json: @autocomplete_service.merge_requests + end + + def labels + render json: @autocomplete_service.labels + end + + def milestones + render json: @autocomplete_service.milestones + end + + def commands + render json: @autocomplete_service.commands(noteable, params[:type]) + end + + private + + def load_autocomplete_service + @autocomplete_service = ::Projects::AutocompleteService.new(@project, current_user) + end + + def noteable + case params[:type] + when 'Issue' + IssuesFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:type_id]) + when 'MergeRequest' + MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:type_id]) + when 'Commit' + @project.commit(params[:type_id]) + end + end +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a8a18b4fa16..d5ee503c44c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -127,39 +127,6 @@ class ProjectsController < Projects::ApplicationController redirect_to edit_project_path(@project), alert: ex.message end - def autocomplete_sources - noteable = - case params[:type] - when 'Issue' - IssuesFinder.new(current_user, project_id: @project.id). - execute.find_by(iid: params[:type_id]) - when 'MergeRequest' - MergeRequestsFinder.new(current_user, project_id: @project.id). - execute.find_by(iid: params[:type_id]) - when 'Commit' - @project.commit(params[:type_id]) - else - nil - end - - autocomplete = ::Projects::AutocompleteService.new(@project, current_user) - participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable) - - @suggestions = { - emojis: Gitlab::AwardEmoji.urls, - issues: autocomplete.issues, - milestones: autocomplete.milestones, - mergerequests: autocomplete.merge_requests, - labels: autocomplete.labels, - members: participants, - commands: autocomplete.commands(noteable, params[:type]) - } - - respond_to do |format| - format.json { render json: @suggestions } - end - end - def new_issue_address return render_404 unless Gitlab::IncomingEmail.supports_issue_creation? diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index e138ebab018..3daa1e90a8c 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -3,6 +3,14 @@ - if project :javascript - GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_type, type_id: params[:id])}" - GitLab.GfmAutoComplete.cachedData = undefined; - GitLab.GfmAutoComplete.setup(); + gl.GfmAutoComplete.dataSources = { + emojis: "#{emojis_namespace_project_autocomplete_sources_path(project.namespace, project)}", + members: "#{members_namespace_project_autocomplete_sources_path(project.namespace, project, type: noteable_type, type_id: params[:id])}", + issues: "#{issues_namespace_project_autocomplete_sources_path(project.namespace, project)}", + mergeRequests: "#{merge_requests_namespace_project_autocomplete_sources_path(project.namespace, project)}", + labels: "#{labels_namespace_project_autocomplete_sources_path(project.namespace, project)}", + milestones: "#{milestones_namespace_project_autocomplete_sources_path(project.namespace, project)}", + commands: "#{commands_namespace_project_autocomplete_sources_path(project.namespace, project, type: noteable_type, type_id: params[:id])}" + }; + + gl.GfmAutoComplete.setup(); diff --git a/changelogs/unreleased/18435-autocomplete-is-not-performant.yml b/changelogs/unreleased/18435-autocomplete-is-not-performant.yml new file mode 100644 index 00000000000..019c55e27dc --- /dev/null +++ b/changelogs/unreleased/18435-autocomplete-is-not-performant.yml @@ -0,0 +1,4 @@ +--- +title: Made comment autocomplete more performant and removed some loading bugs +merge_request: 6856 +author: diff --git a/config/routes/project.rb b/config/routes/project.rb index 0754f0ec3b0..e17d6bae10c 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -11,6 +11,18 @@ constraints(ProjectUrlConstrainer.new) do module: :projects, as: :project) do + resources :autocomplete_sources, only: [] do + collection do + get 'emojis' + get 'members' + get 'issues' + get 'merge_requests' + get 'labels' + get 'milestones' + get 'commands' + end + end + # # Templates # @@ -316,7 +328,6 @@ constraints(ProjectUrlConstrainer.new) do post :remove_export post :generate_new_export get :download_export - get :autocomplete_sources get :activity get :refs put :new_issue_address diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index a78a1c9c890..c2545b0c259 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' feature 'Member autocomplete', feature: true do + include WaitForAjax + let(:project) { create(:project, :public) } let(:user) { create(:user) } let(:participant) { create(:user) } @@ -79,11 +81,10 @@ feature 'Member autocomplete', feature: true do end def open_member_suggestions - sleep 1 page.within('.new-note') do - sleep 1 - find('#note_note').native.send_keys('@') + find('#note_note').send_keys('@') end + wait_for_ajax end def visit_issue(project, issue) diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb index 1921ea6d8ae..dd9622f16a0 100644 --- a/spec/features/projects/gfm_autocomplete_load_spec.rb +++ b/spec/features/projects/gfm_autocomplete_load_spec.rb @@ -10,12 +10,12 @@ describe 'GFM autocomplete loading', feature: true, js: true do end it 'does not load on project#show' do - expect(evaluate_script('GitLab.GfmAutoComplete.dataSource')).to eq('') + expect(evaluate_script('gl.GfmAutoComplete.dataSources')).to eq({}) end it 'loads on new issue page' do visit new_namespace_project_issue_path(project.namespace, project) - expect(evaluate_script('GitLab.GfmAutoComplete.dataSource')).not_to eq('') + expect(evaluate_script('gl.GfmAutoComplete.dataSources')).not_to eq({}) end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index b6e7da841b1..77549db2927 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -80,10 +80,6 @@ describe 'project routing' do expect(get('/gitlab/gitlabhq/edit')).to route_to('projects#edit', namespace_id: 'gitlab', id: 'gitlabhq') end - it 'to #autocomplete_sources' do - expect(get('/gitlab/gitlabhq/autocomplete_sources')).to route_to('projects#autocomplete_sources', namespace_id: 'gitlab', id: 'gitlabhq') - end - describe 'to #show' do context 'regular name' do it { expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq') } @@ -117,6 +113,21 @@ describe 'project routing' do end end + # emojis_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/emojis(.:format) projects/autocomplete_sources#emojis + # members_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/members(.:format) projects/autocomplete_sources#members + # issues_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/issues(.:format) projects/autocomplete_sources#issues + # merge_requests_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/merge_requests(.:format) projects/autocomplete_sources#merge_requests + # labels_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/labels(.:format) projects/autocomplete_sources#labels + # milestones_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/milestones(.:format) projects/autocomplete_sources#milestones + # commands_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/commands(.:format) projects/autocomplete_sources#commands + describe Projects::AutocompleteSourcesController, 'routing' do + [:emojis, :members, :issues, :merge_requests, :labels, :milestones, :commands].each do |action| + it "to ##{action}" do + expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq') + end + end + end + # pages_project_wikis GET /:project_id/wikis/pages(.:format) projects/wikis#pages # history_project_wiki GET /:project_id/wikis/:id/history(.:format) projects/wikis#history # project_wikis POST /:project_id/wikis(.:format) projects/wikis#create From bcc09ca76098e8e12b0a42454920e1d4df6434c2 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 15 Dec 2016 23:37:22 +0800 Subject: [PATCH 129/386] Just use YAML.safe_load and assume the format should be correct since it's already passing the validation anyway. Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20076187 --- lib/gitlab/serialize/yaml_variables.rb | 26 +++---------------- .../gitlab/serialize/yaml_variables_spec.rb | 21 --------------- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/lib/gitlab/serialize/yaml_variables.rb b/lib/gitlab/serialize/yaml_variables.rb index 68ca50ed60e..db1e7641c74 100644 --- a/lib/gitlab/serialize/yaml_variables.rb +++ b/lib/gitlab/serialize/yaml_variables.rb @@ -10,36 +10,16 @@ module Gitlab def load(string) return unless string - object = YAML.load(string) + object = YAML.safe_load(string, [Symbol]) - # We don't need to verify the object once we're using SafeYAML - if YamlVariables.verify_object(object) - YamlVariables.convert_object(object) - else - [] - end + object.map(&YamlVariables.method(:convert_key_value_to_string)) end def dump(object) YAML.dump(object) end - def verify_object(object) - YamlVariables.verify_type(object, Array) && - object.all? { |obj| YamlVariables.verify_type(obj, Hash) } - end - - # We use three ways to check if the class is exactly the one we want, - # rather than some subclass or duck typing class. - def verify_type(object, klass) - object.kind_of?(klass) && - object.class == klass && - klass === object - end - - def convert_object(object) - object.map(&YamlVariables.method(:convert_key_value_to_string)) - end + private def convert_key_value_to_string(variable) variable[:key] = variable[:key].to_s diff --git a/spec/lib/gitlab/serialize/yaml_variables_spec.rb b/spec/lib/gitlab/serialize/yaml_variables_spec.rb index 41aea95dfdb..6d74f8c44d6 100644 --- a/spec/lib/gitlab/serialize/yaml_variables_spec.rb +++ b/spec/lib/gitlab/serialize/yaml_variables_spec.rb @@ -16,25 +16,4 @@ describe Gitlab::Serialize::YamlVariables do { key: 'key', value: 'value', public: true }, { key: 'wee', value: '1', public: false }]) end - - context 'with a subclass of Array' do - let(:object) do - Kaminari::PaginatableArray.new << 'I am evil' - end - - it 'ignores it' do - is_expected.to eq([]) - end - end - - context 'with the array containing subclasses of Hash' do - let(:object) do - [ActiveSupport::OrderedOptions.new( - key: 'key', value: 'value', public: true)] - end - - it 'ignores it' do - is_expected.to eq([]) - end - end end From 0a5427d7c2679640c816cdfea1ffc30a3e7b4dd8 Mon Sep 17 00:00:00 2001 From: twonegatives Date: Tue, 13 Dec 2016 01:13:14 +0300 Subject: [PATCH 130/386] Made Ci::Builds to have same ref as Ci::Pipeline in dev fixtures --- changelogs/unreleased/change_development_build_fixtures.yml | 4 ++++ db/fixtures/development/14_pipelines.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/change_development_build_fixtures.yml diff --git a/changelogs/unreleased/change_development_build_fixtures.yml b/changelogs/unreleased/change_development_build_fixtures.yml new file mode 100644 index 00000000000..b5dc3792745 --- /dev/null +++ b/changelogs/unreleased/change_development_build_fixtures.yml @@ -0,0 +1,4 @@ +--- +title: Ci::Builds have same ref as Ci::Pipeline in dev fixtures +merge_request: +author: twonegatives diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb index 08ad3097d34..19e001854d2 100644 --- a/db/fixtures/development/14_pipelines.rb +++ b/db/fixtures/development/14_pipelines.rb @@ -115,7 +115,7 @@ class Gitlab::Seeder::Pipelines def job_attributes(pipeline, opts) { name: 'test build', stage: 'test', stage_idx: stage_index(opts[:stage]), - ref: 'master', tag: false, user: build_user, project: @project, pipeline: pipeline, + ref: pipeline.ref, tag: false, user: build_user, project: @project, pipeline: pipeline, created_at: Time.now, updated_at: Time.now }.merge(opts) end From 4ae28cb31d9f0915aac647e1befa61067b6932f1 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 17:30:49 +0000 Subject: [PATCH 131/386] Changes after review --- app/assets/stylesheets/pages/pipelines.scss | 10 +- app/views/ci/status/_graph_badge.html.haml | 5 +- .../projects/pipelines/pipeline_spec.rb | 119 +++++++++++------- 3 files changed, 76 insertions(+), 58 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index d3f39570f11..be22e7bdc79 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -645,14 +645,6 @@ margin-bottom: 0; line-height: 1.2; } - - li:first-child { - padding-top: 6px; - } - - li:last-child { - padding-bottom: 6px; - } } .dropdown-build { @@ -741,4 +733,4 @@ .ci-play-icon { padding: 5px 5px 5px 7px; } -} \ No newline at end of file +} diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml index a7e8544e7d4..df1b763e67c 100644 --- a/app/views/ci/status/_graph_badge.html.haml +++ b/app/views/ci/status/_graph_badge.html.haml @@ -3,9 +3,10 @@ - subject = local_assigns.fetch(:subject) - status = subject.detailed_status(current_user) - klass = "ci-status-icon ci-status-icon-#{status}" +- tooltip_title = "#{subject.name} - #{status.label}" - if status.has_details? - = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status}" } do + = link_to status.details_path, data: { toggle: 'tooltip', title: tooltip_title } do %span{ class: klass }= custom_icon(status.icon) .ci-status-text= subject.name - else @@ -14,6 +15,6 @@ - if status.has_action? = link_to status.action_path, method: status.action_method, - title: "#{subject.name}: #{status.action_title}", class: 'ci-action-icon-container' do + title: tooltip_title, class: 'ci-action-icon-container' do %i.ci-action-icon-wrapper = icon(status.action_icon, class: status.action_class) diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 80a596d34c9..9d43d264bdf 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -39,62 +39,87 @@ describe "Pipelines", feature: true, js: true do end context 'pipeline graph' do - it 'shows a running icon and a cancel action for the running build' do - title = "#{@running.name} - #{@running.status}" + context 'running build' do + it 'shows a running icon and a cancel action for the running build' do + page.within('a[data-title="deploy - running"]') do + expect(page).to have_selector('.ci-status-icon-running') + expect(page).to have_content('deploy') + end - page.within("a[data-title='#{title}']") do - expect(page).to have_selector('.ci-status-icon-running') - expect(page).to have_content('deploy') + page.within('a[data-title="deploy - running"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-ban') + end end - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-ban') - end + it 'should be possible to cancel the running build' do + find('a[data-title="deploy - running"] + .ci-action-icon-container').trigger('click') + expect(page).not_to have_content('Cancel running') + end end - it 'shows the success icon and a retry action for the successfull build' do - title = "#{@success.name} - #{@success.status}" + context 'success build' do + it 'shows the success icon and a retry action for the successfull build' do + page.within('a[data-title="build - passed"]') do + expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_content('build') + end - page.within("a[data-title='#{title}']") do + page.within('a[data-title="build - passed"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + end + end + + it 'should be possible to retry the success build' do + find('a[data-title="build - passed"] + .ci-action-icon-container').trigger('click') + + expect(page).not_to have_content('Retry build') + end + end + + context 'failed build' do + it 'shows the failed icon and a retry action for the failed build' do + page.within('a[data-title="test - failed"]') do + expect(page).to have_selector('.ci-status-icon-failed') + expect(page).to have_content('test') + end + + page.within('a[data-title="test - failed"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + end + end + + it 'should be possible to retry the failed build' do + find('a[data-title="test - failed"] + .ci-action-icon-container').trigger('click') + + expect(page).not_to have_content('Retry build') + end + end + + context 'manual build' do + it 'shows the skipped icon and a play action for the manual build' do + page.within('a[data-title="manual build - manual play action"]') do + expect(page).to have_selector('.ci-status-icon-skipped') + expect(page).to have_content('manual') + end + + page.within('a[data-title="manual build - manual play action"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-play') + end + end + + it 'should be possible to play the manual build' do + find('a[data-title="manual build - manual play action"] + .ci-action-icon-container').trigger('click') + + expect(page).not_to have_content('Play build') + end + end + + context 'external build' do + it 'shows the success icon and the generic comit status build' do expect(page).to have_selector('.ci-status-icon-success') - expect(page).to have_content('build') + expect(page).to have_content('jenkins') end - - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-refresh') - end - end - - it 'shows the failed icon and a retry action for the failed build' do - title = "#{@failed.name} - #{@failed.status}" - - page.within("a[data-title='#{title}']") do - expect(page).to have_selector('.ci-status-icon-failed') - expect(page).to have_content('test') - end - - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-refresh') - end - end - - it 'shows the skipped icon and a play action for the manual build' do - title = "#{@manual.name} - #{@manual.status}" - - page.within("a[data-title='#{title}']") do - expect(page).to have_selector('.ci-status-icon-skipped') - expect(page).to have_content('manual') - end - - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-play') - end - end - - it 'shows the success icon and the generic comit status build' do - expect(page).to have_selector('.ci-status-icon-success') - expect(page).to have_content('jenkins') end end From a5ccaded656fb215f1f8d503b88c8f28bf90ce68 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 6 Dec 2016 15:59:03 -0200 Subject: [PATCH 132/386] Change SlackService to SlackNotificationsService --- app/models/project.rb | 4 +- .../chat_notification_service.rb | 147 ++++++++++++++++++ app/models/project_services/chat_service.rb | 143 +---------------- ....rb => mattermost_notification_service.rb} | 6 +- .../mattermost_slash_commands_service.rb | 12 +- ...rvice.rb => slack_notification_service.rb} | 4 +- app/models/service.rb | 4 +- ...006143943_move_slack_service_to_webhook.rb | 6 +- ...k_service_to_slack_notification_service.rb | 14 ++ db/schema.rb | 2 +- lib/api/services.rb | 10 +- .../import_export/test_project_export.tar.gz | Bin 681774 -> 679415 bytes .../projects/services/slack_service_spec.rb | 4 +- spec/lib/gitlab/import_export/all_models.yml | 4 +- .../chat_notification_service_spec.rb | 12 ++ .../project_services/chat_service_spec.rb | 11 +- ...> mattermost_notification_service_spec.rb} | 2 +- ....rb => slack_notification_service_spec.rb} | 2 +- spec/models/project_spec.rb | 4 +- 19 files changed, 223 insertions(+), 168 deletions(-) create mode 100644 app/models/project_services/chat_notification_service.rb rename app/models/project_services/{mattermost_service.rb => mattermost_notification_service.rb} (87%) rename app/models/project_services/{slack_service.rb => slack_notification_service.rb} (92%) create mode 100644 db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb create mode 100644 spec/models/project_services/chat_notification_service_spec.rb rename spec/models/project_services/{slack_service_spec.rb => mattermost_notification_service_spec.rb} (54%) rename spec/models/project_services/{mattermost_service_spec.rb => slack_notification_service_spec.rb} (56%) diff --git a/app/models/project.rb b/app/models/project.rb index 19c2d24212d..5d092ca42c2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -95,8 +95,8 @@ class Project < ActiveRecord::Base has_one :asana_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy has_one :mattermost_slash_commands_service, dependent: :destroy - has_one :mattermost_service, dependent: :destroy - has_one :slack_service, dependent: :destroy + has_one :mattermost_notification_service, dependent: :destroy + has_one :slack_notification_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy has_one :teamcity_service, dependent: :destroy diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb new file mode 100644 index 00000000000..b0556987721 --- /dev/null +++ b/app/models/project_services/chat_notification_service.rb @@ -0,0 +1,147 @@ +# Base class for Chat notifications services +# This class is not meant to be used directly, but only to inherit from. +class ChatNotificationService < Service + include ChatMessage + + default_value_for :category, 'chat' + + prop_accessor :webhook, :username, :channel + boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines + + validates :webhook, presence: true, url: true, if: :activated? + + def initialize_properties + # Custom serialized properties initialization + self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } + + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + self.notify_only_broken_pipelines = true + end + end + + def can_test? + valid? + end + + def supported_events + %w[push issue confidential_issue merge_request note tag_push + build pipeline wiki_page] + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + return unless webhook.present? + + object_kind = data[:object_kind] + + data = data.merge( + project_url: project_url, + project_name: project_name + ) + + # WebHook events often have an 'update' event that follows a 'open' or + # 'close' action. Ignore update events for now to prevent duplicate + # messages from arriving. + + message = get_message(object_kind, data) + + return false unless message + + opt = {} + + opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel + opt[:username] = username if username + notifier = Slack::Notifier.new(webhook, opt) + notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) + + true + end + + def event_channel_names + supported_events.map { |event| event_channel_name(event) } + end + + def event_field(event) + fields.find { |field| field[:name] == event_channel_name(event) } + end + + def global_fields + fields.reject { |field| field[:name].end_with?('channel') } + end + + def default_channel + raise NotImplementedError + end + + private + + def get_message(object_kind, data) + case object_kind + when "push", "tag_push" + PushMessage.new(data) + when "issue" + IssueMessage.new(data) unless is_update?(data) + when "merge_request" + MergeMessage.new(data) unless is_update?(data) + when "note" + NoteMessage.new(data) + when "build" + BuildMessage.new(data) if should_build_be_notified?(data) + when "pipeline" + PipelineMessage.new(data) if should_pipeline_be_notified?(data) + when "wiki_page" + WikiPageMessage.new(data) + end + end + + def get_channel_field(event) + field_name = event_channel_name(event) + self.public_send(field_name) + end + + def build_event_channels + supported_events.reduce([]) do |channels, event| + channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel } + end + end + + def event_channel_name(event) + "#{event}_channel" + end + + def project_name + project.name_with_namespace.gsub(/\s/, '') + end + + def project_url + project.web_url + end + + def is_update?(data) + data[:object_attributes][:action] == 'update' + end + + def should_build_be_notified?(data) + case data[:commit][:status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end + + def should_pipeline_be_notified?(data) + case data[:object_attributes][:status] + when 'success' + !notify_only_broken_pipelines? + when 'failed' + true + else + false + end + end +end diff --git a/app/models/project_services/chat_service.rb b/app/models/project_services/chat_service.rb index 8ac049ba939..574788462de 100644 --- a/app/models/project_services/chat_service.rb +++ b/app/models/project_services/chat_service.rb @@ -1,148 +1,21 @@ # Base class for Chat services -# This class is not meant to be used directly, but only to inherrit from. +# This class is not meant to be used directly, but only to inherit from. class ChatService < Service - include ChatMessage - default_value_for :category, 'chat' - prop_accessor :webhook, :username, :channel - boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines + has_many :chat_names, foreign_key: :service_id - validates :webhook, presence: true, url: true, if: :activated? - - def initialize_properties - # Custom serialized properties initialization - self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } - - if properties.nil? - self.properties = {} - self.notify_only_broken_builds = true - self.notify_only_broken_pipelines = true - end - end - - def can_test? - valid? + def valid_token?(token) + self.respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) end def supported_events - %w[push issue confidential_issue merge_request note tag_push - build pipeline wiki_page] + [] end - def execute(data) - return unless supported_events.include?(data[:object_kind]) - return unless webhook.present? - - object_kind = data[:object_kind] - - data = data.merge( - project_url: project_url, - project_name: project_name - ) - - # WebHook events often have an 'update' event that follows a 'open' or - # 'close' action. Ignore update events for now to prevent duplicate - # messages from arriving. - - message = get_message(object_kind, data) - - return false unless message - - opt = {} - - opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel - opt[:username] = username if username - - notifier = Slack::Notifier.new(webhook, opt) - notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) - - true - end - - def event_channel_names - supported_events.map { |event| event_channel_name(event) } - end - - def event_field(event) - fields.find { |field| field[:name] == event_channel_name(event) } - end - - def global_fields - fields.reject { |field| field[:name].end_with?('channel') } - end - - def default_channel + def trigger(params) raise NotImplementedError end - - private - - def get_message(object_kind, data) - case object_kind - when "push", "tag_push" - PushMessage.new(data) - when "issue" - IssueMessage.new(data) unless is_update?(data) - when "merge_request" - MergeMessage.new(data) unless is_update?(data) - when "note" - NoteMessage.new(data) - when "build" - BuildMessage.new(data) if should_build_be_notified?(data) - when "pipeline" - PipelineMessage.new(data) if should_pipeline_be_notified?(data) - when "wiki_page" - WikiPageMessage.new(data) - end - end - - def get_channel_field(event) - field_name = event_channel_name(event) - self.public_send(field_name) - end - - def build_event_channels - supported_events.reduce([]) do |channels, event| - channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel } - end - end - - def event_channel_name(event) - "#{event}_channel" - end - - def project_name - project.name_with_namespace.gsub(/\s/, '') - end - - def project_url - project.web_url - end - - def is_update?(data) - data[:object_attributes][:action] == 'update' - end - - def should_build_be_notified?(data) - case data[:commit][:status] - when 'success' - !notify_only_broken_builds? - when 'failed' - true - else - false - end - end - - def should_pipeline_be_notified?(data) - case data[:object_attributes][:status] - when 'success' - !notify_only_broken_pipelines? - when 'failed' - true - else - false - end - end end diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_notification_service.rb similarity index 87% rename from app/models/project_services/mattermost_service.rb rename to app/models/project_services/mattermost_notification_service.rb index 9d61c251a32..de18c4b1f00 100644 --- a/app/models/project_services/mattermost_service.rb +++ b/app/models/project_services/mattermost_notification_service.rb @@ -1,4 +1,4 @@ -class MattermostService < ChatService +class MattermostNotificationService < ChatNotificationService def title 'Mattermost notifications' end @@ -8,7 +8,7 @@ class MattermostService < ChatService end def to_param - 'mattermost' + 'mattermost_notification' end def help @@ -28,7 +28,7 @@ class MattermostService < ChatService def default_fields [ - { type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' }, + { type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' }, { type: 'text', name: 'username', placeholder: 'username' }, { type: 'checkbox', name: 'notify_only_broken_builds' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 3993dfbda17..33431f41dc2 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -1,18 +1,8 @@ -class MattermostSlashCommandsService < Service +class MattermostSlashCommandsService < ChatService include TriggersHelper prop_accessor :token - def valid_token?(token) - self.respond_to?(:token) && - self.token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) - end - - def supported_events - [] - end - def can_test? false end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_notification_service.rb similarity index 92% rename from app/models/project_services/slack_service.rb rename to app/models/project_services/slack_notification_service.rb index 0df1743c4ba..3cbf89efba4 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_notification_service.rb @@ -1,4 +1,4 @@ -class SlackService < ChatService +class SlackNotificationService < ChatNotificationService def title 'Slack notifications' end @@ -8,7 +8,7 @@ class SlackService < ChatService end def to_param - 'slack' + 'slack_notification' end def help diff --git a/app/models/service.rb b/app/models/service.rb index 8e58f2a1925..0bbab078cf6 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -220,8 +220,8 @@ class Service < ActiveRecord::Base pivotaltracker pushover redmine - mattermost - slack + mattermost_notification + slack_notification teamcity ] end diff --git a/db/migrate/20141006143943_move_slack_service_to_webhook.rb b/db/migrate/20141006143943_move_slack_service_to_webhook.rb index 8cb120f7007..42e88d6d6e3 100644 --- a/db/migrate/20141006143943_move_slack_service_to_webhook.rb +++ b/db/migrate/20141006143943_move_slack_service_to_webhook.rb @@ -1,7 +1,11 @@ # rubocop:disable all class MoveSlackServiceToWebhook < ActiveRecord::Migration + + DOWNTIME = true + DOWNTIME_REASON = 'Move old fields "token" and "subdomain" to one single field "webhook"' + def change - SlackService.all.each do |slack_service| + SlackNotificationService.all.each do |slack_service| if ["token", "subdomain"].all? { |property| slack_service.properties.key? property } token = slack_service.properties['token'] subdomain = slack_service.properties['subdomain'] diff --git a/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb b/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb new file mode 100644 index 00000000000..a7278d7b5a6 --- /dev/null +++ b/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb @@ -0,0 +1,14 @@ +class ChangeSlackServiceToSlackNotificationService < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + DOWNTIME_REASON = 'Rename SlackService to SlackNotificationService' + + def up + execute("UPDATE services SET type = 'SlackNotificationService' WHERE type = 'SlackService'") + end + + def down + execute("UPDATE services SET type = 'SlackService' WHERE type = 'SlackNotificationService'") + end +end diff --git a/db/schema.rb b/db/schema.rb index 4711b7873af..5bd4ef1f21b 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: 20161212142807) do +ActiveRecord::Schema.define(version: 20161213172958) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lib/api/services.rb b/lib/api/services.rb index b1e072b4f47..59232c84c24 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -473,7 +473,7 @@ module API desc: 'The description of the tracker' } ], - 'slack' => [ + 'slack-notification' => [ { required: true, name: :webhook, @@ -493,6 +493,14 @@ module API desc: 'The channel name' } ], + 'mattermost-notification' => [ + { + required: true, + name: :webhook, + type: String, + desc: 'The Mattermost webhook. e.g. http://mattermost_host/hooks/...' + } + ], 'teamcity' => [ { required: true, diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz index bfe59bdb90e75cb869cab6aa86cb9dc81ac5e225..d3165d07d7b9a37d532a6bf97c7d74de7d32098b 100644 GIT binary patch delta 420246 zcmaf3<9l9Tu#Iiowr$&XV_S_*?4&W?n2nt@ZfvVbW7}+_+uyx^!2P;E%(K^-S!>qp zIdwDi&t3EYX&fAw?~A$~803YqO9k^UZGh)*k)67FI1W-gEh*EJ702giH^l*{8}#c* z`2H>h?ziijjdg*)4~Nas!PL~HKe9MhQ7plynFzjNL!lZr)cYJ%nKV6?MokEvMB^SV zy$01oHAUUK&C}L#u0QAN)(;nt!JNTYkL!-}nd~*-{KvDw`HvNo*X(N5t||jMeK6Mk}#N z&ga>_?lVc(=LhW*$vf3X-|NnGpy^BBrS9iT!ngOEZGY142UXzN#^vYR`L@6BHEqyk zoM}+D(93H*u($njOe*qL1OTrt1FyY-FRQ>T)ou>yYtOddz2W`2?DogVub{s^+n+hW z-AG^o@QzvVD!uUe=Qwfe??8bG@F&Ej_4D~`Tjayw9ar>DSM>4sw~zi;(zgefD8#6@ z!_3}iM%Ex3?@vS0phL**_q%meA<2*DsE@}{;3?2FVEa-R+~4G6`|auHn0H=xex?EY`OPwuCBlAlQP zr#M!5a;DlVX=%UZ=TFvpTpMcVIBr1StF+Qa*ZSAIctQrsnaN3`=CgK0Zm)-qt+Lv$ z`aA+w!mPjNL8skwB^i9m>SRWRiS5mk&6Eo%{FQP(Ha`bs+d;;+EVFZ+d{naSxyJg1 zRl}J!*r5HYJmWuyHmtK5r2|gCma>9A_u_-KpnfhU*A2Mx$|j%pdqK-RpMC@0Ck=f9 z5HnTQw5ADIPlb8opy}c4@(7&Uy(W6e6kkY1ps%u~UfaS)_wQ7GdD_ambYI*C#R$B} z{T<7+873I?ITShDfrOYh~fF* zKN%Y=Q{N`$ad;BHRh;eh%odf-av(q&<@PPH6O66kHmSWOuT$)X2@7X{KrnLh!xos${A>#?ida<*6#<}n@F6_aKg+_ z>~AEt`KqmZoO45euK#jO<~uLFLv|ZGgb~KpUL3fic)+?kqvu@KwANJZB|mB3XwTPB zR{dztCz173n1>EHO{xm5S6_cp%rcg%xoQv_I%ii%(!_{GPu8+I}wlAxHUlZ zZp{GMGq+Z7v+^4Lir4`D9YH8N#gy~77&)m94)EHnR8e>!aU%L?sjEc%#aJc=7dD5w zUOI?Rotm7Y$xae9G5)CQ#L)Dj=2=e9R^}joMmd8p4exvFGi=kdz1pwn+hHxlu~wYU z5K%)kl9*!h^#K>HS3L9P;`ZW7lC&3)SeiCd!OZc{vS33V7Y7p9KQCO*byWCa)K6sO zMR;HyP}?aSk#v+5U+DVor+-zjpge!+*Ew4?zKlazloheB3XPi78a@i=wW(xgpw1Y$NM+?917>Cb(44))e{)Kryb} zm>Hftq!N#w5dh0iG+dkxD%jeJ83~e_<9SHalg*u{Tt&JxnG88`gpm*N z-*}if;IIQ*CA8!pGUvMo)**`k$CnvYIDmo|9s_bizN7Y5(l{zsl1=u;fpzAu*YM~c z>N)Dl+o1V0dwGWp#Ra?H8LDh_wLOav4AkMjoqtlr4FhCBOixQ(JKMYt?qc>@moA@R zbDLVSgu!VpPDbc7!2A!akc(-36Q-uG$t(-D=+bWZ%o3t3cWiR%!RDePL26R+=!?zKdBU4}t^2bk3uY{r%4G9y%`PwYP(QzAx|bsyPeDP4fFm@m@=Amx z<*1qALIYd3ba2!NQbMTx>_>n|t>! z9(@Cagv{Z;Rp*8Edz;~i@2&e9qk+L15p%Af)&uItT$2R3#^WEi*T`-u_swR0=XNBO7g^VKsu&Ev!!U!gau7t%%*q zd&0tu7wwYKe`!gCm8puLS*lyYf{Ol15=wKSdvngEz$GPZ85}EwPhIHQ0gt_+cB|z9 zs{_P7-qU8MDKTSVi!%>{Q!7`v;ootD9`Zi2$?S@KO;it6%*@#_n}ui2gz64Pc?+{9 zM4yfA-WEaHzh560^-G|4doYnF8ecY9Tw#u;f}SsIs&TmDy0Z=KJp~il*#;4%#5VqB zeNgV2jDhi)j)CbICrIjW1Lfvp_Q0h6t_0jdcFQYGl}M3De(UpK;|Yfnp^Z8SkXbHM zxR!X?d#1|HZULDqr*<`4LeX!9Gkg4Xcb5r;^Jn}d865yA zdUL^1%0C;ZAGjo2#m5WHn&JqLrX zOFdhHs@Z(`Jfq>y{*FHF2mzb%N-g2FA5!7TXR8zmAc+@yjJbbBK(k_IE>ZHAVc0#d zgR`{sE3XtMqaTRmjnKc_?(9frL<*QtE^L5_Mc-P|9nY}Qip;$y?w%{BmxpxK<76Eo zkq32{;NW+3NH`CKb1TKCmi8+Acr%LVGw^JoOP7VJ9h;wwMQ|XpJA&!OrwQK(hj++g z+m{Puo(+{k=bjyKayw}miIZVO4rr(_Ym)(4J0E0Ak4ZR(#&BoFCztms)B_u5>!CGZ zNRk8jHk()l5~2ew6*jqoWdYG^d*lZ+TC4X$v{w)Ldn&C}uQ|eQU;)D6dEL2M(=O?z z#~W2|zrdF~4Wy4yVnCNtc;CxT<8vohAraB@@+EsFdJ3uAM^GS%R{10vqbhBd1U%aL zhZt>CtyP;5`|__YnE4WgTY5y}GS}}If@;WUm7nditC#G^{Td(_0e)GX` z3L_YCeo7Tm0mbVQRVlglI$)#t1{fFcfLMpep&r2EcxJDA!VlK zQiH3uU222k!82%4_#ld)qAt92lk1YHjF#;cj7)2YhI42%cXYg3 zQOHE8(55<)+=8ZW9T8i~rcyj-Q3oL`4Lv43ulN7~)kyZ77`2kf7Ae}28yK5T zfJG)o`_qm&QntTCE?|3AzTYL_)d5euGWTU928JQR{jsfgG6lv7+G)SfyPjPq^osxc zV<`t6dmD2}3msH*?>Gt$H81%vfyRjKyx5`U(*!w&*AYW&D5JX=E{aouKjVw0ArifX zAW{G-H81#3zQ5luq|>F-J#(MVq=Sl;vppC)`0Bc$ANuWUgTP{R3Q^V`Q%hk)=1o#d zUx2M6yKmkG&+4WdX}i926U>XVUk35Be;PD(nWSwxeW0sKC(>**zK!3V_3HzKYq4OS zVB)@1t=A=3#DNTUnZ`_aF<11DLE|<8uudq==_ihggw_T)?wsFMIll??5v-}%zsRO- zbYvGign|mdY=F40_`4CAP$Cj07q3OSR26Ws94x70i1$=*_X8~Y1h#^EQV`0+?1MVv zMXdPi25Wi6OTKA5(1A7~;sU1Q%IyttahSG?Vr5!&Z3dl*9H-U2R6AV@(8gHFO(|qE zsdF6UE2hv>J>2ftOewruZ7P{LX6DE=G6z)?9YXy9%0OGn(sb?(GR+aT*gh8n-I^*g zA15_$NIwDIh^5eU9vE%Cexc!~ngr7dP=*`>-5mYY!PDDLP;U35=ItLP2pGXzRXcQh!kd-2=dPe) zY!@h+VR+V}vH>DlH20C1thALRi0S{!GOGVCq>CjauO}1RNQM|aWmA8T??ry-m3*<` zhdUNyG8F@BXdb|K|0NWB!fkHcoWC^UN^nO2g@|POnUns7PMo`4BfOR**?p^2Pu0aA zKlgcicW}7h#J(i5a|U5F%Nk}2NZJ$Gm@P#UM2jX`lBtR->xosd!4M8BJv#JB&CE@t z^~Cl(uEHg*1`~NX#tJMI#tdu|VeVJ^6y?WnOcO!|rT0))L2YBPHFS6G`QawcnIL39 z`}d2y60x34$G9Bao^`hguS4%A>9gY16~~WXB%*}v>h>6diXHGo-u-SGIZJwwQ5hOU z4DjK}gnw6WDnJ|@0r2?oM8k+BXFG`gj!ru?k&Fr)wR9Qc76U7h45#<2X5MIpa{c1o z`x#C%QgR7@ue4t2kWM!GrYSgE{&h}(d1a16;yCi}2myr}d5PsRi()O)irjkN^yvEB zxjQC9P0>Jbi}s&<8?vh9EQ5`hCRBkc&u<&{39fNp`xN0*wvMiNVBENV6UP zq`G73)r1Tub!x|u*Dk|dR`5G6V_U(@;MQrbcx9VMVuEn zd4(2;0v0V!2Tp(JCw>lB`8U_d6e>GEU1wG9NWR*iDP&8j+#Fxr<@K;zGEaSrf~UbDEzG zxd$&n2uEyEltqNjZ~BZb6>kqfID0&S6)cr7hpnSgnueF;8;4iNK03iAMo035!hN0J z84@^-m_=hz8s!R!3t^|7y+%FR;mkDNFzyl_HM9YhRXz^fFdsBzGPD)`Upvr&6*@Kv zOKgkM!WfGSAv6K^XgQUaf!!kJyQMG;@@Y+Oxq*9b3-YudI$Z}mzIQU<24P2N7iBm< zm$j6uYH>)vQL=h=vysN6hg0-TI1=Z=)m$Tv?2sm0PcnKwME7@wxc3Lt_pNB5_gLH- z$Hz^JQ?mVwZ{Xa79paW6i&TgIYntw(y}x!8b)nitc?9$MIPnH zGTgIf6hE=BF!jz&P1vMkhX=e%|8;Hux{IaN9TPi8 zG_5|`tJJ7$B)nhwsS=BhGyA6fkecO#E5Gg>}==6+m)^kgdM6C0ZyGwA1lh03Pl zm&Ejmq`wC&u=CJ`CX|?G+)Z*lk>hlNb(7gXK?yDIk%2#UGj3vO@ZZh6+Ovfbc)a(A zO3zc$1bNpnjRCcF%vbf(uXb20M`)mMxLw=)bouBK3gjD#+hcK(RmSU(dI3Wz5AZYqluy}CM$k^021;Vcow z?HP~O*r;$ORJ($baZOmEvwSv7u?QN4q2*xcfJa*aW1ndmVF>h4%tr}{GQj!@2P0ag zPE^<^1`Rh6JVEjcg!eQfKhxtg(*ujmS;wu@K~(F2K=TkjLM^zW`G@rHsY5v)KCKQW zFP@9kcVJQ(uQ4*2-JE-CvsThrbt@*JK+ZWqsq7#+MnBmd6XoJ-k&j~eg1eo3xy-)h z8M6@zIh)|QhwZ&=W`PcIaNoB8EWyC`?nUlReY+Jm!_rA&pk1e1(%vece7Xub1scT7 zaS-3Jaoep^vshE4vFo)K;}Pe6@FjA@^dzL}4A`#h5nSjY6BI775n~(VNDl4bL;ThG zr(06E`h;1IH=$e7$-x&KZe=dq44E0DCdjt$nXzL(Bt!)`P7Ph-JDJg0X& z3bRntXcdjKrF%n0iXbl~mWdiAR`@*`f`p%?MRvKg(O=t;IJn(XCfCSRh)ld$&h?tA z3E;+r@$d?iK^#4BHe&@K_(R$6ja2I2{2M`-R)+`Vp{l%Vbf!@y)5ZgjGBuROk}~RW zo+=iGloJv;lH4(AYQ7AFGbn349cpr|g=V96=vPk+=>2`#@%p{5Ab#1Q&%}5@%o-5m$(`Fra&WZUJIc?-2GHFxscOW{bD5R3><cbH6iq^^ZZ1yu{)A z&8dnIUz*Ktn#om$#Ea6Zh;7LF=eFXyBSmgZK8=xyuaY_GsoB4FAlT|d;O`0aEKCA< zNw4>+C-df>DK^SXFw?Pm6^IEtfp}Y4&Up=)l^dA!@0Wl1XZp&W@v=(6%l=uUs+~|! zRnyQs1@b{1%5aTwNtnKCQq+$5T5-_k$8YmT$C9dcy(86>_?K+Wm4x4Df#Uxg2EPnV zRUyqF-?+$i%;U-nN=0N;s^3L8|7lrMBlE&6kx9^ShKS}4j(2PRtpnm0Zq9I8Ej^}O zf~L3A61s8z&P%tC7?Y`*{rdlHYH6WZaTF@@<*~H7EW#!Q+H2FPmT^N~1xyfyYQb zDx2mhGrsPfB&@2%hqd7U%op3D zuArqpP+kXgT&DuM{RRG^TqM(3Yclo;u_} z+`rYJBwCx+Cfi``Oq4w|@GtdI+^+FxHI;3dk(b+8r{3Lb;0CT#D_P4^${d`Wlm|3Y zdXnMb%~d|i2esflU2BcqqN1oe5BJ0~wARQts!AgBw46C}Wadb$>WRe) z;^Led0M=OhWc$)yFJtJ7)4Z?Uw?9P%`&5ocNBQ`!;Tupy4t@BPUz(>Mrd>Hm9fVsimpm6 z(_1aj4&urqLC};Mx3(=#f4#_6$1%2JzI+We1!A_ip_Vv;BgSRL-W@+bTr1I2kn)*4 zOU#zkmk%S)os`%$NX4;wzhy>i8D4u$-?wl1eG|E^S_gH4?Reh{2lz{yDL7ou!IB&y z1X|yQa2jz7+uc&a`WpA29X9wtk*OgRUi*LBS-0(|YCw$q;_+8?bf7);5GxNs#aS!v3Ga^1^K#q^j+m<^FBRln(9$I=Y<@DXmrUvZR$6e`> zVVjL1-nB9shZ}8D-M{8)GFaox+W@a?>nrC(1Tb6~vH341*N=&EIx=pL83p}_0hgKk z>o0!2P_2q?z3hIBvC|~>buAo)#{e(keOaCbk!ljMi*>sPYEN~#+d_0;~a^{ zDmgd{vK;n;=srW_wtIk3!4}!Mv<(n$3;?tlJ#uL-`qZHTfH& zyfy|k#c;U=b?C%6A5eQuJF#zc@t&h#tjQN&VYo%7{$x-dy7A!kDpvlm1(3@7?LEyR6;enlSU5uEyb*;VJo}K3u!^-oA&|e*c?`c=iWvXSzPm7?=&_?Np zrB~8gDL^FYm*ylOoxCO42b8;OI^$P*ba@{s6Pn8MGW_4uzcV)LGC-e467Za|sC>;W zmC~o7vT$8R%j;=%U4e6`xtoWTY@e2j*e$~BX2JJOq!h@Z++$zXO;6Vne}=@ai}EU= z9M+A^^(nFc4-f&py^K=f6ka*kd@h?!YC{MkPWGWXnmCumvD_^{Z-NWOb*!sx1*V;O z+yqs-c4m#uDy$>yu4f^z5Ku<*VB5I*4^0C6jAnx>5w1qml(bSV<2l9tOen)w#a3LV zQ;)JgSoM?8ANkI@`^ME_mS5SteDr7qIDE@$%FdYLofeF9Rir44&|)~voy;`K`jE%_ zH3xHbc1Wp9=0X8{k+AQ+#T6IFjzUsUo+kv%(;w#WtpqKelsn@V zvv)$dS+Xv6nZ_rM!Jd&1lMQEDl7yfKusCV)sEO%&Yt6P}F}=E77W*yy;wCgAJA-S? z-HGeJ?)6wo9j5hF#IM|(#KTM25H`Vq(<=Th)IzdCa}oi3Ty7+PR!W3R<5M8n-VbH% zRgYJXBgTV|Fy1WGzrCLCG3q(;1ew0&bQR2$lR7V$n{*ps3`o|P;0IQi{@Fv85oPZ- zN-5RFT%zR-V?}lxunD4I{>74(t%*vdu}Wl_h?ENpQ%AEJEYCN-kP6iNf#0j!DJ8~8;ksx_w{RJcuD{-atclBo|5E4IX#357;;*=z_{M{3wo_|V zvp?P(6khe?hXYRgr46>qDQ3%kpz(Niv@(nRpo$z)T5M)UZJ~^&IGUMS$wLPC;iHhb|AYYTi?i! z#ydh_)0XT;O1LI8W;KduFc&4K_CSH2>_75%H_Wf5f0k!`>R4yQ@&U;C zCl|o&lV>5ys0QJ67^+Z5E){P-VM9Xqz6@qjPg6%!L;qCT0VQJBJJK~tC*El3$a#w?+eN^CO`1Yw?p2Znhh1`IJ^^uc5eXi(8( z#a5IVDTNfu*p<*o&E=S~weHz;4_ztKuhnM~{BUm0btz0{!nSxVWU=oI!#me$JStC& zY@Ot}B5Lnz9@Hib!*X~7DIm{plGX@GA}2%Y*k^4sa+8=;s_!H9j_@N}9W>T$P6NwE zmuz{{8GXzBh@2N=V?q20*qdtFUQJ)l5*E3Tp8d@YM2k0t}HX`2g6Tg)8*c{|E*N&C(5F7-I(#=1$RMpr0RrV_R?9 zN0|wuT7qBZ>4~M4GH!gVcjl=pNAKjVz)C1*H9MLqANEyG6j7Ok)v@yWB_I68f9dA7 z^iOnXMGO?ZU>LXPYg1u!42qGFaelU}8DQc^As&Wv>FDN#+hSSQp4~G5YK$u08)wih zpb`<+GAKA>&mSW8{}jW$p@wg9hvkK=6q=R8y*hy=*&hdvh}57ItnQaS1GSVf@aWh` zzq`S;UU3#yEi+>EEyG&0AViT$4BE1w>JD_ZrzOpqfjrI9$i)9czTX(L)G9fIx|lAD zDv_~LwcW)pLoT}W;;bI9`YNTj#r@i_gCy~zFRmQK9rb;3&I*$Zu<}b=_+KRQ=1n=l zvJeC%*?TBVEku~D0($+SHi>w86Za3W!#OiCKEZcL&x%PaFmGSu)dmdCnf!i0MjJ)+9tgP*LnSDn4CoC%LDazqtf~3*o^zaH;tBEQL zQ(heX3NHyX3Mrh_1eSRrZ$(oInLS;&UL`{uDtx}?;EX|jeLW8U_!I~CHz^%`$ZnSd zaa@5o;@58&hMGMM>KDU&@L>9LqXXU6THQo|g}E7F=ih5g_vkr({bZ+Sky~Mnq=e*m z;??NNpsMELF1}?q(D9Cb(h6oWooo`XGKmseN$&nmr-o#!0IHbgO12_VobI;~FDnJY zbSnpUFmD7rfz3Xd*e7^Omwy1HE6T|^VcROSaa8eQ_8p8c!Zq7{vdm~8TxNLI6~+Mp z|35?)KH7}S+KPi;PXqH^?>%ksR^`rvNM2(9kw|haO}jyh6I=hO5Iq#0l}@iB-`Ap@ zDn!5|RD2=(OOhcoB?B|v<$`rX-#bUIqLsIO-XxL0Wbk~ZNa9uG)t=`N{RC2xIFvJT zvr_X^z!-cGm{sx&0l}9~AqKS6r&)l5&cPE$LK8h&6`sUE8l^642b@I#@tNv)WU)Kl zYHZ(hq5CB~MHnBvfJs+|(LUuSjWe`hHU@M4pf8VtM~`iDAS6Dxlj0#ZIa4dFIZ*i< zIcsmk&!b_V#n*?TJop!M)3@TcPiBpH4NjG9|6!dsz<=I*1K92))RK1DasO^F(uTf$UlDFDW^5TT z>=1K3T^Ge)YAe@BqeTn2c;I>yiDl7c^WgGf^X;;o`A(4PrFbNoBKQ}s2_w8(|E>DVSn|fP4k;N|Shq>=R zg-imDg*GZVEuSu5DO45%?&xJwudlyzqQw}76Y@de@S@n@{TyU0b_J@1wS{-M%S{n~ z{MB_!wf4V_W*XSiHLyzvLr;L8w<0cWkwBXko>U)@>>PwB)lJ)no_e^=yEosZ88*#u z>hGs!@6dljU*9b=Ft65ih^uZjOJKQ}Oc<16wKuW)Sx5I263I17Ws}Vz$jkhl%cn&cr z8KNm&Its?8m}pI+ITvk?0d*B|L2bUrZ-m1E%}O~7^09?r|CM5-`3?ta@Tm;pD&pn! z&VlAC?W&KERCV*b%UXBnCyg4tEQ8s*g-pAjbAH9wEV}o{T>wImqioESwVc?7BAmVF zJ$d{;NB6#>H|ANp!DiI?=O~0hQ|ZSuve|WZnReITl$M+s-z1*-=Jly;{^&ay6TxO4 zF6@i7?2*$CTb{(r>d`{T?1)eVags<1b zkNaIY>h*K&+Fqc?n()zdNV)@LGb;d+sNnc$XvKM6UI7RKk5Unx^BV>g{Eq{Jd^xY1 zmBKKX1~WLetgZXtEI7^%Y7Y1^sO*X&Vp3j7IKl*FgxjVJ98j2>VRQL@p54?wt^MU? z)#aJlvle2#%KY);T+;1fZQ9&T^k>jttZ<;2NjU3!I;Y3I<~wBXVdb(0kXZ0^>2A_y zYEL)(whDAit{W6LtENB^w-oXiDBCKyl7?91mLP_V#$%gN|Mek@79ZR5j<*N=E;JXtxeURH6?E@kTVuqNDjR)?Z{>p9XhFr?(y2Qltu!q=3ockY4-3>$jpz<@49e&fm$&_%_svceywyCH#l1mKKcBRF z*%iUVv^9ON!N;ut{s2v8YxXKx}Ao3Hj7Uys~`20JI#Ud?;6m?iZG^S?J2 z-vUn%+lddXw=nC^FhqzM7y=J3I=Zm|LSG?uNw#GBoPBp!z#(bsZ#_f@KLZnO$T7OZ zfwwY)iZgj_o!LR_>jA&JODD0ON?tZtuw`oJ<>~D@0>6>;x$Gec(Oj#3${&N=j3Q}>13){l zLrJausHkIkm**mB#qbPt$dmo3pV?RhhkOAheDJIvyH zSw|+A>mNvxpsaIs7Kp9Do%IqTD zSRtBIXM;y}J=A)Hb$bzpAi5~bj>}tVIio}y7J%WF@i7==@5j*Opk~7nsHsdV|BP57Bw8Dg4Sk@e-u!2oCeWx8EJh;D0+*y@zBg&} zsO`6SNuBK=Yu|?Z^_Ps{=%%JbQHoT_!y9N#eH7L z!~Pd7aiF~e&A<?fO>GQF(u?T$QC=??+Zc%mic&eQk}9e1G()`4Il|e8ego`|~uz#N#o9XG-O( zys~q)#ozS_B(<__giu*zmUp^Xf8}?_?a1_Nw=}t}7~=p6QYI1CBm~ryGD^>yY5#6U zkD%EPrwXAcAM9)?E{#MZm_va#7=6uSVDF8R7H>~>q2Q|YTg29tM?ayMq05aMl01y} zaG+g{6xUAD$8CawidTss1u`b|#+s70 z@V7WE;Q%{ds!rAJ2HUess4f7)D6fHve3 z??uja$tqyi(5<_!!c>pk?P3UvjN?iVXkklKnpy!oFaofRGUC72byXs6NS$+zQz?j3 zko=IAg@oyA+Sz}sj-uYx1K@H)P(#LViW(XyU`g!{6D1KTFiBE`ec@0CB}JD^j<1BO zX&#ATLtDN~^2Nhh4KUck3Pd~H%yZ+R^AAN^vdB<4G!VzE@}l^vr)r_uEg`Q6_~ zACUo0*cf_`)TuFnKsOfNfWFCl`)fMyhf(bn?!FFrjl!a$LCY@bdKt5|%Tg6mIgZdV zN92QYB5jRCw)rbgkly!zFR9)v*1$QO+5UIHn;(pqbvp{S+7TwFtZ&9G-dXE>kDpq}$wscBmY*8uaL97lGt#p+`CtM+KftfjNIQeppPnkm znTW3`q2ZXvF}8!QIi+eYQ9}rG^XtZ!w`DSi#C}x?YeRTLL69FoYQQ8DlBmsH*MM9)cYD!1kqdeQO$`{L8O5;wlm0p*bp(GSp9`4^$W6gK0Ret|DC(uw+%!; zb5beYLA^g5>?=xdLdJCb)C-b`%IfsYTunDZGa($>@EoZv;L0X>4?(mu@lqYz9I^njrUk{+&HgQPdptQ z$JV?MNtG`|2rniIP7fIjm88}jitTT-nyNvyQbknPkhd*!Y_kng#0;cFduaywwDb!y zSQz|9h@XA$>t3MVz>*9Ab%&0GiParY&%{nra`Kz-f~qlIuL^D01PIq9y?TUtqYrg! zgM@n;7A;w4D?zR3z>=DU>(VRv@{~fPe-3i^Fv~82AmB_$(8e)1Ad(4phXh9;#F-PH*-#d-?8tcdxNH7(UC-ugQfvVfl69)NOF=V8K|U8 zWNr^cUupq-5M@0*rp~&g`Mo`#Eq`zB-R%l6;AO?_TR2k~0K&~O+#Dv)Z+Nu)7uupa z!+XNEcXW*4R0CU5v_lJH@E0#+!bwZOHdXE!2dL!`c8@HNSdMX4T2G*}|`PQsY zv*5d8v(?Nx1L^oO3wr%cFTsW#|Moe1h{!1B`W)_Xh&KEf8~Ue|$AlDe9N#2Qf@`EA z6XjI%q!xU2*Y}*g$!32G+hF;zTDtQw!Rwo0NC$e)#uHI#J~QGiV6#+jtjVk zk`YrAunQ5}(i}{_+2wa9a1XXz4JGZfd(k8-kid)j0<$>q0;SU2B?W7n5bN?8^(wtt zdvpb5pJ}*jVhiqcJis4ndT5oylCAav*0aW!{u>Lixpn(o>lQ&;S4UCy)H%ZShA4M@VO%cLNsl_!o#o z+{b?lLGGzycbIb5QlLWgRVNYqk+jK0$Y@Cv9bDExkHhGBIkOA<(NtSqRFvsV6^p*d z{(Y0zuap5b^EUOpUr+cd?f5b`iVLm^6pBsS%qLq~rn#0=o2@;qibx)1P)`rXt{0M( z+W;cHHheS%ktbjoK-VkZw*BV0E1mT=H|DSg zdEr{S9vogi$Z0dxk8z8XY3us;Pt%A(EB^a_@T(Y+!u}LwW*rX}5B2Bzr_tWMTy9Lv+%8>`pRDSfctrSjFmn>p&{M)}(~0 z6H&Z!yW>r90N(2Ilt7}}45%0HbkkC^6Q6`K&UOE&KWN@vz6X^8~twRO`N<)~@NljdL z?)Gxi(GnXq13~j%;P2DZlX5(0+2$oKII4c<0$aIDK%g!!tXG)Bo`cPE0&?1uE=~w1 zGpuRlAz4IOBbH`lyABp(0{(5h;1Sc0{`!y^!6KPlQOUCR;&ZIOb*BKoUpxiu>$z0=)s}iGeY`Suh5MofcGEEC%)yABjWBmC+#rgeEO3wU5^QlM=&o>$ ztLIP^0pj?=Gv-=|?>m(<8yqWJ<-ov7(Z<7=YUB{j zgtSqzUB}SMD?&6VrioM35PW(gEHm!o>ICVAON^WKKw`CjkJY$9T>Gv3No_PLYB^0S zKM}e^h$hS~#*Fip*}}-97#vv?PSDnd`g6em3@bP{1qNkDN6LkdfNK1jM_k9_F?owx zjF=b%6G#1HyDCXq>yysL+#ZA$O{g?-i4@k{x+c|B#_A zKUy$n_;Pe_(XIA}(9a1|%+gk~5qgSNZkF7_#%p+)83d95Pv?kMoa;TlZ<6j1H=F<& zY5KvzO)Turv?mp7|wIIb#7$ z?)`vsq-yKGU)P^9Yu&vN8YtB&jH-UGp@{g}QEpx`&0xf87MwjV)3|-V@Uq(O%hTLR zJOO3-53y?t+HX{tD>m!W*xGEax-Cx$(06NoEyY4so>@)q$~vCtRh#A3$e;&i2k2D> z^x) zm39A;z+*YJhKbs(h|NQuwhKj1hzQEln(}86w(zz0_MiVUD7w-O!G2TAlK_YDWAG5u z;!g+HBEByQ6BOy)i}yj@3>Sa}vnm))-lpX)NyITiMiSLbFp4~)lVip}$w%LGL$p>j zILjL&Q&_~|VV7&@Z}e;MU{=_GBZ*KexPKyqxTCrmof6vBh@*yDb`H|9=PBQPgnUj( z;hdM+i|#wHCgI~+D>K{rs4TLHS(`h+{m(&#!5FrEwrrE5SM?r72-A6_Y*6N zAQ&mhi~Ia0k&4tLLb*JM%!64X*T+!)25Rz9$+9S4#D;l>nhJ0;g&{E9C8io&oyCWC zqLrwZXM^eRoiJnf&jW=6>WN$rk%T7^;F)H5nZ&EF#(#p;Wu7^6sVBlwb?4EM*#ol8 zvb4lxM+Hz=gVfI1hZmzl+})J?Oz)JyE+h}VeAWv80y>yI7{;WJvj`3joYDbuv?mj1U?u|nbe zgF488Nqt`(QO2)@U%L^OcF5m=1TL%>i8RRJl6#p-(Snq-j5XTXMrkG41s<6G=GeRu zA8t=ZLWH@IDF~>@siJZbIW#sGg0?6EMYJI0Nv^aeiGQ(NQgT0lm&%BQFYOSOM&_S1 ze_gYXP=fN|esv~xd5}>YJuBF{F-r#@Ns_qBJ>X$xJH`xUk7^@f)S8f5^-WmI-(AZa+G=`kSTTt%+&5FV6eakBQCk7fVq0M7{R~NHS4n6uXwao6^W9)y)evU%I9CbbFfh@nVnWZVjugGn1aGyve$a%5i$F4s^0HH@ zK-vyjjV}7dx)E3nIkYu@oyNa*wPHc0g~JBl11v<3{?@hw$9D5;J<6cp%YIZb4z@J$ zcD=s$$NavVRYl6T97PpWuFSf;GQ(Ry&`l0q#(#m%MJAI!8L47w~NUs_p2N6zA0b(R6= z0hLb?qP1nymD;08)$1Vlf-~Hes}Bo1zf-rd=bxwg+lhUlsLxT4hxXmI{0f81p;3+W z`^@jAzHb!Y{|eDJqp#M9b~`Kq?H${ZnG=r!LjL}&SB@U9F3wA@KK}!GK!(3ZJ}92^ zdoNWu#_nq-+|T<@9}N|O+SeKUwj-Np=FyXcI&5rzQR zP)2V@?(2ShAKaKPtjk(G2O8|PtbhJj_8{RJAsO@m`*L5m3LpJBY!q{#Jl<1Y z`Ej{X^S7;Gou|SWM-5It?#ta&c%BL?^=ytiMX(pveE;h8^D*kn+C*Eqlc%X#mG4p_ z>Pe9NdwH+kwY%1$$_dr^<0oIfrhl#d_WQ7G2WsxZ?kBHehI95WuU@~Hy?+mP`?LD= zuVb`Ma(t*(?)K20rNgA710|A9iGM0rFFvTv7{#H%s@T5V=QzJN#U3a&=*Ijzii^fy!q_E%YVdOd#(nV$n%A2xtj*h(qN;VsGI{W_VPk6Bi^%~Ef=%+ zJ;$h#52mC0ayK=er^ck79~UvW+*e~St@GV|Y{VatmJNEzbWYiC!a04+v}{t!i-V`H zm&PFuUmz`R;XC%>J(_w%k#WYcPn4spMRfcB?Xmv8J=41 z>sIEY@28Do_I1}w?R>j^|G(vYuEywYu!(F(?(5dwuk|1{`q@_@?W-^b*0WU0vL2(y zEU2=TyT$zzD>yDs@_hDnO!jpAQ@I(n$HL4#7XB9Y*vIsFTCMDBu9viVzM01^U$vNh ztM2r2InHk-G6&3R<$rEE%-3Pk%0V$fPl4;TyVIjiNW$`xZnfN3uC>SBIUB_s1pIy7 zsl_P8Wnssy+)Z(3DlTZH)!P63+mjE=Wh$?1^;aQ%4UxV3CHS#(SNwgQZZlVz{2AcS z2q-W)oqF4otIPcQ<>-Uz+UeLo&{zHLfx_z7cQjVN<`$`jet*55$M*cT*yXrJA5>R! z;pm5jFTHkQtr9Y{ufSUHlm?SXs-e7JZc=S}J<*`4o@~_OU7Xy|mdC;ah3&19GI8|y zyLq_KysF}=uYCAM`i6%uuNUQN<&J(-<$NA_V{i+=Y_K%5ll$jCfA`Jp=h}X1yDs#0 zVdw5L*8t&%5q}c@hn#}`{{SLDK=>Wv@H?THfFKvU1?V?3aGBfL{n^HTf!XXEA9j<~ zi3J#X|116Z{p=%}y)QSj$^d@H2p3o&!K7zaE5?vfhC=0xfR1YU|Fm6sT#ecPPtoYn z&=kcWX%a^1*_VC|g)GT3vQOk$&ZD$kZ4`r|kY$u5ihroSH8jK+iule*iX^5|rl<_b z65kn&eVO^5Tlq~lcYd$u{^rgfUhb>cdA~pBoX>t<_nsp$G7t$2vk3ums1pcU-)CroB6yE&Zb*Gl^owxj%BmN0e@}D53{s~h0pCDxrj#lGIjKNTv1Du4ixGYm5 zgR?S)%6~w>7!>C)z-S?b7C+xFXubv`M(UlB(mNxqcSim1Bdd2tPT!0;s&__A?~J(K z840~J>fL8>z55KVcb~!a?lZXFeFoRN&k%a|8A9(qL+IUS2)+9Zp?9Al{#Mr3#z^U( z@oyCYS{WI=GqQST&GN zP%=vJ9LiG|-u%3eL}4_;vJ8Q86v1*_3#0mX=%G#n)z_g8L@1Iccno710TWo76>yQI zVEf7tq$uEwNXs2y2D18H9gdP{TwqxE2}MPM)R+ti zTo@MV*m9DGpaLm~oceiAipBwC0*okffF(!@V*yV?b7F8#(9m$}aUTNh0KJG=L?}ceFwqJGEfAu_OBl33K;Tf3>KqAmiwX=%!NAPY#QP$YB7b$v=50kNMe3MN z+KNz$L`V?^D^cVHnh;qW8VOuA911rSCLlrWnlO38;G#u@LL@@UI0XwQR$xUK=P9^8 zOyqe6U@|AmFrCu?=3AWPwTMuN)UgCt7g-9Li1Pu`ikq9MoIKk4ejDx9>pjlXqav~5oQGzub235#P zBw$G`F-j>?$6}+c2&G6J6PJbvg-9I>0Syrfkvf+58X^=T5olTx17OY5TEC(&jNt+% z!Onye2u|eTf3V2XqHd)~9s7c|B9tN#R$WfvB6Jv@Q$M*z!GC#>D1ouCiWCTd!tw*g z22P9XDMjkov9%SU6scn+ttmnoQpW>44G{{FIu^MaA`~JKj^{`c)>|A*D!fd>>?@%n z%gO`_a0X^&TqH3OXw`*6q>e2@TMt{A4o~j@Ft?g-8@6y;IV}9;AhX<2tQ(PZE3mWBVq)9TnO zW`F!d--LLV(6B2>Yu!^K60%m#dxmxL^c+;QIIG%xj#UbsY;KuuIcLTbpL+9S0iF-f zXZ$>(pw9vN?iW`_f4Qj0@5em5qB~1=SyhLN!=L)kIUdxMq65aUMC)=XZYHE@m=cH)vk-@G#UVhjY9N_Z9T>Y`J6mzb@%bI$bWB6a(`on zvQ8;mDwj;?i>JF^$QXHl!H?y~^Un@BJo$E(>)z6{OpJ4GBKPgHginS{yO!GB_?v9I zehquBo8=hy2+43g=;iz6UUEUjoTDDeC(3X2NlW#=cgOS``t{)K0bVs_UHIUXgF5?DdR##)&3V;4U)_1#PwR-5~TTAy{`mJf{uwA}MXHF+06;tJX zqh}xc-DB&m@(hB+a6OU)dL`-7l-uTGB|D|hr!#{oV&BLfyy=TUU zZrIVP`;HjYedIdJZlUqpF8$GQly^tf?%I=Ko2}>1yK~Oda`E;0S>;v_XMaAOHf`ij z1*Y}`dpq|wUz(lg)zkYy&+G(x-W~J*`Ssu5eeN(d;+wKhZ|*byc*1Gnmw>G`sfmI9 zBP%z=o!l{@ctzxTkDS~IQ{3%y`v1D&arg4nhUxYVWk7JcJN0#5{;*AF$OAc(>ne@r zG+ONO^Zx3~J+~iyl;Q4lXn*I&zfA7G?qc6HvtRmvM2@)#bZ_SMz9rz^ zsoUJIp79@=dZncG@~iub9O2?qk-!fxe>7#y(SnGS2&XrZH*=6j_^!y637FfF$+8not>EpV5#(!H6q+(T7ReWvz zS9d#k|Ilmh{!y+KJ2Jw5bewTFV${Ickvq3^rhE5G89im3J1u4&j=ofJeC~_-F?%++ ziSDDq>_UoX`SF80IZ?mATxIFJBR*wQt=+*v$j}~1cN2u5-qm5^%q()P_25qX zmkd~z!Iq>Z&1sskev9X-rp&L_y&8UsMh5tY^%<=)9hlvB>3xUsNAG`iEXJspWkk@L zyn;o$Le7uhUw=LQNeuI1gw?KzW0rXS_QbU0{M^~uuR3>|GJf#%MJ3tgrwVSSH0_?Z zZ)Qb@6LW@+cP_h~b2};Ng?EL0;Sa04ubS4yTsU#r^1zeu zuQN>!U$C0}^W4Gxhh5pyvi|d`?^42-Z|w@#zo8s4?0;|Oe*G_5WgsmDj{JzB!mo-a+RJP8Zro(36`MTF&H#6Ry*KM#sI~47I(R3{5-uQzT#K>osuT#Z0H)qu!4qD7*wxcFK=oHl}V%O4w7crk7%NRsW>yOnqoG*1b z?^0e`oL^~EROV1+Q&mt{Ygf+JgdQ-)z;burU&)enHC$HmUq4|?lx|gxH307 zA-aRr_R!@)L2}%P*~=4FewSuq@C)1PbW;f;+G3lUQrq)v(V&`Q zgU%J6o~5Rxr5iUmI&Lg=bev+}boNN{j+x25C++g>|Dim{x3|HxkYMi4o!fHapo~VV z!Gn5#Y-AwXS{=FDX`Ti@TjNqh4k)j#cDZQF!5P=!RdxfaY>LPln=1AqJGdx6AE#<6 z=YORKxg3d3ds(=Ah@TAzI*=MTPnwrs)VOQU&YiQ;;}d-{QUiQD8XO(cYe7?|X!}+X zC!2Svytvq*rr4pXlBFCk7M7J3I@A={3@EQGV+V8Pm26RAbwPE7Z~7v`ozYpbS$XCE z#~$@JNFLjB-43;ZTyX1JU}NT5zHHLz`hRLu6d!<{wFXgcx0A}5!;xb0u!)k0Gu$5@0#kvy`murDz_ zpr3!*t<$lgo!v9%pWJX{Sf-ohGJorVk@w#QZfn@|Jm-PL`HFo%a4g}eQH&oHVdR7?JgsNfOI&V;nMq&W3Xn>X)t8rVsJXwWPeG6;oLt5 z$3@@!>%jv|!Ws;ty8JmhA-3i(M;G{-tZX!l;@*vRT7Nhz;Zb2+qF^&y%kax>Zq{TM=`smDr zqC%6zn>JTxYMDvBGxBkxA^PXMV>6>~{Pn!Io7`^vbHkn1C#B`p{PpN6lcwE0)D^{q ziAI>|=;sMbJ~+~3m{qcA%Q(B3nTK7&sw@iQu1&n)>0#gaMvDL0pnrP*uQkiN94j#$ zjoVvfZ^lYul8=~dUTs~K&?6~6{adX$Tt2#T z`{n9kw*Q%uwA(G*;(t}@hSg(g#)mm?%zpI8!J^&w)?bgoi$+(D&9FftU7=PJEM<4?x7XBakkowCG zC;%^+`&Tkp6!1`CjMds0IvxSwJg6UeW#*A3EoSzd36qP#n2pAf>5=imOokH@UYPBu zOGzrTeDSb4*n`s0a;TF_E$bQyNLGu>n8x^=uc?A3iFMSBEeGK_ zh9tsbk=!$rqPcDV8eCzoY{L#^I11m!L`0D5cp@FyS|YV#011@RBL7W3DDMDgW_b71 zxN#Vs+7m<vX@5u6msR3*aTGym7K2A$d)Wfq zsl?73ZpB5C6sR3RY;QJq?&;W#tM2*E&f)rQp%WY~}L zNUbz{Z&H1@!!Ye;J(rA1i89nzjHNCmJP6+bj;J}~aK{r@W9)Ig0Qa6Id~@jWEYA=` ziL&nOz}wYhg?tbebt77Uo?HMes?4`6|# z1Alf3?B7n9_=orr1onJn8PVm*ekXtkJFB2yLtT?kEuvXdvv&17L0sd(cejW8MW1pY zAe$kx^RipzIm7(Ku{lVRvol09-#ZJ*ax z&Z=hbHTRu0jMVC6BgfPjwMd)IPJ@P;7k>l;iwZaGSX8QfE1hdXu^4qZg*?xd(w{T) zgJxmzcQ0`*B&nBI?dYL@PTP1V7ExeQVc&cBqjT zpEy|QAmtSVscxb9D#*`EA)jgs!}0j1t=vsk3@pX`8F?{<(pY!quq}k)1oT|o%3wM4 zTz-zBw+29Rnjr-FrXhy3=mNO24}SrS;7{J=_u;X8R}jn|2Ys0T0jHi_bkiEHNEb1r zY4<0jt?l7`iaR&c-AZ%J^)0q#Y~V@iKU6U>VFApD;J4RNp6b;b+2|QbfYFjaLV@;d znUW+e4r|-jc9ab^ub?d)DK&ccvx|?-Y~D!eK!Obd5|1$@!iwyoPQcEdA2HG6K0qxJ;1u~5{4(&b~qs^eIzk8t*akoA( zUwBUZ6ZJ)20zL?LSS_|M86SK zKEJ$Wy8$+-sD{ddZn^YJv1r!%B^DgUQ`4uC79X}++fSQ@!~oTlg{yDVCtwa2Xce>P zW=a(&&f;WmU=^yC5So4NpuP291?aOgcR)01aail8K7RmB87rEwfjbfmd$m5z zXZz60i!mQ^kf={^^oOh{Qjd(JKBVi)mQ2JN)pb13i5VR{(LE<6Bn$H4j+&L_;{JY- z8`(P4&SBc3`wSca{s09i3vQ497oT$$e>(ADvumQhy;5=EIl1iwp>k=mlZsiqfMwVk&T{hr=(Nky18}`9bCkR-1XQL6k5K zLuWct(h_7NxL;z4eO9}K2~+WNg--mm*qhzIYkvQ<)Zn{U6={&QV_|FTRuYc1Y!`;Lv9H;&3%{yTG|0#J} zsZ}i8dhJe3@Q=#1x7nXwi9`+&Q+?3WQw_hUQ3M9ys|4Mw)FM~FEdN=>i$DcG$XTk; zL`J6Ax@;tif`)2Ymy?sHZCm&@ zdL4eZ6n|6PkZ%s5w3P3~!aSV9=H-lCom$6|e8v5eqKc%SOQ1g_DRtFMZfapla+0i0At$jKOjB0)W0K?bwq zc(n0k9HY1+S_1wMSr`)yVH!Bw8YQ~$}_gr_fmtZn{JlP!Ar20G042W2qctwBLk@j_MUIJVLQd1$}lHIP)#@qAEY zGZ8pXM75ti`eD7Ee?>aFc9lHYfBKdeuAc!$V2`)t7F57>{!?8Lf-M!pGl0YeB%ryp z6@R*<#7y7oy|5$#0;K*_aH1(<2|l`1GD&@r3J zCF@ie+@cst&JlX0q{`?%@zKv(W$)Ja?==kh@OC-*g!`U&Y_53l0J-3LhEJloVjxZx z9|);@t511g?5^*)QhI{hzpIfB%1cpNuLSR4%YL@sEwqdE=-`2d-eFjAI^`uT zX!<9A0>JrQwZK%(^`tZMm33Ls_AL6Hbti6>E~4M@L^jsLFB1RqOd2QSzQRg6n)TFf=#BE9tX>^K9^Qah~U5N+HYtbe$2ApT-l z#&&GZzgnIJqtrAZ#tK$d#mh=|F=3=|7!)QBETaznmH%*9R2f;lp}$^gjh*O<;X|Nf z{yx4!Xlnv-YJ=K5ZWZHTq$S)?Ry(PP*FVM7H`=!?%a=q1HMfb!f2j z0Qe=78sfETPHJURSh**eOn)rte42K~?{Ft$^nQcH zH+4aI)iWt@fxO{e@1e8$;9zK}U6i?yf)Dirl}ULA{=D4BnDLhml47JR|5{k#7cP+- zU?D9%Xu(k#!HT75SXJx8XQCE-n`3|mYN+i3$qoqnKcwQ}vd5gG!ioh^NdZNFs^#xf zbx4|z3+aAC5q1biCVz&b2%~%Ja?mUd2Up1{Lr35@Vzj&Pa1Btc6IbJiJrQ@H;ntj-MBzykTcH6 zoaeB+XI8~eB%})uCA`J!wmE$A~Y%gLlhPl6rcKsMaEbIK_n3hg)j`lQIH(UTzsM&PqI80A^ZnG3Y^lYF}fKs<4!mM3B}AMhU19Q)5#;NQk( ziit*=IXweme}7N_$I1r>5RnjobK5(6@LpaEZbMA;|e&>L+}oCWP|T9 zoT*Bng;C(40f;pkY%f=NX2JLm+q&6exTtIWoWv?RfzC!h(hL%?>P{8pbg-fO%0mqx zTyL(dW5fWq9Ufa%qdVv(a-?WfEgSiYgGJpSAr z8|nhpcYjR)T*F-;KcM$}1U~RggyizVPscyV-T4ZW#Sa;b@Xw?>rlSp<>$9|2a7GY` zWGC$zqsCwXlu#3MKaY|}T;L+ocU0S^IITN~@0hSxMAhK@ zTJk(O`P+yMMrOM{eY!o zQzP88>A!~UWf#Kax-IiRO8cDvFo|(iLmSvwJs*$8OCr&a1Q0+}p?YIq1r+kO5@AuQGUGa@B97-JOEQi( zl1wB@&!fongJ5e4?$f9!3z4YHy9^+h_|bQoV>V{QH{62}Z#GqSp$EO(YP~~_yzt+e zZSU6Ok=r}~Loq43NT7luM#lyrxX+3OB!B#{(B0g3Pwp*4+ z;Pe6;_^i#Foz6?`%BpfW0ILPv>|;cUAo|uZVoScG_MRQIk67VF`FB`tw1>*o?si_} zkSB~#eftm7P5^?`ZgPZ~q%X6u>sT@Ggk=RuH)L^NYr_XKc@=rK@{we>-M@^WTYnwr zM7Mahm@kjw6>nsKr-+bxutr>DHJ2 zdLr4e$yDL+V%3EOkQ;WUr0nNxH<>uUs!d7?_MaN>Ifc4h&!x2wsj5P7(me7Vii3NDxkXQ#TrO^GC^Z7;-r=a~dQe=tYw;g&}G zjH@UKUJ5F~1+7K3JMmjPA1X^XF17YlE4kUyd8WbZg28^0G@W>t*IgxL`F~(9iJk8g zv=AjWqX!lJ!0o-cNi5w}0v>7kAp=}>qdW#-3Rno~y(DxF)JSRu4$ge+1lQL(PArtO zN#-4DIr)u<@(F}K?I3dUN8Cy-F^`EG$6IeRb zH5%#p=4y*ro3ym_d{fD}u76$_F{cDXA{xrR_(KyLuYYN@Ixu*rJ=Q>TMAVzfa4Aq> zpt%gT+k3?br$L`Y8ASIS5P2X?z<)4$!5HUw#u!;RM0Z@u;$8+-5FvLvj8(MsMIi(I zjnZRq@(usVU8C-BZLjhLiUa>X_(T}c8$uVf9LR?v`Ym^GPi7Z^!++zWfoy<+-Sz*4m*n!&%g+YcQ7k`7P>)eq&n|{IaV#}4c zTU2c;)$?r)(FRXXUBN6E(txwRQx1BxAd2nZa?S5%-JW-aO+hRH`KnQcLOj$A776E-8cD zYhwEtMg`Qbq(BU#<@&-%>9QO(+03+Ek1Dw7c7JXN77c4z$LZqK3p*^hRUf_%ddBL0 zdpvFBTnTD-uZ**N@v6C*S*ZxHnbf5n-O0^ZIHF~|d?AeS*3`tFM}0HCI-`)Ldg!a2 zvw&2z0u6Ar=wf(77ba4jy+r(a{sb8NMi6l#i?lHu53I3u51OCqqa)4f5hrV3$T8-W zv45^v7kt-CFeyWXOvlA$1^DN4_X|PwB7)c!&Lwvw>qmc@bz{#6VSj^nb>n?;e=GXD zOjsOXmk~}arGnAb`|>?%*9%^eot03T3%535ka@*}3yD5O3Xcz;%%EFoysGw3k@ADw z#0`LN%;N)~_js?i%nuomJ_A@qNHM2XJb!|yNp^qg3S=JsJdpm#0gW-e2TIrM^ON2p zq%WX_A(7DkqP71MZ9oGHRn}A#fA*n4VH^(_fm?eC2%vKKte<10WXm3lI((OE&c9n& zhvgCFM~UbTNipvnM#Y4MnRUZG1P{d%hXukpRKo;N7hA3KgbUS&nWD`cF~WmPM1TBq zVwD0LJ{rqlS-hAFfL=A#so}m9{u~2jJ8T&@dIyVo zw7{663x}8xtmaX2i$M6!1-!>4>3;!gM{Y!wU?S5FG>Qw;NF5rNk+fxi{xkV|@RrIT=tY4`pU+MBUcNpFSe= z12ya_Feh&uO)+f81Gm13oJFrt(%-+zM^=sQk+L@U%=$P@YMS>{9%`vw-hYWf>k|WD zCAAYGaUA`9ie21oy%{RhWHT;9MxC5@5|}13j@phUA!A*!HaOAoMK z#DU1^%cu}aRYR8j`>Zo+wwz6muS!ek`5l2Qu0s*#0j1rvCkpnXQM=595`ne9p@Co-TH_WJD+zgM_mX1rY`71Qg0g z%S2H%<`4RArGG(OC?`U+?uIABO2=lF3imq1{|P5KqmV!iVpWht(aFkFFGSf>p9Qqd z$W*h}tt~d><=KO-d4DA?k%8?Y?b$oL1OFa{nw&=NkGMgQV3x=xR1mQz`4a#D#%0X% z$e%Ppr!mFE@y+10N8{7#z-Hg!>}}KnkDl|_v{hLSPmrG`UJa*trgC751!Zv{lI276 zOggw29jO6S8MGU)3hfTBIrvuZVI0dimE{yK*fPch-LK|X*SeS$ufnD2f zLoyHSUiUjZLDaF(fVUh;8JToitg;nRORSh^H@Z!krVNAJ`UoGs*2xrDT?nh1a2xt| z?M*gTtsF@t_;m4$UYcKDbF<1H)ZWL#XjqAu-{*v(?!W<*_YHH*86WY!FNo`aMJEldes4-gYs-&SU_-7J~*hmRq<O2Ml=Rae<+ z_9Y_!qQ_OcJ(WS`{#}lT8fa0WveL-ODZ<*Z($&k0+JF1ye`%z`H$JFV`B5!dGf`$G zCP50F-_-qm+5bt(lRc9$3-1WI6PS+RFJa;g|168l1N^F;V2<$MaUIP6vw;i{qlU6< zqkUV*yMX0KJ0Y1K>5(3N@O`HDjra*f&w|C7{V&cW{^ATHJ1nHfU^$dj1U9Ow7vA3hS~0C^np^dXzm3f z|9|3NMIvu|EJyBHtiy%Ra0bfno$<AT2#MNdoDY6h;oU4&>&V1K*R$GAfEAxaDzC8-I>-X?Y-3UKIR*A|Lg z3(HTFxf_@nbV%=BM%(a~yH_h>+@nqtzYp!>rXNV2LUk}k;&i2Hort%S>)JV3RVf(a zlp<)A;GJ|-wlLXiY**(#SIdGpa6KmhF?0%=TXD;9@00nKX5!^y_AJjD(-ajX6Mwxi z?w3aSCY)xb%JrA7*lGfKQEvsY_6weYb|)mpk^knmGXC7S4o4f0wvan%WHSo_`iNC#XxE zBB58))-MJ`*?k`a*jg-@s=Pr-taSuw1u;5=L4Z3<#{A8pjQz*yf3-7{Z-H0u!P;3u zUPT02>~__~gUheOFr(dhpU2S~lQd;5(m(QN$AwNn7KA>zP)1&CsiC6!F?YN4iX(0Hw&kdN)6f~ zYB`U|bfu=mV%KsI=6_`2H#dMDBZCuvIoQdfJ9bFFq4Dy(S@i)HYIEpbE+bMIQI+)Q z{Ev&CR|sFgPI#=n9JYUeHWmLDXu&UEC|%OwSi!akspEw}{mvl5zQX>vK(Ld!6a=Mm z_+6q5fZfqhH$}y{lsI>-` z{ha~aI-cUPZmQ`#+H7$S$JbYX zGqd&J&6jn~KU--54FFiF&8vbcqJsvdpkq`48!DiHB}5*A;)wbrtN3T?X~sk3z}|yI zWH6X-$z}p>D3O`M&)TrO@8HQ&^LqKnwiWYfLbP>D0e>??iK(3o zMnC+5>G}D?@(+Nu%Q4!@X!%7!3~`;7aDL^*w;*x4y`juN)w##D2_n{X_UxGBgjUE> zd}L%nxPMVOP-~kfUUA2#y5${Tl9<6|`t231LwW?Bi{WiDZk7t~GVqc(aYmqbY$!y> zD0icfMG=x;u)K(5oaja{k_|}09_Gwhz&a$Yg4wf!9zy2TB&U@ylWPRuwY)k?U*M*Z zODx?rv>$!Z{Mc>-SbNo16-e(bVU;bwtBS*TLw`xge96Gqo1W!ge6bv&P$mE8@U3lz z0E)oN18ReHu%UkMu&|8^gL}9U0~j~kHtZzK?k$x*(&rQDRDMQvx$c>5^tF^)i5+Gj z#84&yo)U>wBC(2+aW$2RLG%I5l4Me#%6o+AesYyXDM9F2`5mnR4ZmxsF3c#sc_ zzt}4Li>(Ba^_9+dm|e?uThA%w(*q$LbbnlLKaCystpW^B6?5HIGUx+wV8Ii9p8fw}0`B zvsR9#3~402aU}&xPnGHy(-ZK!A)X~M4bS7~)CSllx3lW8cCYnlKnp;g$h_j&sxTX$ zMc$D+oSQUv*-_7}`IpPi?=*90kKmd#EKUoyR~b5CkK=6dK)q2z00ya?`i5B2hi5AD zXd9^tCyhac+D(x_L~PNF$rg0 ztMAp~~X0tCD zpjy+yo+cP1NlZ|J$%XK_(Da8;ckfSh>^}OZ4a}kR|EE|c7V%(tNRyWNF@JDSz!6c^ z1Gi&uDCp;k9R4+CH1%UtNgX^c_6RQw+x>R0$g&~)w`L9!YkwZ`YAN{gf?$fyLdn7$NG1v(@b9U+hx}F4=lLd}IKLB* zTIs1VS^+mZTdA4XL#FNEcB5HnE5^xBlUI6CKUv&>)k3m(5XtLddR8r*{s{nPG0rh7 zJXpZ&<>gM$Be$a-TYQlH2Pi)R3FVZ313*>QCX0~>FUT63kz<2w($?v~6fz#sbS=_`;+oFjKV@{^-E)=ra2Jx;PL97zL| z_`%E0IdjTVDbh7P_AtdKL0XxmgD^1wBGZqR?_l9>l@MKS6JDgG42z9NQ&4Yq#Sd4X zD=TNb$o9?+QMn=aWq+@b!o%P}S4uwz#ctxB=dYn9_i7j?FKi3p!&cf5kGCPa{AMHZ$q#U$Ja#6B?feDaJsC=?y_8?K=pc zcX0jvj!xW1Kf;Iekog~=HT(^lqAw5BrhkEUkJ6e{2sDoS3lv1zdBg}zVH+OQ*Qc{B z!n$6vD5d0|bAJO|Hc^bm8bpSgD4fyIf{gnl{(A!=1PExMNTxu9Fu@`i2=ec&(^{jk zpcC}&G_StmkiRhA6l!-lCvRy;|M>Spu)Ksj_TH>pDGaD2H@YzOM(ClrFzPyN^xDml zXGZ@o&H}7C8W0@< zv&Or6{ugM7D>90Qb_hpV!N=VE!f1xOfoTfNA^v)*zF*S!H2`#aD_yz|mW{DmC>Fpa zx9iO&4{SL-s!jOjs*8+%0AyaQ%ElTR!%T|L?H!)2oiym};m&gf$c88Rk9 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

    ?a_E{6;P%1`bcng9cGAo&ZVGFQ8pI@M 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

    and not !1
    ' + '
    and not !1
    ' end it { is_expected.to include issue_first.to_reference(new_project) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index c5d67a90abc..8304c408064 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -167,7 +167,7 @@ describe API::Projects, api: true do expect(json_response).to satisfy do |response| response.one? do |entry| entry.has_key?('permissions') && - entry['name'] == project.name && + entry['name'] == project.name && entry['owner']['username'] == user.username end end From 88e364f0f6f8d21b73f9eea786c7f8326dff61fe Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 16 Dec 2016 12:56:38 -0600 Subject: [PATCH 235/386] Put back bootstrap wells --- app/assets/stylesheets/framework/tw_bootstrap.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index d998d654aa4..718dbbfea27 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -35,7 +35,7 @@ @import "bootstrap/alerts"; // @import "bootstrap/progress-bars"; @import "bootstrap/list-group"; -// @import "bootstrap/wells"; +@import "bootstrap/wells"; @import "bootstrap/close"; @import "bootstrap/panels"; From 1b313e8db8513f41808923d9d429305a68bcee3a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 15 Dec 2016 12:55:20 +0100 Subject: [PATCH 236/386] Make CI/CD detailed status group concept explicit --- app/views/ci/status/_badge.html.haml | 5 +++-- lib/gitlab/ci/status/build/play.rb | 4 ++++ lib/gitlab/ci/status/build/stop.rb | 4 ++++ lib/gitlab/ci/status/core.rb | 9 +-------- spec/lib/gitlab/ci/status/build/cancelable_spec.rb | 8 ++++++++ spec/lib/gitlab/ci/status/build/play_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/build/retryable_spec.rb | 8 ++++++++ spec/lib/gitlab/ci/status/build/stop_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/canceled_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/created_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/failed_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/pending_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/running_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/skipped_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/success_spec.rb | 4 ++++ 15 files changed, 64 insertions(+), 10 deletions(-) diff --git a/app/views/ci/status/_badge.html.haml b/app/views/ci/status/_badge.html.haml index f2135af2686..601fb7f0f3f 100644 --- a/app/views/ci/status/_badge.html.haml +++ b/app/views/ci/status/_badge.html.haml @@ -1,10 +1,11 @@ - status = local_assigns.fetch(:status) +- css_classes = "ci-status ci-#{status.group}" - if status.has_details? - = link_to status.details_path, class: "ci-status ci-#{status}" do + = link_to status.details_path, class: css_classes do = custom_icon(status.icon) = status.text - else - %span{ class: "ci-status ci-#{status}" } + %span{ class: css_classes } = custom_icon(status.icon) = status.text diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 5c506e6d59f..1bf949c96dd 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -17,6 +17,10 @@ module Gitlab 'icon_status_manual' end + def group + 'manual' + end + def has_action? can?(user, :update_build, subject) end diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index f8ffa95cde4..e1dfdb76d41 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -17,6 +17,10 @@ module Gitlab 'icon_status_manual' end + def group + 'manual' + end + def has_action? can?(user, :update_build, subject) end diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 46fef8262c1..43d2b1b40d4 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -22,14 +22,7 @@ module Gitlab raise NotImplementedError end - # Deprecation warning: this method is here because we need to maintain - # backwards compatibility with legacy statuses. We often do something - # like "ci-status ci-status-#{status}" to set CSS class. - # - # `to_s` method should be renamed to `group` at some point, after - # phasing legacy satuses out. - # - def to_s + def group self.class.name.demodulize.downcase.underscore end diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb index 9376bce17a1..b3c07347de1 100644 --- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -32,6 +32,14 @@ describe Gitlab::Ci::Status::Build::Cancelable do end end + describe '#group' do + it 'does not override status group' do + expect(status).to receive(:group) + + subject.group + end + end + describe 'action details' do let(:user) { create(:user) } let(:build) { create(:ci_build) } diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index 4ddf04a8e11..f1b50a59ce6 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -18,6 +18,10 @@ describe Gitlab::Ci::Status::Build::Play do it { expect(subject.icon).to eq 'icon_status_manual' } end + describe '#group' do + it { expect(subject.group).to eq 'manual' } + end + describe 'action details' do let(:user) { create(:user) } let(:build) { create(:ci_build) } diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb index d61e5bbaa6b..62036f8ec5d 100644 --- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -32,6 +32,14 @@ describe Gitlab::Ci::Status::Build::Retryable do end end + describe '#group' do + it 'does not override status group' do + expect(status).to receive(:group) + + subject.group + end + end + describe 'action details' do let(:user) { create(:user) } let(:build) { create(:ci_build) } diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb index 59a85b55f90..597e02e86e4 100644 --- a/spec/lib/gitlab/ci/status/build/stop_spec.rb +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -20,6 +20,10 @@ describe Gitlab::Ci::Status::Build::Stop do it { expect(subject.icon).to eq 'icon_status_manual' } end + describe '#group' do + it { expect(subject.group).to eq 'manual' } + end + describe 'action details' do let(:user) { create(:user) } let(:build) { create(:ci_build) } diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb index 4639278ad45..38412fe2e4f 100644 --- a/spec/lib/gitlab/ci/status/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Canceled do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_canceled' } end + + describe '#group' do + it { expect(subject.group).to eq 'canceled' } + end end diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb index 2ce176a29d6..6d847484693 100644 --- a/spec/lib/gitlab/ci/status/created_spec.rb +++ b/spec/lib/gitlab/ci/status/created_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Created do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_created' } end + + describe '#group' do + it { expect(subject.group).to eq 'created' } + end end diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb index 9d527e6a7ef..990d686d22c 100644 --- a/spec/lib/gitlab/ci/status/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/failed_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Failed do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_failed' } end + + describe '#group' do + it { expect(subject.group).to eq 'failed' } + end end diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb index d03f595d3c7..7bb6579c317 100644 --- a/spec/lib/gitlab/ci/status/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/pending_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Pending do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_pending' } end + + describe '#group' do + it { expect(subject.group).to eq 'pending' } + end end diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb index 9f47090d396..852d6c06baf 100644 --- a/spec/lib/gitlab/ci/status/running_spec.rb +++ b/spec/lib/gitlab/ci/status/running_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Running do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_running' } end + + describe '#group' do + it { expect(subject.group).to eq 'running' } + end end diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb index 94601648a8d..e00b356a24b 100644 --- a/spec/lib/gitlab/ci/status/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/skipped_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Skipped do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_skipped' } end + + describe '#group' do + it { expect(subject.group).to eq 'skipped' } + end end diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb index 90f9f615e0d..4a89e1faf40 100644 --- a/spec/lib/gitlab/ci/status/success_spec.rb +++ b/spec/lib/gitlab/ci/status/success_spec.rb @@ -16,4 +16,8 @@ describe Gitlab::Ci::Status::Success do describe '#icon' do it { expect(subject.icon).to eq 'icon_status_success' } end + + describe '#group' do + it { expect(subject.group).to eq 'success' } + end end From cce41cb2a6574de609211e4e2284f684e2d236d8 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 18:08:11 +0000 Subject: [PATCH 237/386] Adds CSS --- app/assets/stylesheets/framework/icons.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 226bd2ead31..b37847e3d96 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -49,3 +49,11 @@ fill: $gray-darkest; } } + +.ci-status-icon-manual { + color: $gl-text-color; + + svg { + fill: $gray-darkest; + } +} From b18897ac40ffa9f68cf189f2a011ea6eca5ac74f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 16 Dec 2016 15:28:24 +0100 Subject: [PATCH 238/386] Update status group name for success with warnings --- lib/gitlab/ci/status/pipeline/success_with_warnings.rb | 2 +- .../gitlab/ci/status/pipeline/success_with_warnings_spec.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb index a7c98f9e909..24bf8b869e0 100644 --- a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -17,7 +17,7 @@ module Gitlab 'icon_status_warning' end - def to_s + def group 'success_with_warnings' end diff --git a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb index 7e3383c307f..979160eb9c4 100644 --- a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do it { expect(subject.icon).to eq 'icon_status_warning' } end + describe '#group' do + it { expect(subject.group).to eq 'success_with_warnings' } + end + describe '.matches?' do context 'when pipeline is successful' do let(:pipeline) do From 1476bb2c54d8195ea673777c2d5c873c2a3becf7 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 16 Dec 2016 15:32:54 +0100 Subject: [PATCH 239/386] Improve how we calculate detailed status group name --- lib/gitlab/ci/status/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 43d2b1b40d4..73b6ab5a635 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -23,7 +23,7 @@ module Gitlab end def group - self.class.name.demodulize.downcase.underscore + self.class.name.demodulize.underscore end def has_details? From dbe2ac8ccc07f53669214eb954489a6cb233d4e9 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 16 Dec 2016 17:11:04 -0200 Subject: [PATCH 240/386] Fix rubucop offenses --- lib/gitlab/bitbucket_import/importer.rb | 50 +++++++++---------- .../bitbucket/representation/issue_spec.rb | 1 - 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index f3760640655..d5287c69e6e 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -60,8 +60,6 @@ module Gitlab create_labels - gitlab_issue = nil - client.issues(repo).each do |issue| begin description = '' @@ -87,31 +85,33 @@ module Gitlab gitlab_issue.labels << @labels[label_name] - if gitlab_issue.persisted? - client.issue_comments(repo, issue.iid).each do |comment| - # The note can be blank for issue service messages like "Changed title: ..." - # We would like to import those comments as well but there is no any - # specific parameter that would allow to process them, it's just an empty comment. - # To prevent our importer from just crashing or from creating useless empty comments - # we do this check. - next unless comment.note.present? + import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted? + end + end - note = '' - note += @formatter.author_line(comment.author) unless find_user_id(comment.author) - note += comment.note + def import_issue_comments(issue, gitlab_issue) + client.issue_comments(repo, issue.iid).each do |comment| + # The note can be blank for issue service messages like "Changed title: ..." + # We would like to import those comments as well but there is no any + # specific parameter that would allow to process them, it's just an empty comment. + # To prevent our importer from just crashing or from creating useless empty comments + # we do this check. + next unless comment.note.present? - begin - gitlab_issue.notes.create!( - project: project, - note: note, - author_id: gitlab_user_id(project, comment.author), - created_at: comment.created_at, - updated_at: comment.updated_at - ) - rescue StandardError => e - errors << { type: :issue_comment, iid: issue.iid, errors: e.message } - end - end + note = '' + note += @formatter.author_line(comment.author) unless find_user_id(comment.author) + note += comment.note + + begin + gitlab_issue.notes.create!( + project: project, + note: note, + author_id: gitlab_user_id(project, comment.author), + created_at: comment.created_at, + updated_at: comment.updated_at + ) + rescue StandardError => e + errors << { type: :issue_comment, iid: issue.iid, errors: e.message } end end end diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb index 9a195bebd31..20f47224aa8 100644 --- a/spec/lib/bitbucket/representation/issue_spec.rb +++ b/spec/lib/bitbucket/representation/issue_spec.rb @@ -14,7 +14,6 @@ describe Bitbucket::Representation::Issue do it { expect(described_class.new({}).milestone).to be_nil } end - describe '#author' do it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' } }).author).to eq('Ben') } it { expect(described_class.new({}).author).to be_nil } From d58fffb27766f49b82866c5460fe218854f9596e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 16 Dec 2016 14:30:28 -0600 Subject: [PATCH 241/386] fix margin on alert stripes within ":flash_message" block --- app/assets/stylesheets/framework/layout.scss | 8 ++++++++ app/views/layouts/_page.html.haml | 7 ++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index 66711aa1804..bef24162924 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -32,6 +32,14 @@ body { } } +.alert-wrapper { + margin-bottom: $gl-padding; + + .alert { + margin-bottom: 0; + } +} + /* The following prevents side effects related to iOS Safari's implementation of -webkit-overflow-scrolling: touch, which is applied to the body by jquery.nicescroling plugin to force hardware acceleration for momentum scrolling. Side diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index a9a0b149049..54d02ee8e4b 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -22,9 +22,10 @@ = render "layouts/nav/#{nav}" .content-wrapper{ class: "#{layout_nav_class}" } = yield :sub_nav - = render "layouts/broadcast" - = render "layouts/flash" - = yield :flash_message + .alert-wrapper + = render "layouts/broadcast" + = render "layouts/flash" + = yield :flash_message %div{ class: "#{(container_class unless @no_container)} #{@content_class}" } .content{ id: "content-body" } = yield From dc9b0913670dc8de5e01a40fd28fa44f48defc9e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 16 Dec 2016 14:38:01 -0600 Subject: [PATCH 242/386] stripe colors for successive alert-warning blocks --- app/assets/stylesheets/framework/layout.scss | 27 +++++++++++++++++++ .../stylesheets/framework/variables.scss | 1 - app/assets/stylesheets/pages/projects.scss | 6 ----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index bef24162924..59fae61a44f 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -38,6 +38,33 @@ body { .alert { margin-bottom: 0; } + + /* Stripe the background colors so that adjacent alert-warnings are distinct from one another */ + .alert-warning { + transition: background-color 0.15s, border-color 0.15s; + background-color: lighten($gl-warning, 4%); + border-color: lighten($gl-warning, 4%); + } + + .alert-warning + .alert-warning { + background-color: $gl-warning; + border-color: $gl-warning; + } + + .alert-warning + .alert-warning + .alert-warning { + background-color: darken($gl-warning, 4%); + border-color: darken($gl-warning, 4%); + } + + .alert-warning + .alert-warning + .alert-warning + .alert-warning { + background-color: darken($gl-warning, 8%); + border-color: darken($gl-warning, 8%); + } + + .alert-warning:only-of-type { + background-color: $gl-warning; + border-color: $gl-warning; + } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 267fcd77b38..0b156d61a23 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -474,7 +474,6 @@ $project-option-descr-color: #54565b; $project-breadcrumb-color: #999; $project-private-forks-notice-odd: #2aa056; $project-network-controls-color: #888; -$project-limit-message-bg: #f28d35; /* * Runners diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 9c3dbc58ae0..3b1b375133d 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -6,12 +6,6 @@ } } -.no-ssh-key-message, -.project-limit-message { - background-color: $project-limit-message-bg; - margin-bottom: 0; -} - .new_project, .edit-project { From ac07ce64a8a62ed4901787063001c7175a5e2cd5 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 16 Dec 2016 14:41:32 -0600 Subject: [PATCH 243/386] add CHANGELOG.md entry for !8151 --- ...lean-up-css-for-project-alerts-and-flash-notifications.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/25743-clean-up-css-for-project-alerts-and-flash-notifications.yml diff --git a/changelogs/unreleased/25743-clean-up-css-for-project-alerts-and-flash-notifications.yml b/changelogs/unreleased/25743-clean-up-css-for-project-alerts-and-flash-notifications.yml new file mode 100644 index 00000000000..0a81124de0d --- /dev/null +++ b/changelogs/unreleased/25743-clean-up-css-for-project-alerts-and-flash-notifications.yml @@ -0,0 +1,4 @@ +--- +title: fix colors and margins for adjacent alert banners +merge_request: 8151 +author: From fe9a372c0b64b47117fc0a64dbdfb514f757ee6e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 16 Dec 2016 19:11:48 -0200 Subject: [PATCH 244/386] Fix import issues method --- lib/gitlab/bitbucket_import/importer.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index d5287c69e6e..7d2f92d577a 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -79,13 +79,13 @@ module Gitlab created_at: issue.created_at, updated_at: issue.updated_at ) + + gitlab_issue.labels << @labels[label_name] + + import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted? rescue StandardError => e errors << { type: :issue, iid: issue.iid, errors: e.message } end - - gitlab_issue.labels << @labels[label_name] - - import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted? end end From 178f9ebb67bddbd2719cbd140967103b57f2c0c7 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 15 Dec 2016 19:32:03 -0500 Subject: [PATCH 245/386] Respect newlines in commit messages on network graph --- app/assets/javascripts/network/branch_graph.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js index 64b19a54893..20a68780cd5 100644 --- a/app/assets/javascripts/network/branch_graph.js +++ b/app/assets/javascripts/network/branch_graph.js @@ -356,7 +356,7 @@ icon = this.image(gon.relative_url_root + commit.author.icon, x, y, 20, 20); nameText = this.text(x + 25, y + 10, commit.author.name); idText = this.text(x, y + 35, commit.id); - messageText = this.text(x, y + 50, commit.message); + messageText = this.text(x, y + 50, commit.message.replace(/\r?\n/g, " \n ")); textSet = this.set(icon, nameText, idText, messageText).attr({ "text-anchor": "start", font: "12px Monaco, monospace" @@ -368,6 +368,7 @@ idText.attr({ fill: "#AAA" }); + messageText.node.style["white-space"] = "pre"; this.textWrap(messageText, boxWidth - 50); rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({ fill: "#FFF", @@ -404,16 +405,21 @@ s.push("\n"); x = 0; } - x += word.length * letterWidth; - s.push(word + " "); + if (word === "\n") { + s.push("\n"); + x = 0; + } else { + s.push(word + " "); + x += word.length * letterWidth; + } } t.attr({ - text: s.join("") + text: s.join("").trim() }); b = t.getBBox(); - h = Math.abs(b.y2) - Math.abs(b.y) + 1; + h = Math.abs(b.y2) + 1; return t.attr({ - y: b.y + h + y: h }); }; From a3be4aeb7a71cc940394a5f13d09e79fcafdb1d5 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 16 Dec 2016 19:51:40 -0200 Subject: [PATCH 246/386] Avoid use of Hash#dig to keep compatibility with Ruby 2.1 --- lib/bitbucket/representation/comment.rb | 2 +- lib/bitbucket/representation/issue.rb | 6 +++--- lib/bitbucket/representation/pull_request.rb | 10 +++++----- lib/bitbucket/representation/pull_request_comment.rb | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb index 3c75e9368fa..4937aa9728f 100644 --- a/lib/bitbucket/representation/comment.rb +++ b/lib/bitbucket/representation/comment.rb @@ -6,7 +6,7 @@ module Bitbucket end def note - raw.dig('content', 'raw') + raw.fetch('content', {}).fetch('raw', nil) end def created_at diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index 3af731753d1..054064395c3 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -12,11 +12,11 @@ module Bitbucket end def author - raw.dig('reporter', 'username') + raw.fetch('reporter', {}).fetch('username', nil) end def description - raw.dig('content', 'raw') + raw.fetch('content', {}).fetch('raw', nil) end def state @@ -28,7 +28,7 @@ module Bitbucket end def milestone - raw.dig('milestone', 'name') + raw['milestone']['name'] if raw['milestone'].present? end def created_at diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb index e37c9a62c0e..eebf8093380 100644 --- a/lib/bitbucket/representation/pull_request.rb +++ b/lib/bitbucket/representation/pull_request.rb @@ -2,7 +2,7 @@ module Bitbucket module Representation class PullRequest < Representation::Base def author - raw.dig('author', 'username') + raw.fetch('author', {}).fetch('username', nil) end def description @@ -36,19 +36,19 @@ module Bitbucket end def source_branch_name - source_branch.dig('branch', 'name') + source_branch.fetch('branch', {}).fetch('name', nil) end def source_branch_sha - source_branch.dig('commit', 'hash') + source_branch.fetch('commit', {}).fetch('hash', nil) end def target_branch_name - target_branch.dig('branch', 'name') + target_branch.fetch('branch', {}).fetch('name', nil) end def target_branch_sha - target_branch.dig('commit', 'hash') + target_branch.fetch('commit', {}).fetch('hash', nil) end private diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb index 4f3809fbcea..4f8efe03bae 100644 --- a/lib/bitbucket/representation/pull_request_comment.rb +++ b/lib/bitbucket/representation/pull_request_comment.rb @@ -18,7 +18,7 @@ module Bitbucket end def parent_id - raw.dig('parent', 'id') + raw.fetch('parent', {}).fetch('id', nil) end def inline? From 34ed74fab90904eb77c8b07779401650684af5b1 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 16 Dec 2016 19:59:53 -0200 Subject: [PATCH 247/386] Avoid use of Hash#dig to keep compatibility with Ruby 2.1 --- app/controllers/concerns/oauth_applications.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb index 7210ed3eb32..9849aa93fa6 100644 --- a/app/controllers/concerns/oauth_applications.rb +++ b/app/controllers/concerns/oauth_applications.rb @@ -6,7 +6,7 @@ module OauthApplications end def prepare_scopes - scopes = params.dig(:doorkeeper_application, :scopes) + scopes = params.fetch(:doorkeeper_application, {}).fetch(:scopes, nil) if scopes params[:doorkeeper_application][:scopes] = scopes.join(' ') From 09388b2021034173156ba8958fa290b01e3a447d Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Tue, 18 Oct 2016 18:22:18 +0600 Subject: [PATCH 248/386] Adds sort dropdown for group members --- app/assets/stylesheets/pages/members.scss | 56 ++++++++++++++----- .../groups/group_members_controller.rb | 10 +++- .../projects/project_members_controller.rb | 5 +- app/helpers/members_helper.rb | 30 +++++++--- app/helpers/sorting_helper.rb | 41 +++++++++++++- .../groups/group_members/index.html.haml | 1 + .../projects/project_members/index.html.haml | 1 + .../shared/members/_sort_dropdown.html.haml | 11 ++++ 8 files changed, 128 insertions(+), 27 deletions(-) create mode 100644 app/views/shared/members/_sort_dropdown.html.haml diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 5f3bbb40ba0..b7521133ce5 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -78,6 +78,20 @@ float: right; } + .dropdown { + width: 100%; + margin-top: 5px; + + .dropdown-menu-toggle { + width: 100%; + } + + @media (min-width: $screen-sm-min) { + top: 2.4px; + width: 155px; + } + } + .form-control { width: 100%; padding-right: 35px; @@ -85,18 +99,34 @@ @media (min-width: $screen-sm-min) { width: 350px; } - } -} -.member-search-btn { - position: absolute; - right: 0; - top: 0; - height: 35px; - padding-left: 10px; - padding-right: 10px; - color: $gray-darkest; - background: transparent; - border: 0; - outline: 0; + &.input-short { + @media (min-width: $screen-md-min) { + width: 170px; + } + + @media (min-width: $screen-lg-min) { + width: 210px; + } + } + } + + .member-search-btn { + position: absolute; + right: 4px; + top: 0; + height: 35px; + padding-left: 10px; + padding-right: 10px; + color: $gray-darkest; + background: transparent; + border: 0; + outline: 0; + + @media (min-width: $screen-sm-min) { + right: 160px; + top: 8px; + } + } + } diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 940a3ad20ba..af8abb96d4a 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -10,11 +10,15 @@ class Groups::GroupMembersController < Groups::ApplicationController @members = @members.non_invite unless can?(current_user, :admin_group, @group) if params[:search].present? - users = @group.users.search(params[:search]).to_a - @members = @members.where(user_id: users) + @members = @members.joins(:user).merge(User.search(params[:search])) end - @members = @members.order('access_level DESC').page(params[:page]).per(50) + if params[:sort].present? + @members = @members.joins(:user).merge(User.sort(@sort = params[:sort])) + end + + + @members = @members.page(params[:page]).per(50) @requesters = AccessRequestsFinder.new(@group).execute(current_user) @group_member = @group.group_members.new diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 53308948f62..e4aba4b700e 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -1,10 +1,12 @@ class Projects::ProjectMembersController < Projects::ApplicationController include MembershipActions + include SortingHelper # Authorize before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] def index + @sort = params[:sort].presence || sort_value_name @group_links = @project.project_group_links @project_members = @project.project_members @@ -40,7 +42,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController @project_members = Member. where(wheres.join(' OR ')). - order(access_level: :desc).page(params[:page]) + sort(@sort). + page(params[:page]) @requesters = AccessRequestsFinder.new(@project).execute(current_user) diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index 877c77050be..f1b8962eb36 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -11,17 +11,17 @@ module MembersHelper text = 'Are you sure you want to ' action = - if member.request? - if member.user == user - 'withdraw your access request for' + if member.request? + if member.user == user + 'withdraw your access request for' + else + "deny #{member.user.name}'s request to join" + end + elsif member.invite? + "revoke the invitation for #{member.invite_email} to join" else - "deny #{member.user.name}'s request to join" + "remove #{member.user.name} from" end - elsif member.invite? - "revoke the invitation for #{member.invite_email} to join" - else - "remove #{member.user.name} from" - end text << action << " the #{member.source.human_name} #{member.real_source_type.humanize(capitalize: false)}?" end @@ -36,4 +36,16 @@ module MembersHelper "Are you sure you want to leave the " \ "\"#{member_source.human_name}\" #{member_source.class.to_s.humanize(capitalize: false)}?" end + + def filter_group_project_member_path(options = {}) + exist_opts = { + search: params[:search], + sort: params[:sort] + } + + options = exist_opts.merge(options) + path = request.path + path << "?#{options.to_param}" + path + end end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 8b138a8e69f..9c6f9f741ed 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -25,7 +25,7 @@ module SortingHelper sort_value_recently_updated => sort_title_recently_updated, sort_value_oldest_updated => sort_title_oldest_updated, sort_value_recently_created => sort_title_recently_created, - sort_value_oldest_created => sort_title_oldest_created, + sort_value_oldest_created => sort_title_oldest_created } if current_controller?('admin/projects') @@ -35,6 +35,17 @@ module SortingHelper options end + def member_sort_options_hash + { + sort_value_last_joined => sort_title_last_joined, + sort_value_oldest_joined => sort_title_oldest_joined, + sort_value_name => sort_title_name_asc, + sort_value_name_desc => sort_title_name_desc, + sort_value_recently_signin => sort_title_recently_signin, + sort_value_oldest_signin => sort_title_oldest_signin + } + end + def sort_title_priority 'Priority' end @@ -95,6 +106,34 @@ module SortingHelper 'Most popular' end + def sort_title_last_joined + 'Last joined' + end + + def sort_title_oldest_joined + 'Oldest joined' + end + + def sort_title_name_asc + 'Name, ascending' + end + + def sort_title_name_desc + 'Name, descending' + end + + def sort_value_last_joined + 'last_joined' + end + + def sort_value_oldest_joined + 'oldest_joined' + end + + def sort_value_name_desc + 'name_desc' + end + def sort_value_priority 'priority' end diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index ebf9aca7700..bc5d3c797ac 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -21,6 +21,7 @@ = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } = icon("search") + = render 'shared/members/sort_dropdown' .panel.panel-default .panel-heading Users with access to diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index bdeb704b6da..4f1cec20f85 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -21,6 +21,7 @@ = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } = icon("search") + = render 'shared/members/sort_dropdown' - if @group_links.any? = render 'groups', group_links: @group_links diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml new file mode 100644 index 00000000000..42c09636ba3 --- /dev/null +++ b/app/views/shared/members/_sort_dropdown.html.haml @@ -0,0 +1,11 @@ +- @sort ||= sort_value_last_joined + +.dropdown.inline + = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }, { id: 'sort-members-dropdown' }) + %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable + %li.dropdown-header + Sort by + - member_sort_options_hash.each do |value, title| + %li + = link_to filter_group_project_member_path(sort: value), class: ("is-active" if @sort == value) do + = title From f438a2aabe7f75c0d6ed3a6c1ce2ab5a62e33b31 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 16 Nov 2016 18:42:39 -0200 Subject: [PATCH 249/386] Add CHANGELOG entry --- .../23573-sort-functionality-for-project-member.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/23573-sort-functionality-for-project-member.yml diff --git a/changelogs/unreleased/23573-sort-functionality-for-project-member.yml b/changelogs/unreleased/23573-sort-functionality-for-project-member.yml new file mode 100644 index 00000000000..73de0a6351b --- /dev/null +++ b/changelogs/unreleased/23573-sort-functionality-for-project-member.yml @@ -0,0 +1,4 @@ +--- +title: Add sorting functionality for group/project members +merge_request: 7032 +author: From f54ddbf1ecb91ea96b52e18f87ff5ed10ad45747 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 16 Nov 2016 19:35:33 -0200 Subject: [PATCH 250/386] Fix MembersHelper --- app/helpers/members_helper.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index f1b8962eb36..cf8fdfca1bd 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -11,17 +11,17 @@ module MembersHelper text = 'Are you sure you want to ' action = - if member.request? - if member.user == user - 'withdraw your access request for' - else - "deny #{member.user.name}'s request to join" - end - elsif member.invite? - "revoke the invitation for #{member.invite_email} to join" + if member.request? + if member.user == user + 'withdraw your access request for' else - "remove #{member.user.name} from" + "deny #{member.user.name}'s request to join" end + elsif member.invite? + "revoke the invitation for #{member.invite_email} to join" + else + "remove #{member.user.name} from" + end text << action << " the #{member.source.human_name} #{member.real_source_type.humanize(capitalize: false)}?" end @@ -39,8 +39,8 @@ module MembersHelper def filter_group_project_member_path(options = {}) exist_opts = { - search: params[:search], - sort: params[:sort] + search: params[:search], + sort: params[:sort] } options = exist_opts.merge(options) From 59d43bea80b56faff54630934694b317cda9f899 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 16 Nov 2016 19:37:51 -0200 Subject: [PATCH 251/386] Fix sort functionality for group/project members --- .../groups/group_members_controller.rb | 14 ++++--------- app/models/member.rb | 20 +++++++++++++++++++ app/models/user.rb | 6 ++++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index af8abb96d4a..812aaa4c191 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -6,19 +6,13 @@ class Groups::GroupMembersController < Groups::ApplicationController def index @project = @group.projects.find(params[:project_id]) if params[:project_id] + @members = @group.group_members @members = @members.non_invite unless can?(current_user, :admin_group, @group) - - if params[:search].present? - @members = @members.joins(:user).merge(User.search(params[:search])) - end - - if params[:sort].present? - @members = @members.joins(:user).merge(User.sort(@sort = params[:sort])) - end - - + @members = @members.search(params[:search]) if params[:search].present? + @members = @members.sort(@sort = params[:sort]) if params[:sort].present? @members = @members.page(params[:page]).per(50) + @requesters = AccessRequestsFinder.new(@group).execute(current_user) @group_member = @group.group_members.new diff --git a/app/models/member.rb b/app/models/member.rb index 3b65587c66b..b82b16e6f33 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -57,6 +57,11 @@ class Member < ActiveRecord::Base scope :owners, -> { active.where(access_level: OWNER) } scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) } + scope :order_name_asc, -> { joins(:user).merge(User.order_name_asc) } + scope :order_name_desc, -> { joins(:user).merge(User.order_name_desc) } + scope :order_recent_sign_in, -> { joins(:user).merge(User.order_recent_sign_in) } + scope :order_oldest_sign_in, -> { joins(:user).merge(User.order_oldest_sign_in) } + before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? } after_create :send_invite, if: :invite?, unless: :importing? @@ -72,6 +77,21 @@ class Member < ActiveRecord::Base default_value_for :notification_level, NotificationSetting.levels[:global] class << self + def search(query) + joins(:user).merge(User.search(query)) + end + + def sort(method) + case method.to_s + when 'recent_sign_in' then order_recent_sign_in + when 'oldest_sign_in' then order_oldest_sign_in + when 'last_joined' then order_created_desc + when 'oldest_joined' then order_created_asc + else + order_by(method) + end + end + def access_for_user_ids(user_ids) where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h end diff --git a/app/models/user.rb b/app/models/user.rb index 1bd28203523..a2812d68384 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -178,6 +178,8 @@ class User < ActiveRecord::Base scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members WHERE user_id IS NOT NULL AND requested_at IS NULL)') } scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) } + scope :order_recent_sign_in, -> { reorder(last_sign_in_at: :desc) } + scope :order_oldest_sign_in, -> { reorder(last_sign_in_at: :asc) } def self.with_two_factor joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id"). @@ -205,8 +207,8 @@ class User < ActiveRecord::Base def sort(method) case method.to_s - when 'recent_sign_in' then reorder(last_sign_in_at: :desc) - when 'oldest_sign_in' then reorder(last_sign_in_at: :asc) + when 'recent_sign_in' then order_recent_sign_in + when 'oldest_sign_in' then order_oldest_sign_in else order_by(method) end From 7783267d6cc41b6a5ced907316aefbc71f2a8e7e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 16 Nov 2016 19:45:35 -0200 Subject: [PATCH 252/386] Add option to sort group/project members by access level --- app/helpers/sorting_helper.rb | 18 ++++++++++++++++++ app/models/member.rb | 2 ++ 2 files changed, 20 insertions(+) diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 9c6f9f741ed..f03c4627050 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -37,6 +37,8 @@ module SortingHelper def member_sort_options_hash { + sort_value_access_level_asc => sort_title_access_level_asc, + sort_value_access_level_desc => sort_title_access_level_desc, sort_value_last_joined => sort_title_last_joined, sort_value_oldest_joined => sort_title_oldest_joined, sort_value_name => sort_title_name_asc, @@ -114,6 +116,14 @@ module SortingHelper 'Oldest joined' end + def sort_title_access_level_asc + 'Access level, ascending' + end + + def sort_title_access_level_desc + 'Access level, descending' + end + def sort_title_name_asc 'Name, ascending' end @@ -130,6 +140,14 @@ module SortingHelper 'oldest_joined' end + def sort_value_access_level_asc + 'access_level_asc' + end + + def sort_value_access_level_desc + 'access_level_desc' + end + def sort_value_name_desc 'name_desc' end diff --git a/app/models/member.rb b/app/models/member.rb index b82b16e6f33..8c36a631ac4 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -83,6 +83,8 @@ class Member < ActiveRecord::Base def sort(method) case method.to_s + when 'access_level_asc' then reorder(access_level: :asc) + when 'access_level_desc' then reorder(access_level: :desc) when 'recent_sign_in' then order_recent_sign_in when 'oldest_sign_in' then order_oldest_sign_in when 'last_joined' then order_created_desc From 4b7a3d0c38126489c42a207411b510b5a7b3264b Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 16 Nov 2016 20:24:43 -0200 Subject: [PATCH 253/386] Add feature spec for sort functionality on group/project members list --- spec/features/groups/members/sorting_spec.rb | 82 +++++++++++++++++++ .../features/projects/members/sorting_spec.rb | 82 +++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 spec/features/groups/members/sorting_spec.rb create mode 100644 spec/features/projects/members/sorting_spec.rb diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb new file mode 100644 index 00000000000..5990e43c261 --- /dev/null +++ b/spec/features/groups/members/sorting_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +feature 'Groups > Members > Sorting', feature: true do + let(:owner) { create(:user, name: 'John Doe') } + let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } + let(:group) { create(:group) } + + background do + group.add_owner(owner) + group.add_developer(developer) + + login_as(owner) + end + + scenario 'sorts by access level ascending' do + visit_members_list(sort: :access_level_asc) + + expect(first_member).to include(developer.name) + expect(second_member).to include(owner.name) + end + + scenario 'sorts by access level descending' do + visit_members_list(sort: :access_level_desc) + + expect(first_member).to include(owner.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by last joined' do + visit_members_list(sort: :last_joined) + + expect(first_member).to include(developer.name) + expect(second_member).to include(owner.name) + end + + scenario 'sorts by oldest joined' do + visit_members_list(sort: :oldest_joined) + + expect(first_member).to include(owner.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by name ascending' do + visit_members_list(sort: :name_asc) + + expect(first_member).to include(owner.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by name descending' do + visit_members_list(sort: :name_desc) + + expect(first_member).to include(developer.name) + expect(second_member).to include(owner.name) + end + + scenario 'sorts by recent sign in' do + visit_members_list(sort: :recent_sign_in) + + expect(first_member).to include(owner.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by oldest sign in' do + visit_members_list(sort: :oldest_sign_in) + + expect(first_member).to include(developer.name) + expect(second_member).to include(owner.name) + end + + def visit_members_list(sort:) + visit group_group_members_path(group.to_param, sort: sort) + end + + def first_member + page.all('ul.content-list > li').first.text + end + + def second_member + page.all('ul.content-list > li').last.text + end +end diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb new file mode 100644 index 00000000000..597e72a5599 --- /dev/null +++ b/spec/features/projects/members/sorting_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +feature 'Projects > Members > Sorting', feature: true do + let(:master) { create(:user, name: 'John Doe') } + let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } + let(:project) { create(:empty_project) } + + background do + project.team << [master, :master] + project.team << [developer, :developer] + + login_as(master) + end + + scenario 'sorts by access level ascending' do + visit_members_list(sort: :access_level_asc) + + expect(first_member).to include(developer.name) + expect(second_member).to include(master.name) + end + + scenario 'sorts by access level descending' do + visit_members_list(sort: :access_level_desc) + + expect(first_member).to include(master.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by last joined' do + visit_members_list(sort: :last_joined) + + expect(first_member).to include(developer.name) + expect(second_member).to include(master.name) + end + + scenario 'sorts by oldest joined' do + visit_members_list(sort: :oldest_joined) + + expect(first_member).to include(master.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by name ascending' do + visit_members_list(sort: :name_asc) + + expect(first_member).to include(master.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by name descending' do + visit_members_list(sort: :name_desc) + + expect(first_member).to include(developer.name) + expect(second_member).to include(master.name) + end + + scenario 'sorts by recent sign in' do + visit_members_list(sort: :recent_sign_in) + + expect(first_member).to include(master.name) + expect(second_member).to include(developer.name) + end + + scenario 'sorts by oldest sign in' do + visit_members_list(sort: :oldest_sign_in) + + expect(first_member).to include(developer.name) + expect(second_member).to include(master.name) + end + + def visit_members_list(sort:) + visit namespace_project_project_members_path(project.namespace.to_param, project.to_param, sort: sort) + end + + def first_member + page.all('ul.content-list > li').first.text + end + + def second_member + page.all('ul.content-list > li').last.text + end +end From 3a2905f5072f451b4b1f284c4383b4054a8892af Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 16 Nov 2016 20:37:50 -0200 Subject: [PATCH 254/386] Sort group/project members alphabetically by default --- app/controllers/groups/group_members_controller.rb | 4 +++- app/views/shared/members/_sort_dropdown.html.haml | 2 -- spec/features/groups/members/sorting_spec.rb | 7 +++++++ spec/features/projects/members/sorting_spec.rb | 7 +++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 812aaa4c191..4f273a8d4f0 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,16 +1,18 @@ class Groups::GroupMembersController < Groups::ApplicationController include MembershipActions + include SortingHelper # Authorize before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access] def index + @sort = params[:sort].presence || sort_value_name @project = @group.projects.find(params[:project_id]) if params[:project_id] @members = @group.group_members @members = @members.non_invite unless can?(current_user, :admin_group, @group) @members = @members.search(params[:search]) if params[:search].present? - @members = @members.sort(@sort = params[:sort]) if params[:sort].present? + @members = @members.sort(@sort) @members = @members.page(params[:page]).per(50) @requesters = AccessRequestsFinder.new(@group).execute(current_user) diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml index 42c09636ba3..a2b47b605b7 100644 --- a/app/views/shared/members/_sort_dropdown.html.haml +++ b/app/views/shared/members/_sort_dropdown.html.haml @@ -1,5 +1,3 @@ -- @sort ||= sort_value_last_joined - .dropdown.inline = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }, { id: 'sort-members-dropdown' }) %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb index 5990e43c261..5904025d978 100644 --- a/spec/features/groups/members/sorting_spec.rb +++ b/spec/features/groups/members/sorting_spec.rb @@ -12,6 +12,13 @@ feature 'Groups > Members > Sorting', feature: true do login_as(owner) end + scenario 'sorts alphabetically by default' do + visit_members_list(sort: nil) + + expect(first_member).to include(owner.name) + expect(second_member).to include(developer.name) + end + scenario 'sorts by access level ascending' do visit_members_list(sort: :access_level_asc) diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index 597e72a5599..61d3a112b93 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -12,6 +12,13 @@ feature 'Projects > Members > Sorting', feature: true do login_as(master) end + scenario 'sorts alphabetically by default' do + visit_members_list(sort: nil) + + expect(first_member).to include(master.name) + expect(second_member).to include(developer.name) + end + scenario 'sorts by access level ascending' do visit_members_list(sort: :access_level_asc) From c3af880aeea12565bc33a29284a9836e3b800019 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 17 Nov 2016 15:51:34 -0200 Subject: [PATCH 255/386] Remove unnecessary curly braces from sort dropdown partial --- app/views/shared/members/_sort_dropdown.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml index a2b47b605b7..8f28324c5f6 100644 --- a/app/views/shared/members/_sort_dropdown.html.haml +++ b/app/views/shared/members/_sort_dropdown.html.haml @@ -1,5 +1,5 @@ .dropdown.inline - = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }, { id: 'sort-members-dropdown' }) + = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }, id: 'sort-members-dropdown') %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable %li.dropdown-header Sort by From 06f696dd0a6326ca521d81203901618afa0f9a9a Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 17 Nov 2016 15:52:15 -0200 Subject: [PATCH 256/386] Refactor MembersHelper#filter_group_project_member_path --- app/helpers/members_helper.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index cf8fdfca1bd..41d471cc92f 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -38,12 +38,8 @@ module MembersHelper end def filter_group_project_member_path(options = {}) - exist_opts = { - search: params[:search], - sort: params[:sort] - } + options = params.slice(:search, :sort).merge(options) - options = exist_opts.merge(options) path = request.path path << "?#{options.to_param}" path From 621b08d8b5cd5ffe22f5bc7fddecc16821c9bd56 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 18 Nov 2016 15:50:29 -0200 Subject: [PATCH 257/386] Fix sort functionality on project/group members to return invited users --- app/models/member.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index 8c36a631ac4..0312aef32fa 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -57,10 +57,10 @@ class Member < ActiveRecord::Base scope :owners, -> { active.where(access_level: OWNER) } scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) } - scope :order_name_asc, -> { joins(:user).merge(User.order_name_asc) } - scope :order_name_desc, -> { joins(:user).merge(User.order_name_desc) } - scope :order_recent_sign_in, -> { joins(:user).merge(User.order_recent_sign_in) } - scope :order_oldest_sign_in, -> { joins(:user).merge(User.order_oldest_sign_in) } + scope :order_name_asc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'ASC')) } + scope :order_name_desc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'DESC')) } + scope :order_recent_sign_in, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.last_sign_in_at', 'DESC')) } + scope :order_oldest_sign_in, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.last_sign_in_at', 'ASC')) } before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? } @@ -94,6 +94,17 @@ class Member < ActiveRecord::Base end end + def left_join_users + users = User.arel_table + members = Member.arel_table + + member_users = members.join(users, Arel::Nodes::OuterJoin). + on(members[:user_id].eq(users[:id])). + join_sources + + joins(member_users) + end + def access_for_user_ids(user_ids) where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h end From b667d834bc77d29b982c3858d6f788b2a4a34c0d Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 21 Nov 2016 16:05:11 -0200 Subject: [PATCH 258/386] Remove unused id from shared members sort dropdown --- app/views/shared/members/_sort_dropdown.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml index 8f28324c5f6..3fad8406374 100644 --- a/app/views/shared/members/_sort_dropdown.html.haml +++ b/app/views/shared/members/_sort_dropdown.html.haml @@ -1,5 +1,5 @@ .dropdown.inline - = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }, id: 'sort-members-dropdown') + = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }) %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable %li.dropdown-header Sort by From 0ef2c8dfbe923fc5e90d2bae306b9ef2aac85112 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 22 Nov 2016 19:10:31 -0200 Subject: [PATCH 259/386] Use factories to create project/group membership on specs --- spec/features/groups/members/sorting_spec.rb | 4 ++-- spec/features/projects/members/sorting_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb index 5904025d978..42ba8b614a3 100644 --- a/spec/features/groups/members/sorting_spec.rb +++ b/spec/features/groups/members/sorting_spec.rb @@ -6,8 +6,8 @@ feature 'Groups > Members > Sorting', feature: true do let(:group) { create(:group) } background do - group.add_owner(owner) - group.add_developer(developer) + create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago) + create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago) login_as(owner) end diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index 61d3a112b93..3754dfa658d 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -6,8 +6,8 @@ feature 'Projects > Members > Sorting', feature: true do let(:project) { create(:empty_project) } background do - project.team << [master, :master] - project.team << [developer, :developer] + create(:project_member, :master, user: master, project: project, created_at: 5.days.ago) + create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago) login_as(master) end From 5479fc9107507fd441de0661dd2c4c0826fb40f0 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 24 Nov 2016 11:17:51 -0200 Subject: [PATCH 260/386] Undo changes on members search button stylesheet --- app/assets/stylesheets/pages/members.scss | 37 +++++++++++------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index b7521133ce5..f2417efeebb 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -110,23 +110,22 @@ } } } - - .member-search-btn { - position: absolute; - right: 4px; - top: 0; - height: 35px; - padding-left: 10px; - padding-right: 10px; - color: $gray-darkest; - background: transparent; - border: 0; - outline: 0; - - @media (min-width: $screen-sm-min) { - right: 160px; - top: 8px; - } - } - +} + +.member-search-btn { + position: absolute; + right: 4px; + top: 0; + height: 35px; + padding-left: 10px; + padding-right: 10px; + color: $gray-darkest; + background: transparent; + border: 0; + outline: 0; + + @media (min-width: $screen-sm-min) { + right: 160px; + top: 8px; + } } From eac34fd9a3347b873fc963856b2f0e2104fe7a9b Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 2 Dec 2016 16:24:28 +0600 Subject: [PATCH 261/386] Fix sort dropdown alignment --- app/assets/stylesheets/pages/members.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index f2417efeebb..36ee5d17211 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -83,11 +83,12 @@ margin-top: 5px; .dropdown-menu-toggle { + vertical-align: middle; width: 100%; } @media (min-width: $screen-sm-min) { - top: 2.4px; + margin-top: 0; width: 155px; } } @@ -126,6 +127,5 @@ @media (min-width: $screen-sm-min) { right: 160px; - top: 8px; } } From ecea127cd1e2ac382a71e03ebc16de44f762b2dd Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 2 Dec 2016 17:52:10 +0600 Subject: [PATCH 262/386] Improve test for sort dropdown on members page --- app/views/shared/members/_sort_dropdown.html.haml | 2 +- spec/features/groups/members/sorting_spec.rb | 9 +++++++++ spec/features/projects/members/sorting_spec.rb | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml index 3fad8406374..bad0891f9f2 100644 --- a/app/views/shared/members/_sort_dropdown.html.haml +++ b/app/views/shared/members/_sort_dropdown.html.haml @@ -1,4 +1,4 @@ -.dropdown.inline +.dropdown.inline.member-sort-dropdown = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }) %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable %li.dropdown-header diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb index 42ba8b614a3..608aedd3471 100644 --- a/spec/features/groups/members/sorting_spec.rb +++ b/spec/features/groups/members/sorting_spec.rb @@ -17,6 +17,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(owner.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending') end scenario 'sorts by access level ascending' do @@ -24,6 +25,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(owner.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending') end scenario 'sorts by access level descending' do @@ -31,6 +33,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(owner.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending') end scenario 'sorts by last joined' do @@ -38,6 +41,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(owner.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Last joined') end scenario 'sorts by oldest joined' do @@ -45,6 +49,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(owner.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined') end scenario 'sorts by name ascending' do @@ -52,6 +57,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(owner.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending') end scenario 'sorts by name descending' do @@ -59,6 +65,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(owner.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending') end scenario 'sorts by recent sign in' do @@ -66,6 +73,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(owner.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in') end scenario 'sorts by oldest sign in' do @@ -73,6 +81,7 @@ feature 'Groups > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(owner.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in') end def visit_members_list(sort:) diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index 3754dfa658d..d6ebb523f95 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -17,6 +17,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(master.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending') end scenario 'sorts by access level ascending' do @@ -24,6 +25,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(master.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending') end scenario 'sorts by access level descending' do @@ -31,6 +33,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(master.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending') end scenario 'sorts by last joined' do @@ -38,6 +41,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(master.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Last joined') end scenario 'sorts by oldest joined' do @@ -45,6 +49,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(master.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined') end scenario 'sorts by name ascending' do @@ -52,6 +57,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(master.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending') end scenario 'sorts by name descending' do @@ -59,6 +65,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(master.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending') end scenario 'sorts by recent sign in' do @@ -66,6 +73,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(master.name) expect(second_member).to include(developer.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in') end scenario 'sorts by oldest sign in' do @@ -73,6 +81,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(first_member).to include(developer.name) expect(second_member).to include(master.name) + expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in') end def visit_members_list(sort:) From e644b8d683d7b9fa2c411fe65e1c828ba9908b57 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 9 Dec 2016 17:19:47 -0200 Subject: [PATCH 263/386] Fix query in Projects::ProjectMembersController to fetch members --- app/controllers/projects/project_members_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index e4aba4b700e..3aec6f18e27 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -37,8 +37,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) end - wheres = ["id IN (#{@project_members.select(:id).to_sql})"] - wheres << "id IN (#{group_members.select(:id).to_sql})" if group_members + wheres = ["members.id IN (#{@project_members.select(:id).to_sql})"] + wheres << "members.id IN (#{group_members.select(:id).to_sql})" if group_members @project_members = Member. where(wheres.join(' OR ')). From bea9795531534a2ce060d14aef4f99577c17f2c3 Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Sat, 17 Dec 2016 06:19:12 +0000 Subject: [PATCH 264/386] Fix link from doc/development/performance.md to 'Performance Monitoring' --- doc/development/performance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/performance.md b/doc/development/performance.md index 5c43ae7b79a..f936a49a2aa 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -37,7 +37,7 @@ graphs/dashboards. GitLab provides built-in tools to aid the process of improving performance: * [Sherlock](profiling.md#sherlock) -* [GitLab Performance Monitoring](../administration/monitoring/performance/monitoring.md) +* [GitLab Performance Monitoring](../administration/monitoring/performance/introduction.md) * [Request Profiling](../administration/monitoring/performance/request_profiling.md) GitLab employees can use GitLab.com's performance monitoring systems located at From f11caaf4692afdde0a2c458b3682aef3f9658b6a Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 12 Dec 2016 09:31:48 +0100 Subject: [PATCH 265/386] Setup mattermost session --- lib/mattermost/mattermost.rb | 102 +++++++++++++++++++++++++ spec/lib/mattermost/mattermost_spec.rb | 42 ++++++++++ 2 files changed, 144 insertions(+) create mode 100644 lib/mattermost/mattermost.rb create mode 100644 spec/lib/mattermost/mattermost_spec.rb diff --git a/lib/mattermost/mattermost.rb b/lib/mattermost/mattermost.rb new file mode 100644 index 00000000000..84d016bb197 --- /dev/null +++ b/lib/mattermost/mattermost.rb @@ -0,0 +1,102 @@ +module Mattermost + class NoSessionError < StandardError; end + # This class' prime objective is to obtain a session token on a Mattermost + # instance with SSO configured where this GitLab instance is the provider. + # + # The process depends on OAuth, but skips a step in the authentication cycle. + # For example, usually a user would click the 'login in GitLab' button on + # Mattermost, which would yield a 302 status code and redirects you to GitLab + # to approve the use of your account on Mattermost. Which would trigger a + # callback so Mattermost knows this request is approved and gets the required + # data to create the user account etc. + # + # This class however skips the button click, and also the approval phase to + # speed up the process and keep it without manual action and get a session + # going. + class Mattermost + include Doorkeeper::Helpers::Controller + include HTTParty + + attr_accessor :current_resource_owner + + def initialize(uri, current_user) + self.class.base_uri(uri) + + @current_resource_owner = current_user + end + + def with_session + raise NoSessionError unless create + yield + destroy + end + + # Next methods are needed for Doorkeeper + def pre_auth + @pre_auth ||= Doorkeeper::OAuth::PreAuthorization.new( + Doorkeeper.configuration, server.client_via_uid, params) + end + + def authorization + @authorization ||= strategy.request + end + + def strategy + @strategy ||= server.authorization_request(pre_auth.response_type) + end + + def request + @request ||= OpenStruct.new(parameters: params) + end + + def params + Rack::Utils.parse_query(@oauth_uri.query).symbolize_keys + end + + private + + def create + return unless oauth_uri + return unless token_uri + + self.class.headers("Cookie" => "MMAUTHTOKEN=#{request_token}") + + request_token + end + + def destroy + post('/api/v3/users/logout') + end + + def oauth_uri + response = get("/api/v3/oauth/gitlab/login", follow_redirects: false) + return unless 300 <= response.code && response.code < 400 + + redirect_uri = response.headers['location'] + return unless redirect_uri + + @oauth_uri ||= URI.parse(redirect_uri) + end + + def token_uri + @token_uri ||= if @oauth_uri + authorization.authorize.redirect_uri if pre_auth.authorizable? + end + end + + def request_token + @request_token ||= if @token_uri + response = get(@token_uri, follow_redirects: false) + response.headers['token'] if 200 <= response.code && response.code < 400 + end + end + + def get(path, options = {}) + self.class.get(path, options) + end + + def post(path, options = {}) + self.class.post(path, options) + end + end +end diff --git a/spec/lib/mattermost/mattermost_spec.rb b/spec/lib/mattermost/mattermost_spec.rb new file mode 100644 index 00000000000..7c99b4df9f3 --- /dev/null +++ b/spec/lib/mattermost/mattermost_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Mattermost::Mattermost do + let(:user) { create(:user) } + + subject { described_class.new('http://localhost:8065', user) } + + # Needed for doorman to function + it { is_expected.to respond_to(:current_resource_owner) } + it { is_expected.to respond_to(:request) } + it { is_expected.to respond_to(:authorization) } + it { is_expected.to respond_to(:strategy) } + + describe '#with session' do + let!(:stub) do + WebMock.stub_request(:get, 'http://localhost:8065/api/v3/oauth/gitlab/login'). + to_return(headers: { 'location' => 'http://mylocation.com' }, status: 307) + end + + context 'without oauth uri' do + it 'makes a request to the oauth uri' do + expect { subject.with_session }.to raise_error(Mattermost::NoSessionError) + end + + context 'with oauth_uri' do + let!(:doorkeeper) do + Doorkeeper::Application.create(name: "GitLab Mattermost", + redirect_uri: "http://localhost:8065/signup/gitlab/complete\nhttp://localhost:8065/login/gitlab/complete", + scopes: "") + end + + context 'without token_uri' do + it 'can not create a session' do + expect do + subject.with_session + end.to raise_error(Mattermost::NoSessionError) + end + end + end + end + end +end From a31cdb29e49b62f0227963cbc54b6564a3ee9da8 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 15 Dec 2016 14:32:50 +0100 Subject: [PATCH 266/386] Improve session tests --- lib/mattermost/{mattermost.rb => session.rb} | 9 ++- spec/lib/mattermost/mattermost_spec.rb | 42 ------------ spec/lib/mattermost/session_spec.rb | 68 ++++++++++++++++++++ 3 files changed, 74 insertions(+), 45 deletions(-) rename lib/mattermost/{mattermost.rb => session.rb} (95%) delete mode 100644 spec/lib/mattermost/mattermost_spec.rb create mode 100644 spec/lib/mattermost/session_spec.rb diff --git a/lib/mattermost/mattermost.rb b/lib/mattermost/session.rb similarity index 95% rename from lib/mattermost/mattermost.rb rename to lib/mattermost/session.rb index 84d016bb197..d14121c91a0 100644 --- a/lib/mattermost/mattermost.rb +++ b/lib/mattermost/session.rb @@ -13,13 +13,14 @@ module Mattermost # This class however skips the button click, and also the approval phase to # speed up the process and keep it without manual action and get a session # going. - class Mattermost + class Session include Doorkeeper::Helpers::Controller include HTTParty attr_accessor :current_resource_owner def initialize(uri, current_user) + # Sets the base uri for HTTParty, so we can use paths self.class.base_uri(uri) @current_resource_owner = current_user @@ -27,8 +28,10 @@ module Mattermost def with_session raise NoSessionError unless create - yield + result = yield destroy + + result end # Next methods are needed for Doorkeeper @@ -85,7 +88,7 @@ module Mattermost end def request_token - @request_token ||= if @token_uri + @request_token ||= begin response = get(@token_uri, follow_redirects: false) response.headers['token'] if 200 <= response.code && response.code < 400 end diff --git a/spec/lib/mattermost/mattermost_spec.rb b/spec/lib/mattermost/mattermost_spec.rb deleted file mode 100644 index 7c99b4df9f3..00000000000 --- a/spec/lib/mattermost/mattermost_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'spec_helper' - -describe Mattermost::Mattermost do - let(:user) { create(:user) } - - subject { described_class.new('http://localhost:8065', user) } - - # Needed for doorman to function - it { is_expected.to respond_to(:current_resource_owner) } - it { is_expected.to respond_to(:request) } - it { is_expected.to respond_to(:authorization) } - it { is_expected.to respond_to(:strategy) } - - describe '#with session' do - let!(:stub) do - WebMock.stub_request(:get, 'http://localhost:8065/api/v3/oauth/gitlab/login'). - to_return(headers: { 'location' => 'http://mylocation.com' }, status: 307) - end - - context 'without oauth uri' do - it 'makes a request to the oauth uri' do - expect { subject.with_session }.to raise_error(Mattermost::NoSessionError) - end - - context 'with oauth_uri' do - let!(:doorkeeper) do - Doorkeeper::Application.create(name: "GitLab Mattermost", - redirect_uri: "http://localhost:8065/signup/gitlab/complete\nhttp://localhost:8065/login/gitlab/complete", - scopes: "") - end - - context 'without token_uri' do - it 'can not create a session' do - expect do - subject.with_session - end.to raise_error(Mattermost::NoSessionError) - end - end - end - end - end -end diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb new file mode 100644 index 00000000000..a93bab877da --- /dev/null +++ b/spec/lib/mattermost/session_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe Mattermost::Session do + let(:user) { create(:user) } + + subject { described_class.new('http://localhost:8065', user) } + + # Needed for doorkeeper to function + it { is_expected.to respond_to(:current_resource_owner) } + it { is_expected.to respond_to(:request) } + it { is_expected.to respond_to(:authorization) } + it { is_expected.to respond_to(:strategy) } + + describe '#with session' do + let(:location) { 'http://location.tld' } + let!(:stub) do + WebMock.stub_request(:get, 'http://localhost:8065/api/v3/oauth/gitlab/login'). + to_return(headers: { 'location' => location }, status: 307) + end + + context 'without oauth uri' do + it 'makes a request to the oauth uri' do + expect { subject.with_session }.to raise_error(Mattermost::NoSessionError) + end + end + + context 'with oauth_uri' do + let!(:doorkeeper) do + Doorkeeper::Application.create(name: "GitLab Mattermost", + redirect_uri: "http://localhost:8065/signup/gitlab/complete\nhttp://localhost:8065/login/gitlab/complete", + scopes: "") + end + + context 'without token_uri' do + it 'can not create a session' do + expect do + subject.with_session + end.to raise_error(Mattermost::NoSessionError) + end + end + + context 'with token_uri' do + let(:state) { "eyJhY3Rpb24iOiJsb2dpbiIsImhhc2giOiIkMmEkMTAkVC9wYVlEaTdIUS8vcWdKRmdOOUllZUptaUNJWUlvNVNtNEcwU2NBMXFqelNOVmVPZ1cxWUsifQ%3D%3D" } + let(:location) { "http://locahost:8065/oauth/authorize?response_type=code&client_id=#{doorkeeper.uid}&redirect_uri=http%3A%2F%2Flocalhost:8065%2Fsignup%2Fgitlab%2Fcomplete&state=#{state}" } + + before do + WebMock.stub_request(:get, /http:\/\/localhost:8065\/signup\/gitlab\/complete*/). + to_return(headers: { 'token' => 'thisworksnow' }, status: 202) + end + + it 'can setup a session' do + expect(subject).to receive(:destroy) + + subject.with_session { 1 + 1 } + end + + it 'returns the value of the block' do + WebMock.stub_request(:post, "http://localhost:8065/api/v3/users/logout"). + to_return(headers: { 'token' => 'thisworksnow' }, status: 200) + + value = subject.with_session { 1 + 1 } + + expect(value).to be(2) + end + end + end + end +end From 9bcc4d4de5510a14ae891105645b4d59891ba78d Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 15 Dec 2016 21:06:17 +0100 Subject: [PATCH 267/386] Ensure the session is destroyed --- lib/mattermost/session.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index d14121c91a0..f4629585da7 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -28,10 +28,12 @@ module Mattermost def with_session raise NoSessionError unless create - result = yield - destroy - result + begin + yield + ensure + destroy + end end # Next methods are needed for Doorkeeper From 48ebfaa49146b8f6fcb24b063f22d553b2f20395 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 11:31:26 +0100 Subject: [PATCH 268/386] Improve Mattermost Session specs --- lib/mattermost/session.rb | 23 +++++++------ spec/lib/mattermost/session_spec.rb | 53 +++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index f4629585da7..7d0290be5a1 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -17,7 +17,7 @@ module Mattermost include Doorkeeper::Helpers::Controller include HTTParty - attr_accessor :current_resource_owner + attr_accessor :current_resource_owner, :token def initialize(uri, current_user) # Sets the base uri for HTTParty, so we can use paths @@ -64,9 +64,9 @@ module Mattermost return unless oauth_uri return unless token_uri - self.class.headers("Cookie" => "MMAUTHTOKEN=#{request_token}") - - request_token + self.token = request_token + self.class.headers("Cookie" => "MMAUTHTOKEN=#{self.token}") + self.token end def destroy @@ -84,16 +84,17 @@ module Mattermost end def token_uri - @token_uri ||= if @oauth_uri - authorization.authorize.redirect_uri if pre_auth.authorizable? - end + @token_uri ||= + if @oauth_uri + authorization.authorize.redirect_uri if pre_auth.authorizable? + end end def request_token - @request_token ||= begin - response = get(@token_uri, follow_redirects: false) - response.headers['token'] if 200 <= response.code && response.code < 400 - end + response = get(@token_uri, follow_redirects: false) + if 200 <= response.code && response.code < 400 + response.headers['token'] + end end def get(path, options = {}) diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb index a93bab877da..69d677930bc 100644 --- a/spec/lib/mattermost/session_spec.rb +++ b/spec/lib/mattermost/session_spec.rb @@ -1,9 +1,12 @@ require 'spec_helper' -describe Mattermost::Session do +describe Mattermost::Session, type: :request do let(:user) { create(:user) } - subject { described_class.new('http://localhost:8065', user) } + let(:gitlab_url) { "http://gitlab.com" } + let(:mattermost_url) { "http://mattermost.com" } + + subject { described_class.new(mattermost_url, user) } # Needed for doorkeeper to function it { is_expected.to respond_to(:current_resource_owner) } @@ -14,7 +17,7 @@ describe Mattermost::Session do describe '#with session' do let(:location) { 'http://location.tld' } let!(:stub) do - WebMock.stub_request(:get, 'http://localhost:8065/api/v3/oauth/gitlab/login'). + WebMock.stub_request(:get, "#{mattermost_url}/api/v3/oauth/gitlab/login"). to_return(headers: { 'location' => location }, status: 307) end @@ -26,9 +29,10 @@ describe Mattermost::Session do context 'with oauth_uri' do let!(:doorkeeper) do - Doorkeeper::Application.create(name: "GitLab Mattermost", - redirect_uri: "http://localhost:8065/signup/gitlab/complete\nhttp://localhost:8065/login/gitlab/complete", - scopes: "") + Doorkeeper::Application.create( + name: "GitLab Mattermost", + redirect_uri: "#{mattermost_url}/signup/gitlab/complete\n#{mattermost_url}/login/gitlab/complete", + scopes: "") end context 'without token_uri' do @@ -40,24 +44,43 @@ describe Mattermost::Session do end context 'with token_uri' do - let(:state) { "eyJhY3Rpb24iOiJsb2dpbiIsImhhc2giOiIkMmEkMTAkVC9wYVlEaTdIUS8vcWdKRmdOOUllZUptaUNJWUlvNVNtNEcwU2NBMXFqelNOVmVPZ1cxWUsifQ%3D%3D" } - let(:location) { "http://locahost:8065/oauth/authorize?response_type=code&client_id=#{doorkeeper.uid}&redirect_uri=http%3A%2F%2Flocalhost:8065%2Fsignup%2Fgitlab%2Fcomplete&state=#{state}" } + let(:state) { "state" } + let(:params) do + { response_type: "code", + client_id: doorkeeper.uid, + redirect_uri: "#{mattermost_url}/signup/gitlab/complete", + state: state } + end + let(:location) do + "#{gitlab_url}/oauth/authorize?#{URI.encode_www_form(params)}" + end before do - WebMock.stub_request(:get, /http:\/\/localhost:8065\/signup\/gitlab\/complete*/). - to_return(headers: { 'token' => 'thisworksnow' }, status: 202) + WebMock.stub_request(:get, "#{mattermost_url}/signup/gitlab/complete"). + with(query: hash_including({ 'state' => state })). + to_return do |request| + post "/oauth/token", + client_id: doorkeeper.uid, + client_secret: doorkeeper.secret, + redirect_uri: params[:redirect_uri], + grant_type: 'authorization_code', + code: request.uri.query_values['code'] + + if response.status == 200 + { headers: { 'token' => 'thisworksnow' }, status: 202 } + end + end + + WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout"). + to_return(headers: { Cookie: 'MMAUTHTOKEN=thisworksnow' }, status: 200) end it 'can setup a session' do - expect(subject).to receive(:destroy) - subject.with_session { 1 + 1 } + expect(subject.token).not_to be_nil end it 'returns the value of the block' do - WebMock.stub_request(:post, "http://localhost:8065/api/v3/users/logout"). - to_return(headers: { 'token' => 'thisworksnow' }, status: 200) - value = subject.with_session { 1 + 1 } expect(value).to be(2) From e663725961de66ac838d0a5a85978656938e74f4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 12:20:42 +0100 Subject: [PATCH 269/386] Store mattermost_url in settings --- config/gitlab.yml.example | 6 ++++++ config/initializers/1_settings.rb | 7 +++++++ lib/mattermost/session.rb | 17 +++++++++-------- spec/lib/mattermost/session_spec.rb | 18 +++++++++++++----- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 327e4a7937c..b8b41a0d86c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -153,6 +153,12 @@ production: &base # The location where LFS objects are stored (default: shared/lfs-objects). # storage_path: shared/lfs-objects + ## Mattermost + ## For enabling Add to Mattermost button + mattermost: + enabled: false + host: 'https://mattermost.example.com' + ## Gravatar ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html gravatar: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 0ee1b1ec634..45404e579ae 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -261,6 +261,13 @@ Settings['lfs'] ||= Settingslogic.new({}) Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil? Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"), Rails.root) +# +# Mattermost +# +Settings['mattermost'] ||= Settingslogic.new({}) +Settings.mattermost['enabled'] = false if Settings.mattermost['enabled'].nil? +Settings.mattermost['host'] = nil unless Settings.mattermost.enabled + # # Gravatar # diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index 7d0290be5a1..a3715bed482 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -17,12 +17,11 @@ module Mattermost include Doorkeeper::Helpers::Controller include HTTParty + base_uri Settings.mattermost.host + attr_accessor :current_resource_owner, :token - def initialize(uri, current_user) - # Sets the base uri for HTTParty, so we can use paths - self.class.base_uri(uri) - + def initialize(current_user) @current_resource_owner = current_user end @@ -30,7 +29,7 @@ module Mattermost raise NoSessionError unless create begin - yield + yield self ensure destroy end @@ -65,7 +64,9 @@ module Mattermost return unless token_uri self.token = request_token - self.class.headers("Cookie" => "MMAUTHTOKEN=#{self.token}") + @headers = { + "Authorization": "Bearer #{self.token}" + } self.token end @@ -98,11 +99,11 @@ module Mattermost end def get(path, options = {}) - self.class.get(path, options) + self.class.get(path, options.merge(headers: @headers)) end def post(path, options = {}) - self.class.post(path, options) + self.class.post(path, options.merge(headers: @headers)) end end end diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb index 69d677930bc..3c2eddbd221 100644 --- a/spec/lib/mattermost/session_spec.rb +++ b/spec/lib/mattermost/session_spec.rb @@ -6,7 +6,7 @@ describe Mattermost::Session, type: :request do let(:gitlab_url) { "http://gitlab.com" } let(:mattermost_url) { "http://mattermost.com" } - subject { described_class.new(mattermost_url, user) } + subject { described_class.new(user) } # Needed for doorkeeper to function it { is_expected.to respond_to(:current_resource_owner) } @@ -14,6 +14,10 @@ describe Mattermost::Session, type: :request do it { is_expected.to respond_to(:authorization) } it { is_expected.to respond_to(:strategy) } + before do + described_class.base_uri(mattermost_url) + end + describe '#with session' do let(:location) { 'http://location.tld' } let!(:stub) do @@ -72,18 +76,22 @@ describe Mattermost::Session, type: :request do end WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout"). - to_return(headers: { Cookie: 'MMAUTHTOKEN=thisworksnow' }, status: 200) + to_return(headers: { Authorization: 'token thisworksnow' }, status: 200) end it 'can setup a session' do - subject.with_session { 1 + 1 } + subject.with_session do |session| + end + expect(subject.token).not_to be_nil end it 'returns the value of the block' do - value = subject.with_session { 1 + 1 } + result = subject.with_session do |session| + "value" + end - expect(value).to be(2) + expect(result).to eq("value") end end end From c9610e0a052526adb3138dccf6114d710979a0b7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 13:43:01 +0100 Subject: [PATCH 270/386] Fix rubocop failures --- config/initializers/1_settings.rb | 4 +- lib/mattermost/session.rb | 90 ++++++++++++++++--------------- 2 files changed, 50 insertions(+), 44 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 45404e579ae..ddea325c6ca 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -265,8 +265,8 @@ Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || # Mattermost # Settings['mattermost'] ||= Settingslogic.new({}) -Settings.mattermost['enabled'] = false if Settings.mattermost['enabled'].nil? -Settings.mattermost['host'] = nil unless Settings.mattermost.enabled +Settings.mattermost['enabled'] = false if Settings.mattermost['enabled'].nil? +Settings.mattermost['host'] = nil unless Settings.mattermost.enabled # # Gravatar diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index a3715bed482..fb8d7d97f8a 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -54,48 +54,7 @@ module Mattermost end def params - Rack::Utils.parse_query(@oauth_uri.query).symbolize_keys - end - - private - - def create - return unless oauth_uri - return unless token_uri - - self.token = request_token - @headers = { - "Authorization": "Bearer #{self.token}" - } - self.token - end - - def destroy - post('/api/v3/users/logout') - end - - def oauth_uri - response = get("/api/v3/oauth/gitlab/login", follow_redirects: false) - return unless 300 <= response.code && response.code < 400 - - redirect_uri = response.headers['location'] - return unless redirect_uri - - @oauth_uri ||= URI.parse(redirect_uri) - end - - def token_uri - @token_uri ||= - if @oauth_uri - authorization.authorize.redirect_uri if pre_auth.authorizable? - end - end - - def request_token - response = get(@token_uri, follow_redirects: false) - if 200 <= response.code && response.code < 400 - response.headers['token'] - end + Rack::Utils.parse_query(oauth_uri.query).symbolize_keys end def get(path, options = {}) @@ -105,5 +64,52 @@ module Mattermost def post(path, options = {}) self.class.post(path, options.merge(headers: @headers)) end + + private + + def create + return unless oauth_uri + return unless token_uri + + @token = request_token + @headers = { + Authorization: "Bearer #{@token}" + } + + @token + end + + def destroy + post('/api/v3/users/logout') + end + + def oauth_uri + return @oauth_uri if defined?(@oauth_uri) + + @oauth_uri = nil + + response = get("/api/v3/oauth/gitlab/login", follow_redirects: false) + return unless 300 <= response.code && response.code < 400 + + redirect_uri = response.headers['location'] + return unless redirect_uri + + @oauth_uri = URI.parse(redirect_uri) + end + + def token_uri + @token_uri ||= + if oauth_uri + authorization.authorize.redirect_uri if pre_auth.authorizable? + end + end + + def request_token + response = get(token_uri, follow_redirects: false) + + if 200 <= response.code && response.code < 400 + response.headers['token'] + end + end end end From dc68a91c444d3da85bbcb87e4ef4e841be9f3887 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 17 Dec 2016 13:34:35 +0100 Subject: [PATCH 271/386] Fix CI/CD statuses actions' CSS on pipeline graphs --- app/views/ci/status/_graph_badge.html.haml | 2 +- spec/features/projects/pipelines/pipeline_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml index c7d04ab61e9..9f3a9c0c6b2 100644 --- a/app/views/ci/status/_graph_badge.html.haml +++ b/app/views/ci/status/_graph_badge.html.haml @@ -2,7 +2,7 @@ - subject = local_assigns.fetch(:subject) - status = subject.detailed_status(current_user) -- klass = "ci-status-icon ci-status-icon-#{status}" +- klass = "ci-status-icon ci-status-icon-#{status.group}" - if status.has_details? = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status.label}" } do diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 0a77eaa123c..57f1e75ea2c 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -99,7 +99,7 @@ describe "Pipelines", feature: true, js: true do context 'when pipeline has manual builds' do it 'shows the skipped icon and a play action for the manual build' do page.within('a[data-title="manual build - manual play action"]') do - expect(page).to have_selector('.ci-status-icon-skipped') + expect(page).to have_selector('.ci-status-icon-manual') expect(page).to have_content('manual') end From a871746e8a3f45e0c732e56ea475de1d8050c93d Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Fri, 16 Dec 2016 17:30:17 +0500 Subject: [PATCH 272/386] Move admin deploy keys spinach test to rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- features/admin/deploy_keys.feature | 16 ------- features/steps/admin/deploy_keys.rb | 46 ------------------- spec/features/admin/admin_deploy_keys_spec.rb | 29 ++++++++++++ 3 files changed, 29 insertions(+), 62 deletions(-) delete mode 100644 features/admin/deploy_keys.feature delete mode 100644 features/steps/admin/deploy_keys.rb create mode 100644 spec/features/admin/admin_deploy_keys_spec.rb diff --git a/features/admin/deploy_keys.feature b/features/admin/deploy_keys.feature deleted file mode 100644 index 33439cd1e85..00000000000 --- a/features/admin/deploy_keys.feature +++ /dev/null @@ -1,16 +0,0 @@ -@admin -Feature: Admin Deploy Keys - Background: - Given I sign in as an admin - And there are public deploy keys in system - - Scenario: Deploy Keys list - When I visit admin deploy keys page - Then I should see all public deploy keys - - Scenario: Deploy Keys new - When I visit admin deploy keys page - And I click 'New Deploy Key' - And I submit new deploy key - Then I should be on admin deploy keys page - And I should see newly created deploy key diff --git a/features/steps/admin/deploy_keys.rb b/features/steps/admin/deploy_keys.rb deleted file mode 100644 index 56787eeb6b3..00000000000 --- a/features/steps/admin/deploy_keys.rb +++ /dev/null @@ -1,46 +0,0 @@ -class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - step 'there are public deploy keys in system' do - create(:deploy_key, public: true) - create(:another_deploy_key, public: true) - end - - step 'I should see all public deploy keys' do - DeployKey.are_public.each do |p| - expect(page).to have_content p.title - end - end - - step 'I visit admin deploy key page' do - visit admin_deploy_key_path(deploy_key) - end - - step 'I visit admin deploy keys page' do - visit admin_deploy_keys_path - end - - step 'I click \'New Deploy Key\'' do - click_link 'New Deploy Key' - end - - step 'I submit new deploy key' do - fill_in "deploy_key_title", with: "laptop" - fill_in "deploy_key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop" - click_button "Create" - end - - step 'I should be on admin deploy keys page' do - expect(current_path).to eq admin_deploy_keys_path - end - - step 'I should see newly created deploy key' do - expect(page).to have_content(deploy_key.title) - end - - def deploy_key - @deploy_key ||= DeployKey.are_public.first - end -end diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb new file mode 100644 index 00000000000..8bf68480785 --- /dev/null +++ b/spec/features/admin/admin_deploy_keys_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +RSpec.describe 'admin deploy keys', type: :feature do + let!(:deploy_key) { create(:deploy_key, public: true) } + let!(:another_deploy_key) { create(:another_deploy_key, public: true) } + + before do + login_as(:admin) + end + + it 'show all public deploy keys' do + visit admin_deploy_keys_path + + expect(page).to have_content(deploy_key.title) + expect(page).to have_content(another_deploy_key.title) + end + + it 'creates new deploy key' do + visit admin_deploy_keys_path + + click_link 'New Deploy Key' + fill_in 'deploy_key_title', with: 'laptop' + fill_in 'deploy_key_key', with: 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop' + click_button 'Create' + + expect(current_path).to eq admin_deploy_keys_path + expect(page).to have_content('laptop') + end +end From 5a6252ff4ab4fd24d4c3f1b14d4551061e7acb65 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Fri, 16 Dec 2016 16:45:52 +0500 Subject: [PATCH 273/386] Move admin application spinach test to rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- features/admin/applications.feature | 18 ------ features/steps/admin/applications.rb | 55 ------------------- features/steps/shared/paths.rb | 4 -- .../admin/admin_manage_applications_spec.rb | 36 ++++++++++++ 4 files changed, 36 insertions(+), 77 deletions(-) delete mode 100644 features/admin/applications.feature delete mode 100644 features/steps/admin/applications.rb create mode 100644 spec/features/admin/admin_manage_applications_spec.rb diff --git a/features/admin/applications.feature b/features/admin/applications.feature deleted file mode 100644 index 2a00e1666c0..00000000000 --- a/features/admin/applications.feature +++ /dev/null @@ -1,18 +0,0 @@ -@admin -Feature: Admin Applications - Background: - Given I sign in as an admin - And I visit applications page - - Scenario: I can manage application - Then I click on new application button - And I should see application form - Then I fill application form out and submit - And I see application - Then I click edit - And I see edit application form - Then I change name of application and submit - And I see that application was changed - Then I visit applications page - And I click to remove application - Then I see that application is removed \ No newline at end of file diff --git a/features/steps/admin/applications.rb b/features/steps/admin/applications.rb deleted file mode 100644 index 7c12cb96921..00000000000 --- a/features/steps/admin/applications.rb +++ /dev/null @@ -1,55 +0,0 @@ -class Spinach::Features::AdminApplications < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - step 'I click on new application button' do - click_on 'New Application' - end - - step 'I should see application form' do - expect(page).to have_content "New application" - end - - step 'I fill application form out and submit' do - fill_in :doorkeeper_application_name, with: 'test' - fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' - click_on "Submit" - end - - step 'I see application' do - expect(page).to have_content "Application: test" - expect(page).to have_content "Application Id" - expect(page).to have_content "Secret" - end - - step 'I click edit' do - click_on "Edit" - end - - step 'I see edit application form' do - expect(page).to have_content "Edit application" - end - - step 'I change name of application and submit' do - expect(page).to have_content "Edit application" - fill_in :doorkeeper_application_name, with: 'test_changed' - click_on "Submit" - end - - step 'I see that application was changed' do - expect(page).to have_content "test_changed" - expect(page).to have_content "Application Id" - expect(page).to have_content "Secret" - end - - step 'I click to remove application' do - page.within '.oauth-applications' do - click_on "Destroy" - end - end - - step "I see that application is removed" do - expect(page.find(".oauth-applications")).not_to have_content "test_changed" - end -end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 82c07d4f536..a78d0a775ba 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -207,10 +207,6 @@ module SharedPaths visit admin_spam_logs_path end - step 'I visit applications page' do - visit admin_applications_path - end - # ---------------------------------------- # Generic Project # ---------------------------------------- diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb new file mode 100644 index 00000000000..c2c618b5659 --- /dev/null +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +RSpec.describe 'admin manage applications', feature: true do + before do + login_as :admin + end + + it do + visit admin_applications_path + + click_on 'New Application' + expect(page).to have_content('New application') + + fill_in :doorkeeper_application_name, with: 'test' + fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' + click_on 'Submit' + expect(page).to have_content('Application: test') + expect(page).to have_content('Application Id') + expect(page).to have_content('Secret') + + click_on 'Edit' + expect(page).to have_content('Edit application') + + fill_in :doorkeeper_application_name, with: 'test_changed' + click_on 'Submit' + expect(page).to have_content('test_changed') + expect(page).to have_content('Application Id') + expect(page).to have_content('Secret') + + visit admin_applications_path + page.within '.oauth-applications' do + click_on 'Destroy' + end + expect(page.find('.oauth-applications')).not_to have_content('test_changed') + end +end From 27fd32613dac9c093d538e576131e7fda3f7d8e3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 17 Dec 2016 13:46:16 +0100 Subject: [PATCH 274/386] Add `ci-manual` status CSS with darkest gray color --- app/assets/stylesheets/pages/status.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index f3b0608e545..637df7e349e 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -113,6 +113,19 @@ fill: $gl-gray-light; } } + + &.ci-manual { + color: $gl-gray-dark; + border-color: $gl-gray-dark; + + &:not(span):hover { + background-color: rgba( $gl-gray-dark, .07); + } + + svg { + fill: $gl-gray-dark; + } + } } } From 3932c20ee61d404196022258d7b7e83c137e4519 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sat, 17 Dec 2016 13:38:18 +0000 Subject: [PATCH 275/386] Improve spacing and fixes manual status color --- app/assets/stylesheets/framework/icons.scss | 2 +- app/assets/stylesheets/pages/status.scss | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index b37847e3d96..8624a25c052 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -54,6 +54,6 @@ color: $gl-text-color; svg { - fill: $gray-darkest; + fill: $gl-text-color; } } diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 637df7e349e..7ce8f7757f3 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,5 +1,6 @@ .container-fluid { .ci-status { + display: inline-block; padding: 2px 7px; margin-right: 10px; border: 1px solid $gray-darker; @@ -15,8 +16,7 @@ height: 13px; width: 13px; position: relative; - top: 1px; - margin-right: 3px; + top: 2px; overflow: visible; } @@ -115,15 +115,15 @@ } &.ci-manual { - color: $gl-gray-dark; - border-color: $gl-gray-dark; + color: $gl-text-color; + border-color: $gl-text-color; &:not(span):hover { - background-color: rgba( $gl-gray-dark, .07); + background-color: rgba( $gl-text-color, .07); } svg { - fill: $gl-gray-dark; + fill: $gl-text-color; } } } From e1e677a653a1c6658ad7f3c9f4fbae7f120c8a8f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sat, 17 Dec 2016 12:55:26 +0000 Subject: [PATCH 276/386] Changes after review --- app/views/ci/status/_mini_graph_badge.html.haml | 5 ----- app/views/projects/ci/pipelines/_pipeline.html.haml | 13 +++++++------ 2 files changed, 7 insertions(+), 11 deletions(-) delete mode 100644 app/views/ci/status/_mini_graph_badge.html.haml diff --git a/app/views/ci/status/_mini_graph_badge.html.haml b/app/views/ci/status/_mini_graph_badge.html.haml deleted file mode 100644 index 34e07e75ae8..00000000000 --- a/app/views/ci/status/_mini_graph_badge.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- status = subject.detailed_status(current_user) -- icon = "#{status.icon}_borderless" -- klass = "ci-status-icon ci-status-icon-#{status}" - -%span{ class: klass }= custom_icon(icon) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 0651447f616..2e2bac9cd32 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -44,10 +44,11 @@ - pipeline.stages.each do |stage| - if stage.status - detailed_status = stage.detailed_status(current_user) - - details_path = detailed_status.details_path if detailed_status.has_details? - klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status}" - hasMultipleBuilds = stage.statuses.count > 1 - - tooltip = "#{stage.name.titleize}: #{stage.status || 'not found'}" + - icon_status = "#{detailed_status.icon}_borderless" + - icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status}" + - tooltip = "#{stage.name}: #{detailed_status.label || 'not found'}" .stage-container.mini-pipeline-graph - if hasMultipleBuilds @@ -55,7 +56,7 @@ %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } %span{ class: klass } %span.mini-pipeline-graph-icon-container - = render 'ci/status/mini_graph_badge', subject: stage + %span{ class: icon_status_klass }= custom_icon(icon_status) = icon('caret-down', class: 'dropdown-caret') .dropdown-menu.grouped-pipeline-dropdown .arrow-up @@ -64,10 +65,10 @@ %li.dropdown-build = render 'ci/status/graph_badge', subject: status - else - - if details_path - = link_to details_path, class: klass, title: tooltip do + - if detailed_status.has_details? + = link_to detailed_status.details_path, class: klass, title: tooltip do %span.mini-pipeline-graph-icon-container - = render 'ci/status/mini_graph_badge', subject: stage + %span{ class: icon_status_klass }= custom_icon(icon_status) %td - if pipeline.duration From 25b84b2f849d10005d214cc537ed833503d8dc89 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sat, 17 Dec 2016 14:09:38 +0000 Subject: [PATCH 277/386] Fix extra spacing in all rgba methods in status file --- app/assets/stylesheets/pages/status.scss | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 7ce8f7757f3..055dacd81f4 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -25,7 +25,7 @@ border-color: $gl-danger; &:not(span):hover { - background-color: rgba( $gl-danger, .07); + background-color: rgba($gl-danger, .07); } svg { @@ -39,7 +39,7 @@ border-color: $gl-success; &:not(span):hover { - background-color: rgba( $gl-success, .07); + background-color: rgba($gl-success, .07); } svg { @@ -52,7 +52,7 @@ border-color: $gl-info; &:not(span):hover { - background-color: rgba( $gl-info, .07); + background-color: rgba($gl-info, .07); } svg { @@ -66,7 +66,7 @@ border-color: $gl-gray; &:not(span):hover { - background-color: rgba( $gl-gray, .07); + background-color: rgba($gl-gray, .07); } svg { @@ -79,7 +79,7 @@ border-color: $gl-warning; &:not(span):hover { - background-color: rgba( $gl-warning, .07); + background-color: rgba($gl-warning, .07); } svg { @@ -92,7 +92,7 @@ border-color: $blue-normal; &:not(span):hover { - background-color: rgba( $blue-normal, .07); + background-color: rgba($blue-normal, .07); } svg { @@ -106,7 +106,7 @@ border-color: $gl-gray-light; &:not(span):hover { - background-color: rgba( $gl-gray-light, .07); + background-color: rgba($gl-gray-light, .07); } svg { @@ -119,7 +119,7 @@ border-color: $gl-text-color; &:not(span):hover { - background-color: rgba( $gl-text-color, .07); + background-color: rgba($gl-text-color, .07); } svg { From 8c2297f00b96cafea2adf7918086b3ef5fd049fc Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 14 Dec 2016 15:51:25 -0600 Subject: [PATCH 278/386] Move access request buttons to project header --- app/assets/stylesheets/pages/projects.scss | 8 +++-- app/views/projects/_home_panel.html.haml | 3 ++ app/views/projects/show.html.haml | 1 - .../members/_access_request_buttons.html.haml | 29 ++++++++++--------- changelogs/unreleased/leave-project-btn.yml | 4 +++ 5 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 changelogs/unreleased/leave-project-btn.yml diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 3b1b375133d..29a8f5b3995 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -178,8 +178,10 @@ .download-button, .dropdown-toggle, .notification-dropdown, - .project-dropdown { + .project-dropdown, + .access-button { margin-left: 10px; + vertical-align: top; } .notification-dropdown .dropdown-menu { @@ -201,7 +203,7 @@ display: inline-block; input { - height: 29px; + height: 28px; } } @@ -255,7 +257,7 @@ line-height: 13px; padding: $gl-vert-padding $gl-padding; letter-spacing: .4px; - padding: 7px 14px; + padding: 6px 14px; text-align: center; vertical-align: middle; touch-action: manipulation; diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 5a04c3318cf..d13177f6776 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -26,3 +26,6 @@ - if @project.feature_available?(:repository, current_user) .project-clone-holder = render "shared/clone_panel" + + - if current_user + = render 'shared/members/access_request_buttons', source: @project diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index c50093cf47c..20ea8600a40 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -69,7 +69,6 @@ %li.project-repo-buttons.right .project-right-buttons - if current_user - = render 'shared/members/access_request_buttons', source: @project = render "projects/buttons/koding" .btn-group.project-repo-btn-group diff --git a/app/views/shared/members/_access_request_buttons.html.haml b/app/views/shared/members/_access_request_buttons.html.haml index e166dfab710..e9b9ed554c1 100644 --- a/app/views/shared/members/_access_request_buttons.html.haml +++ b/app/views/shared/members/_access_request_buttons.html.haml @@ -1,16 +1,17 @@ - model_name = source.model_name.to_s.downcase -- if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) - = link_to "Leave #{model_name}", polymorphic_path([:leave, source, :members]), - method: :delete, - data: { confirm: leave_confirmation_message(source) }, - class: 'btn' -- elsif requester = source.requesters.find_by(user_id: current_user.id) - = link_to 'Withdraw Access Request', polymorphic_path([:leave, source, :members]), - method: :delete, - data: { confirm: remove_member_message(requester) }, - class: 'btn' -- elsif source.request_access_enabled && can?(current_user, :request_access, source) - = link_to 'Request Access', polymorphic_path([:request_access, source, :members]), - method: :post, - class: 'btn' +.access-button.inline.hidden-sm.hidden-xs + - if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) + = link_to "Leave #{model_name}", polymorphic_path([:leave, source, :members]), + method: :delete, + data: { confirm: leave_confirmation_message(source) }, + class: 'btn' + - elsif requester = source.requesters.find_by(user_id: current_user.id) + = link_to 'Withdraw Access Request', polymorphic_path([:leave, source, :members]), + method: :delete, + data: { confirm: remove_member_message(requester) }, + class: 'btn' + - elsif source.request_access_enabled && can?(current_user, :request_access, source) + = link_to 'Request Access', polymorphic_path([:request_access, source, :members]), + method: :post, + class: 'btn' diff --git a/changelogs/unreleased/leave-project-btn.yml b/changelogs/unreleased/leave-project-btn.yml new file mode 100644 index 00000000000..ff07dfeed17 --- /dev/null +++ b/changelogs/unreleased/leave-project-btn.yml @@ -0,0 +1,4 @@ +--- +title: Move access project button to header +merge_request: +author: From 2f87703cfff38a8c88f44310031c35dbe7f017ff Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 16 Dec 2016 09:46:24 -0600 Subject: [PATCH 279/386] Move all project buttons to header --- app/assets/stylesheets/framework/mobile.scss | 2 +- app/assets/stylesheets/pages/projects.scss | 15 ++++++++------- app/views/projects/_home_panel.html.haml | 18 ++++++++++++------ app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/buttons/_dropdown.html.haml | 2 +- app/views/projects/buttons/_koding.html.haml | 6 +++--- app/views/projects/show.html.haml | 13 +------------ .../members/_access_request_buttons.html.haml | 2 +- .../shared/notifications/_button.html.haml | 2 +- 9 files changed, 29 insertions(+), 33 deletions(-) diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index abfdd7a759d..7eb9962ba33 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -54,7 +54,7 @@ } // Display Star and Fork buttons without counters on mobile. - .project-action-buttons { + .project-repo-buttons { display: block; .count-buttons .btn { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 29a8f5b3995..acfcb6c0e24 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -143,10 +143,12 @@ margin-top: 0; } -.project-repo-buttons, .group-buttons { margin-top: 15px; +} +.project-repo-buttons, +.group-buttons { .btn { @include btn-gray; padding: 3px 10px; @@ -175,12 +177,8 @@ } } - .download-button, - .dropdown-toggle, - .notification-dropdown, - .project-dropdown, - .access-button { - margin-left: 10px; + .project-action-button { + margin: 15px 5px 0 5px; vertical-align: top; } @@ -197,10 +195,12 @@ .count-buttons { display: inline-block; vertical-align: top; + margin-top: 15px; } .project-clone-holder { display: inline-block; + margin: 15px 5px 0 0; input { height: 28px; @@ -494,6 +494,7 @@ a.deploy-project-label { .project-stats { font-size: 0; + text-align: center; border-bottom: 1px solid $border-color; .nav { diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index d13177f6776..b7e0c7f7128 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -18,14 +18,20 @@ = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - .project-repo-buttons.project-action-buttons + .project-repo-buttons .count-buttons = render 'projects/buttons/star' = render 'projects/buttons/fork' - - if @project.feature_available?(:repository, current_user) - .project-clone-holder - = render "shared/clone_panel" + %span.hidden-xs + - if @project.feature_available?(:repository, current_user) + .project-clone-holder + = render "shared/clone_panel" - - if current_user - = render 'shared/members/access_request_buttons', source: @project + = render 'projects/buttons/download', project: @project, ref: @ref + = render 'projects/buttons/dropdown' + = render 'shared/notifications/button', notification_setting: @notification_setting + + - if current_user + = render "projects/buttons/koding" + = render 'shared/members/access_request_buttons', source: @project diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 40bfa01a45a..324a7f8cd3f 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,5 +1,5 @@ - if !project.empty_repo? && can?(current_user, :download_code, project) - .dropdown.inline.download-button + .project-action-button.dropdown.inline %button.btn{ 'data-toggle' => 'dropdown' } = icon('download') = icon("caret-down") diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index d3ccebbe290..f5659be25f0 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -1,5 +1,5 @@ - if current_user - .dropdown.inline + .project-action-button.dropdown.inline %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"} = icon('plus') = icon("caret-down") diff --git a/app/views/projects/buttons/_koding.html.haml b/app/views/projects/buttons/_koding.html.haml index fdc80d44253..f8cad1605f6 100644 --- a/app/views/projects/buttons/_koding.html.haml +++ b/app/views/projects/buttons/_koding.html.haml @@ -1,7 +1,7 @@ - if koding_enabled? && current_user && can_push_branch?(@project, @project.default_branch) - if @repository.koding_yml - = link_to koding_project_url(@project), class: 'btn', target: '_blank' do + = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank' do Run in IDE (Koding) - else - = link_to add_koding_stack_path(@project), class: 'btn' do - Set Up Koding + = link_to add_koding_stack_path(@project), class: 'btn project-action-button inline' do + Set up Koding diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 20ea8600a40..5a60ea58a9e 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -64,19 +64,8 @@ - unless @repository.gitlab_ci_yml %li.missing = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do - Set Up CI + Set up CI - %li.project-repo-buttons.right - .project-right-buttons - - if current_user - = render "projects/buttons/koding" - - .btn-group.project-repo-btn-group - = render 'projects/buttons/download', project: @project, ref: @ref - = render 'projects/buttons/dropdown' - - .pull-right - = render 'shared/notifications/button', notification_setting: @notification_setting - if @repository.commit .project-last-commit{ class: container_class } = render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project diff --git a/app/views/shared/members/_access_request_buttons.html.haml b/app/views/shared/members/_access_request_buttons.html.haml index e9b9ed554c1..fb795ad1c72 100644 --- a/app/views/shared/members/_access_request_buttons.html.haml +++ b/app/views/shared/members/_access_request_buttons.html.haml @@ -1,6 +1,6 @@ - model_name = source.model_name.to_s.downcase -.access-button.inline.hidden-sm.hidden-xs +.project-action-button.inline - if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) = link_to "Leave #{model_name}", polymorphic_path([:leave, source, :members]), method: :delete, diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml index fbad0d05de3..8e4fa5d48c3 100644 --- a/app/views/shared/notifications/_button.html.haml +++ b/app/views/shared/notifications/_button.html.haml @@ -1,5 +1,5 @@ - if notification_setting - .dropdown.notification-dropdown + .project-action-button.dropdown.notification-dropdown.inline = form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f| = hidden_setting_source_input(notification_setting) = f.hidden_field :level, class: "notification_setting_level" From 30a7a76f3bbc0572a151fc6612c46591f7de7ec0 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 16 Dec 2016 10:11:51 -0600 Subject: [PATCH 280/386] Add empty koding state; check permissions for project buttons --- app/views/projects/_home_panel.html.haml | 11 +++++------ app/views/projects/buttons/_koding.html.haml | 10 +++------- app/views/projects/show.html.haml | 3 +++ changelogs/unreleased/leave-project-btn.yml | 6 +++--- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index b7e0c7f7128..0d1f2b70018 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -28,10 +28,9 @@ .project-clone-holder = render "shared/clone_panel" - = render 'projects/buttons/download', project: @project, ref: @ref - = render 'projects/buttons/dropdown' - = render 'shared/notifications/button', notification_setting: @notification_setting - - - if current_user - = render "projects/buttons/koding" + - if current_user && can?(current_user, :download_code, @project) + = render 'projects/buttons/download', project: @project, ref: @ref + = render 'projects/buttons/dropdown' + = render 'shared/notifications/button', notification_setting: @notification_setting + = render 'projects/buttons/koding' = render 'shared/members/access_request_buttons', source: @project diff --git a/app/views/projects/buttons/_koding.html.haml b/app/views/projects/buttons/_koding.html.haml index f8cad1605f6..5d9a776da89 100644 --- a/app/views/projects/buttons/_koding.html.haml +++ b/app/views/projects/buttons/_koding.html.haml @@ -1,7 +1,3 @@ -- if koding_enabled? && current_user && can_push_branch?(@project, @project.default_branch) - - if @repository.koding_yml - = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank' do - Run in IDE (Koding) - - else - = link_to add_koding_stack_path(@project), class: 'btn project-action-button inline' do - Set up Koding +- if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch) + = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank' do + Run in IDE (Koding) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 5a60ea58a9e..097dd224140 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -65,6 +65,9 @@ %li.missing = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do Set up CI + - if koding_enabled? && @repository.koding_yml.blank? + %li.missing + = link_to 'Set Up Koding', add_koding_stack_path(@project) - if @repository.commit .project-last-commit{ class: container_class } diff --git a/changelogs/unreleased/leave-project-btn.yml b/changelogs/unreleased/leave-project-btn.yml index ff07dfeed17..2aa553d7b97 100644 --- a/changelogs/unreleased/leave-project-btn.yml +++ b/changelogs/unreleased/leave-project-btn.yml @@ -1,4 +1,4 @@ --- -title: Move access project button to header -merge_request: -author: +title: Move all action buttons to project header +merge_request: +author: From 78e8ac5da1fce5dab13667b9eafe7c91550e7210 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 16 Dec 2016 11:13:17 -0600 Subject: [PATCH 281/386] Add sentence casing, fix groups page buttons, fix dark gray variable --- app/assets/stylesheets/framework/variables.scss | 1 + app/assets/stylesheets/pages/groups.scss | 6 ------ app/assets/stylesheets/pages/projects.scss | 6 +----- app/assets/stylesheets/pages/tree.scss | 2 +- app/views/projects/show.html.haml | 2 +- app/views/shared/notifications/_button.html.haml | 2 +- 6 files changed, 5 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index d0c27d64239..460c5d995be 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -24,6 +24,7 @@ $gray-lightest: #fdfdfd; $gray-light: #fafafa; $gray-lighter: #f9f9f9; $gray-normal: #f5f5f5; +$gray-dark: darken($gray-light, $darken-dark-factor); $gray-darker: #eee; $gray-darkest: #c4c4c4; diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index a9af7af59e2..16bff5f1e03 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -27,12 +27,6 @@ } } -.group-buttons { - .notification-dropdown { - display: inline-block; - } -} - .groups-header { @media (min-width: $screen-sm-min) { .nav-links { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index acfcb6c0e24..a443b6a37b3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -143,10 +143,6 @@ margin-top: 0; } -.group-buttons { - margin-top: 15px; -} - .project-repo-buttons, .group-buttons { .btn { @@ -178,7 +174,7 @@ } .project-action-button { - margin: 15px 5px 0 5px; + margin: 15px 5px 0; vertical-align: top; } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index c0341db7289..05c0a4c29f4 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -172,7 +172,7 @@ position: relative; z-index: 2; - .download-button { + .project-action-button { margin-left: $btn-side-margin; } } diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 097dd224140..8a214e1de58 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -67,7 +67,7 @@ Set up CI - if koding_enabled? && @repository.koding_yml.blank? %li.missing - = link_to 'Set Up Koding', add_koding_stack_path(@project) + = link_to 'Set up Koding', add_koding_stack_path(@project) - if @repository.commit .project-last-commit{ class: container_class } diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml index 8e4fa5d48c3..ce33d1497ba 100644 --- a/app/views/shared/notifications/_button.html.haml +++ b/app/views/shared/notifications/_button.html.haml @@ -1,5 +1,5 @@ - if notification_setting - .project-action-button.dropdown.notification-dropdown.inline + .project-action-button.dropdown.inline = form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f| = hidden_setting_source_input(notification_setting) = f.hidden_field :level, class: "notification_setting_level" From 26c8e0b3f472c2567ae4d7eed16f19cd40132b70 Mon Sep 17 00:00:00 2001 From: Jeff Stubler Date: Fri, 16 Dec 2016 09:10:04 -0600 Subject: [PATCH 282/386] Fix 500 error for invalid path when visiting blame page Closes #25761. --- app/controllers/projects/blame_controller.rb | 3 +++ changelogs/unreleased/fix-blame-500.yml | 4 ++++ spec/controllers/projects/blame_controller_spec.rb | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 changelogs/unreleased/fix-blame-500.yml diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index f576d0be1fc..863a766a255 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -8,6 +8,9 @@ class Projects::BlameController < Projects::ApplicationController def show @blob = @repository.blob_at(@commit.id, @path) + + return render_404 unless @blob + @blame_groups = Gitlab::Blame.new(@blob, @commit).groups end end diff --git a/changelogs/unreleased/fix-blame-500.yml b/changelogs/unreleased/fix-blame-500.yml new file mode 100644 index 00000000000..379d81aaa44 --- /dev/null +++ b/changelogs/unreleased/fix-blame-500.yml @@ -0,0 +1,4 @@ +--- +title: Fix blame 500 error on invalid path. +merge_request: 25761 +author: Jeff Stubler diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb index 25f06299a29..4402ca43c65 100644 --- a/spec/controllers/projects/blame_controller_spec.rb +++ b/spec/controllers/projects/blame_controller_spec.rb @@ -25,5 +25,10 @@ describe Projects::BlameController do let(:id) { 'master/files/ruby/popen.rb' } it { is_expected.to respond_with(:success) } end + + context "invalid file" do + let(:id) { 'master/files/ruby/missing_file.rb'} + it { expect(response).to have_http_status(404) } + end end end From e7d019c6997fbfed56a415de6dfe4754a5c966d4 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Sun, 18 Dec 2016 21:07:06 +0100 Subject: [PATCH 283/386] Bring back "notification-dropdown" class for styling and use "js-notification-dropdown" for JavaScript --- app/assets/javascripts/notifications_dropdown.js | 2 +- app/views/shared/notifications/_button.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index 324b68a7efc..5d0d594073d 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -19,7 +19,7 @@ }); $(document).off('ajax:success', '.notification-form').on('ajax:success', '.notification-form', function(e, data) { if (data.saved) { - return $(e.currentTarget).closest('.notification-dropdown').replaceWith(data.html); + return $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html); } else { return new Flash('Failed to save new settings', 'alert'); } diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml index ce33d1497ba..1d072c16b32 100644 --- a/app/views/shared/notifications/_button.html.haml +++ b/app/views/shared/notifications/_button.html.haml @@ -1,5 +1,5 @@ - if notification_setting - .project-action-button.dropdown.inline + .js-notification-dropdown.notification-dropdown.project-action-button.dropdown.inline = form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f| = hidden_setting_source_input(notification_setting) = f.hidden_field :level, class: "notification_setting_level" From 3f60a276fc36fc7d1c5323c38b33fdbc774cfbbf Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 13 Dec 2016 16:53:01 +0000 Subject: [PATCH 284/386] Added slack slash commands frontend help well Added tests --- app/assets/stylesheets/framework/forms.scss | 4 + .../slack_slash_commands/_help.html.haml | 93 +++++++++++++++++++ .../services/slack_slash_command_spec.rb | 48 ++++++++++ 3 files changed, 145 insertions(+) create mode 100644 app/views/projects/services/slack_slash_commands/_help.html.haml create mode 100644 spec/features/projects/services/slack_slash_command_spec.rb diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 940807fc399..8726a69867b 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -96,6 +96,10 @@ label { code { line-height: 1.8; } + + img { + margin-right: $gl-padding; + } } @media(max-width: $screen-xs-max) { diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml new file mode 100644 index 00000000000..c45052e3954 --- /dev/null +++ b/app/views/projects/services/slack_slash_commands/_help.html.haml @@ -0,0 +1,93 @@ +- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}" + +.well + This service allows GitLab users to perform common operations on this + project by entering slash commands in Slack. + %br + See list of available commands in Slack after setting up this service, + by entering + %code /<command> help + %br + %br + To setup this service: + %ul.list-unstyled + %li + 1. + = link_to 'Add a slash command', 'https://my.slack.com/services/new/slash-commands' + in your Slack team with these options: + + %hr + + .help-form + .form-group + = label_tag nil, 'Command', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.text-block + %p Fill in the word that works best for your team. + %p + Suggestions: + %code= 'gitlab' + %code= @project.path # Path contains no spaces, but dashes + %code= @project.path_with_namespace + + .form-group + = label_tag :url, 'URL', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.input-group + = text_field_tag :url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly' + .input-group-btn + = clipboard_button(clipboard_target: '#url') + + .form-group + = label_tag nil, 'Method', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.text-block POST + + .form-group + = label_tag :customize_name, 'Customize name', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.input-group + = text_field_tag :customize_name, 'GitLab', class: 'form-control input-sm', readonly: 'readonly' + .input-group-btn + = clipboard_button(clipboard_target: '#customize_name') + + .form-group + = label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.text-block + = image_tag(asset_url('gitlab_logo.png'), width: 36, height: 36) + = link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank') + + .form-group + = label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.text-block Show this command in the autocomplete list + + .form-group + = label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.input-group + = text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly' + .input-group-btn + = clipboard_button(clipboard_target: '#autocomplete_description') + + .form-group + = label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.input-group + = text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly' + .input-group-btn + = clipboard_button(clipboard_target: '#autocomplete_usage_hint') + + .form-group + = label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-xs-12 control-label' + .col-sm-10.col-xs-12.input-group + = text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control input-sm', readonly: 'readonly' + .input-group-btn + = clipboard_button(clipboard_target: '#descriptive_label') + + %hr + + %ul.list-unstyled + %li + 2. Paste the + %strong Token + into the field below + %li + 3. Select the + %strong Active + checkbox, press + %strong Save changes + and start using GitLab inside Slack! diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb new file mode 100644 index 00000000000..dee43d69895 --- /dev/null +++ b/spec/features/projects/services/slack_slash_command_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +feature 'Setup Slack slash commands', feature: true do + include WaitForAjax + + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:service) { project.create_slack_slash_commands_service } + + before do + project.team << [user, :master] + login_as(user) + end + + describe 'user visits the slack slash command config page', js: true do + it 'shows a help message' do + visit edit_namespace_project_service_path(project.namespace, project, service) + + wait_for_ajax + + expect(page).to have_content('This service allows GitLab users to perform common') + end + end + + describe 'saving a token' do + let(:token) { ('a'..'z').to_a.join } + + it 'shows the token after saving' do + visit edit_namespace_project_service_path(project.namespace, project, service) + + fill_in 'service_token', with: token + click_on 'Save' + + value = find_field('service_token').value + + expect(value).to eq(token) + end + end + + describe 'the trigger url' do + it 'shows the correct url' do + visit edit_namespace_project_service_path(project.namespace, project, service) + + value = find_field('url').value + expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger") + end + end +end From 676b79b94d33e007b34d70c00900a52983ce1106 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 18 Dec 2016 23:30:37 +0100 Subject: [PATCH 285/386] Fix Rubocop --- lib/gitlab/chat_commands/presenter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index e94d0ce2470..b4c4dc252ca 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -43,7 +43,7 @@ module Gitlab end def access_denied - ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).") + ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).") end private @@ -88,7 +88,7 @@ module Gitlab reference = resource.try(:to_reference) || resource.try(:id) title = resource.try(:title) || resource.try(:name) - "[#{reference} #{title}](#{url(resource)})" + "[#{reference} #{title}](#{url(resource)})" end def header_with_list(header, items) From 9e3153dbf556b9b9397806bedcf0a195e8ee3fa4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 18 Dec 2016 23:30:48 +0100 Subject: [PATCH 286/386] Remove not related spec changes --- .../chat_message/build_message_spec.rb | 8 +++--- .../chat_message/issue_message_spec.rb | 10 +++---- .../chat_message/merge_message_spec.rb | 12 ++++----- .../chat_message/note_message_spec.rb | 22 ++++++++-------- .../chat_message/push_message_spec.rb | 26 +++++++++---------- .../chat_message/wiki_page_message_spec.rb | 8 +++--- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/spec/models/project_services/chat_message/build_message_spec.rb b/spec/models/project_services/chat_message/build_message_spec.rb index 50ad5013df9..b71d153f814 100644 --- a/spec/models/project_services/chat_message/build_message_spec.rb +++ b/spec/models/project_services/chat_message/build_message_spec.rb @@ -10,7 +10,7 @@ describe ChatMessage::BuildMessage do tag: false, project_name: 'project_name', - project_url: 'http://example.gitlab.com', + project_url: 'example.gitlab.com', commit: { status: status, @@ -48,10 +48,10 @@ describe ChatMessage::BuildMessage do end def build_message(status_text = status) - ":" \ - " Commit :" \ + " Commit " \ - " of branch" \ + " of branch" \ " by hacker #{status_text} in #{duration} #{'second'.pluralize(duration)}" end end diff --git a/spec/models/project_services/chat_message/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb index 190ff4c535d..ebe0ead4408 100644 --- a/spec/models/project_services/chat_message/issue_message_spec.rb +++ b/spec/models/project_services/chat_message/issue_message_spec.rb @@ -10,14 +10,14 @@ describe ChatMessage::IssueMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'http://somewhere.com', + project_url: 'somewhere.com', object_attributes: { title: 'Issue title', id: 10, iid: 100, assignee_id: 1, - url: 'http://url.com', + url: 'url', action: 'open', state: 'opened', description: 'issue description' @@ -40,11 +40,11 @@ describe ChatMessage::IssueMessage, models: true do context 'open' do it 'returns a message regarding opening of issues' do expect(subject.pretext).to eq( - '[] Issue opened by test.user') + '] Issue opened by test.user') expect(subject.attachments).to eq([ { title: "#100 Issue title", - title_link: "http://url.com", + title_link: "url", text: "issue description", color: color, } @@ -60,7 +60,7 @@ describe ChatMessage::IssueMessage, models: true do it 'returns a message regarding closing of issues' do expect(subject.pretext). to eq( - '[] Issue closed by test.user') + '] Issue closed by test.user') expect(subject.attachments).to be_empty end end diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb index cc154112e90..07c414c6ca4 100644 --- a/spec/models/project_services/chat_message/merge_message_spec.rb +++ b/spec/models/project_services/chat_message/merge_message_spec.rb @@ -10,14 +10,14 @@ describe ChatMessage::MergeMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'http://somewhere.com', + project_url: 'somewhere.com', object_attributes: { title: "Issue title\nSecond line", id: 10, iid: 100, assignee_id: 1, - url: 'http://url.com', + url: 'url', state: 'opened', description: 'issue description', source_branch: 'source_branch', @@ -31,8 +31,8 @@ describe ChatMessage::MergeMessage, models: true do context 'open' do it 'returns a message regarding opening of merge requests' do expect(subject.pretext).to eq( - 'test.user opened '\ - 'in : *Issue title*') + 'test.user opened '\ + 'in : *Issue title*') expect(subject.attachments).to be_empty end end @@ -43,8 +43,8 @@ describe ChatMessage::MergeMessage, models: true do end it 'returns a message regarding closing of merge requests' do expect(subject.pretext).to eq( - 'test.user closed '\ - 'in : *Issue title*') + 'test.user closed '\ + 'in : *Issue title*') expect(subject.attachments).to be_empty end end diff --git a/spec/models/project_services/chat_message/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb index da700a08e57..31936da40a2 100644 --- a/spec/models/project_services/chat_message/note_message_spec.rb +++ b/spec/models/project_services/chat_message/note_message_spec.rb @@ -11,15 +11,15 @@ describe ChatMessage::NoteMessage, models: true do avatar_url: 'http://fakeavatar' }, project_name: 'project_name', - project_url: 'http://somewhere.com', + project_url: 'somewhere.com', repository: { name: 'project_name', - url: 'http://somewhere.com', + url: 'somewhere.com', }, object_attributes: { id: 10, note: 'comment on a commit', - url: 'http://url.com', + url: 'url', noteable_type: 'Commit' } } @@ -37,8 +37,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on commits' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*Added a commit message*") expected_attachments = [ { @@ -63,8 +63,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on a merge request' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*merge request title*") expected_attachments = [ { @@ -90,8 +90,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on an issue' do message = described_class.new(@args) expect(message.pretext).to eq( - "test.user in : " \ + "test.user in : " \ "*issue title*") expected_attachments = [ { @@ -115,8 +115,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on a project snippet' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*snippet title*") expected_attachments = [ { diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb index 24928873bad..b781c4505db 100644 --- a/spec/models/project_services/chat_message/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -10,7 +10,7 @@ describe ChatMessage::PushMessage, models: true do project_name: 'project_name', ref: 'refs/heads/master', user_name: 'test.user', - project_url: 'http://url.com' + project_url: 'url' } end @@ -19,20 +19,20 @@ describe ChatMessage::PushMessage, models: true do context 'push' do before do args[:commits] = [ - { message: 'message1', url: 'http://url1.com', id: 'abcdefghijkl', author: { name: 'author1' } }, - { message: 'message2', url: 'http://url2.com', id: '123456789012', author: { name: 'author2' } }, + { message: 'message1', url: 'url1', id: 'abcdefghijkl', author: { name: 'author1' } }, + { message: 'message2', url: 'url2', id: '123456789012', author: { name: 'author2' } }, ] end it 'returns a message regarding pushes' do expect(subject.pretext).to eq( - 'test.user pushed to branch of '\ - ' ()' + 'test.user pushed to branch of '\ + ' ()' ) expect(subject.attachments).to eq([ { - text: ": message1 - author1\n"\ - ": message2 - author2", + text: ": message1 - author1\n"\ + ": message2 - author2", color: color, } ]) @@ -47,14 +47,14 @@ describe ChatMessage::PushMessage, models: true do project_name: 'project_name', ref: 'refs/tags/new_tag', user_name: 'test.user', - project_url: 'http://url.com' + project_url: 'url' } end it 'returns a message regarding pushes' do expect(subject.pretext).to eq('test.user pushed new tag ' \ - ' to ' \ - '') + ' to ' \ + '') expect(subject.attachments).to be_empty end end @@ -66,8 +66,8 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a new branch' do expect(subject.pretext).to eq( - 'test.user pushed new branch to '\ - '' + 'test.user pushed new branch to '\ + '' ) expect(subject.attachments).to be_empty end @@ -80,7 +80,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a removed branch' do expect(subject.pretext).to eq( - 'test.user removed branch master from ' + 'test.user removed branch master from ' ) expect(subject.attachments).to be_empty end diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb index a2ad61e38e7..94c04dc0865 100644 --- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -10,10 +10,10 @@ describe ChatMessage::WikiPageMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'http://somewhere.com', + project_url: 'somewhere.com', object_attributes: { title: 'Wiki page title', - url: 'http://url.com', + url: 'url', content: 'Wiki page description' } } @@ -25,7 +25,7 @@ describe ChatMessage::WikiPageMessage, models: true do it 'returns a message that a new wiki page was created' do expect(subject.pretext).to eq( - 'test.user created in : '\ + 'test.user created in : '\ '*Wiki page title*') end end @@ -35,7 +35,7 @@ describe ChatMessage::WikiPageMessage, models: true do it 'returns a message that a wiki page was updated' do expect(subject.pretext).to eq( - 'test.user edited in : '\ + 'test.user edited in : '\ '*Wiki page title*') end end From f9023adbad4939ba597d509e319105659e61734b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 18 Dec 2016 23:32:53 +0100 Subject: [PATCH 287/386] Fix spec failures --- app/models/project_services/slack_slash_commands_service.rb | 2 +- spec/features/admin/admin_settings_spec.rb | 4 ++-- spec/lib/gitlab/import_export/all_models.yml | 1 + spec/models/project_spec.rb | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb index 8413c657099..6782ba5ad0a 100644 --- a/app/models/project_services/slack_slash_commands_service.rb +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -2,7 +2,7 @@ class SlackSlashCommandsService < ChatSlashCommandsService include TriggersHelper def title - 'Slack Slash Command' + 'Slack Command' end def description diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 8cd66f189be..e7a23746244 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -17,9 +17,9 @@ feature 'Admin updates settings', feature: true do expect(page).to have_content "Application settings saved successfully" end - scenario 'Change Slack Service template settings' do + scenario 'Change Slack Notifications Service template settings' do click_link 'Service Templates' - click_link 'Slack' + click_link 'Slack notifications' fill_in 'Webhook', with: 'http://localhost' fill_in 'Username', with: 'test_user' fill_in 'service_push_channel', with: '#test_channel' diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 9b49d6837c3..7e618e2fcf5 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -129,6 +129,7 @@ project: - builds_email_service - pipelines_email_service - mattermost_slash_commands_service +- slack_slash_commands_service - irker_service - pivotaltracker_service - hipchat_service diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index bab3c3dbb02..4b39dc77f29 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -20,7 +20,6 @@ describe Project, models: true do it { is_expected.to have_many(:deploy_keys) } it { is_expected.to have_many(:hooks).dependent(:destroy) } it { is_expected.to have_many(:protected_branches).dependent(:destroy) } - it { is_expected.to have_many(:chat_services) } it { is_expected.to have_one(:forked_project_link).dependent(:destroy) } it { is_expected.to have_one(:slack_notification_service).dependent(:destroy) } it { is_expected.to have_one(:mattermost_notification_service).dependent(:destroy) } @@ -37,6 +36,7 @@ describe Project, models: true do it { is_expected.to have_one(:hipchat_service).dependent(:destroy) } it { is_expected.to have_one(:flowdock_service).dependent(:destroy) } it { is_expected.to have_one(:assembla_service).dependent(:destroy) } + it { is_expected.to have_one(:slack_slash_commands_service).dependent(:destroy) } it { is_expected.to have_one(:mattermost_slash_commands_service).dependent(:destroy) } it { is_expected.to have_one(:gemnasium_service).dependent(:destroy) } it { is_expected.to have_one(:buildkite_service).dependent(:destroy) } From f5ff372140d066d7bcedc0ad0799c723a9012bb0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 18 Dec 2016 23:34:40 +0100 Subject: [PATCH 288/386] Fix failures --- spec/features/admin/admin_settings_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index e7a23746244..47fa2f14307 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -30,7 +30,7 @@ feature 'Admin updates settings', feature: true do expect(page).to have_content 'Application settings saved successfully' - click_link 'Slack' + click_link 'Slack notifications' page.all('input[type=checkbox]').each do |checkbox| expect(checkbox).to be_checked From 9fd775def2d89500cf291fe675458b68ead7cd2c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 18 Dec 2016 23:39:45 +0100 Subject: [PATCH 289/386] Add CHANGELOG --- changelogs/unreleased/dockerfile-templates.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/dockerfile-templates.yml diff --git a/changelogs/unreleased/dockerfile-templates.yml b/changelogs/unreleased/dockerfile-templates.yml new file mode 100644 index 00000000000..e4db46cdf9a --- /dev/null +++ b/changelogs/unreleased/dockerfile-templates.yml @@ -0,0 +1,4 @@ +--- +title: Add support for Dockerfile templates +merge_request: 7247 +author: From 3cc334eae5abf65b0ecb4159e4e9712dab81c33c Mon Sep 17 00:00:00 2001 From: Arsenev Vladislav Date: Sun, 18 Dec 2016 21:56:13 +0000 Subject: [PATCH 290/386] remove build_user from model User --- app/models/user.rb | 4 ---- changelogs/unreleased/25678-remove-user-build.yml | 4 ++++ lib/api/users.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/25678-remove-user-build.yml diff --git a/app/models/user.rb b/app/models/user.rb index 3a17c98eff6..d771eaff472 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -311,10 +311,6 @@ class User < ActiveRecord::Base find_by(id: Key.unscoped.select(:user_id).where(id: key_id)) end - def build_user(attrs = {}) - User.new(attrs) - end - def reference_prefix '@' end diff --git a/changelogs/unreleased/25678-remove-user-build.yml b/changelogs/unreleased/25678-remove-user-build.yml new file mode 100644 index 00000000000..873e637d670 --- /dev/null +++ b/changelogs/unreleased/25678-remove-user-build.yml @@ -0,0 +1,4 @@ +--- +title: remove build_user +merge_request: 8162 +author: Arsenev Vladislav diff --git a/lib/api/users.rb b/lib/api/users.rb index 0842c3874c5..4c22287b5c6 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -94,7 +94,7 @@ module API identity_attrs = params.slice(:provider, :extern_uid) confirm = params.delete(:confirm) - user = User.build_user(declared_params(include_missing: false)) + user = User.new(declared_params(include_missing: false)) user.skip_confirmation! unless confirm if identity_attrs.any? From f8dde43d418d0a9f5a3ead75481cf9bfe8c2e7c6 Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Sun, 18 Dec 2016 20:35:07 -0500 Subject: [PATCH 291/386] Always use `fixture_file_upload` helper to upload files in tests. * Also is not a good idea to use File.open without closing the file handler. We should use it with a block or close it explicitly. --- spec/helpers/groups_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 233d00534e5..c8b0d86425f 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -6,7 +6,7 @@ describe GroupsHelper do it 'returns an url for the avatar' do group = create(:group) - group.avatar = File.open(avatar_file_path) + group.avatar = fixture_file_upload(avatar_file_path) group.save! expect(group_icon(group.path).to_s). to match("/uploads/group/avatar/#{group.id}/banana_sample.gif") From cfd1c6a54e36a25282afb61dea5f1be37e9b3500 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 19 Dec 2016 07:58:10 +0000 Subject: [PATCH 292/386] Fix typo --- doc/ci/yaml/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index dd8e1078c60..7158b2e7895 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1056,7 +1056,7 @@ variables: GET_SOURCES_ATTEMPTS: "3" ``` -You can set the them in the global [`variables`](#variables) section or the [`variables`](#job-variables) +You can set them in the global [`variables`](#variables) section or the [`variables`](#job-variables) section for individual jobs. ## Shallow cloning From 561ed7b9f91a1d1d08c4a1c1f15bdbcf44f973f1 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Sat, 17 Dec 2016 19:22:16 +0500 Subject: [PATCH 293/386] Move admin labels spinach test to rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- features/admin/labels.feature | 38 -------- features/steps/admin/labels.rb | 117 ----------------------- spec/features/admin/admin_labels_spec.rb | 99 +++++++++++++++++++ 3 files changed, 99 insertions(+), 155 deletions(-) delete mode 100644 features/admin/labels.feature delete mode 100644 features/steps/admin/labels.rb create mode 100644 spec/features/admin/admin_labels_spec.rb diff --git a/features/admin/labels.feature b/features/admin/labels.feature deleted file mode 100644 index 1af0e700bd4..00000000000 --- a/features/admin/labels.feature +++ /dev/null @@ -1,38 +0,0 @@ -Feature: Admin Issues Labels - Background: - Given I sign in as an admin - And I have labels: "bug", "feature", "enhancement" - Given I visit admin labels page - - Scenario: I should see labels list - Then I should see label 'bug' - And I should see label 'feature' - - Scenario: I create new label - Given I submit new label 'support' - Then I should see label 'support' - - Scenario: I edit label - Given I visit 'bug' label edit page - When I change label 'bug' to 'fix' - Then I should not see label 'bug' - Then I should see label 'fix' - - Scenario: I remove label - When I remove label 'bug' - Then I should not see label 'bug' - - @javascript - Scenario: I delete all labels - When I delete all labels - Then I should see labels help message - - Scenario: I create a label with invalid color - Given I visit admin new label page - When I submit new label with invalid color - Then I should see label color error message - - Scenario: I create a label that already exists - Given I visit admin new label page - When I submit new label 'bug' - Then I should see label exist error message diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb deleted file mode 100644 index 55ddcc25085..00000000000 --- a/features/steps/admin/labels.rb +++ /dev/null @@ -1,117 +0,0 @@ -class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - - step 'I visit \'bug\' label edit page' do - visit edit_admin_label_path(bug_label) - end - - step 'I visit admin new label page' do - visit new_admin_label_path - end - - step 'I visit admin labels page' do - visit admin_labels_path - end - - step 'I remove label \'bug\'' do - page.within "#label_#{bug_label.id}" do - click_link 'Delete' - end - end - - step 'I have labels: "bug", "feature", "enhancement"' do - ["bug", "feature", "enhancement"].each do |title| - Label.create(title: title, template: true) - end - end - - step 'I delete all labels' do - page.within '.labels' do - page.all('.btn-remove').each do |remove| - remove.click - sleep 0.05 - end - end - end - - step 'I should see labels help message' do - page.within '.labels' do - expect(page).to have_content 'There are no labels yet' - end - end - - step 'I submit new label \'support\'' do - visit new_admin_label_path - fill_in 'Title', with: 'support' - fill_in 'Background color', with: '#F95610' - click_button 'Save' - end - - step 'I submit new label \'bug\'' do - visit new_admin_label_path - fill_in 'Title', with: 'bug' - fill_in 'Background color', with: '#F95610' - click_button 'Save' - end - - step 'I submit new label with invalid color' do - visit new_admin_label_path - fill_in 'Title', with: 'support' - fill_in 'Background color', with: '#12' - click_button 'Save' - end - - step 'I should see label exist error message' do - page.within '.label-form' do - expect(page).to have_content 'Title has already been taken' - end - end - - step 'I should see label color error message' do - page.within '.label-form' do - expect(page).to have_content 'Color must be a valid color code' - end - end - - step 'I should see label \'feature\'' do - page.within '.manage-labels-list' do - expect(page).to have_content 'feature' - end - end - - step 'I should see label \'bug\'' do - page.within '.manage-labels-list' do - expect(page).to have_content 'bug' - end - end - - step 'I should not see label \'bug\'' do - page.within '.manage-labels-list' do - expect(page).not_to have_content 'bug' - end - end - - step 'I should see label \'support\'' do - page.within '.manage-labels-list' do - expect(page).to have_content 'support' - end - end - - step 'I change label \'bug\' to \'fix\'' do - fill_in 'Title', with: 'fix' - fill_in 'Background color', with: '#F15610' - click_button 'Save' - end - - step 'I should see label \'fix\'' do - page.within '.manage-labels-list' do - expect(page).to have_content 'fix' - end - end - - def bug_label - Label.templates.find_or_create_by(title: 'bug') - end -end diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb new file mode 100644 index 00000000000..eaa42aad0a7 --- /dev/null +++ b/spec/features/admin/admin_labels_spec.rb @@ -0,0 +1,99 @@ +require 'spec_helper' + +RSpec.describe 'admin issues labels' do + include WaitForAjax + + let!(:bug_label) { Label.create(title: 'bug', template: true) } + let!(:feature_label) { Label.create(title: 'feature', template: true) } + + before do + login_as :admin + end + + describe 'list' do + before do + visit admin_labels_path + end + + it 'renders labels list' do + page.within '.manage-labels-list' do + expect(page).to have_content('bug') + expect(page).to have_content('feature') + end + end + + it 'deletes label' do + page.within "#label_#{bug_label.id}" do + click_link 'Delete' + end + + page.within '.manage-labels-list' do + expect(page).not_to have_content('bug') + end + end + + it 'deletes all labels', js: true do + page.within '.labels' do + page.all('.btn-remove').each do |remove| + wait_for_ajax + remove.click + end + end + + page.within '.manage-labels-list' do + expect(page).not_to have_content('bug') + expect(page).not_to have_content('feature_label') + end + end + end + + describe 'create' do + before do + visit new_admin_label_path + end + + it 'creates new label' do + fill_in 'Title', with: 'support' + fill_in 'Background color', with: '#F95610' + click_button 'Save' + + page.within '.manage-labels-list' do + expect(page).to have_content('support') + end + end + + it 'does not creates label with invalid color' do + fill_in 'Title', with: 'support' + fill_in 'Background color', with: '#12' + click_button 'Save' + + page.within '.label-form' do + expect(page).to have_content('Color must be a valid color code') + end + end + + it 'does not creates label if label already exists' do + fill_in 'Title', with: 'bug' + fill_in 'Background color', with: '#F95610' + click_button 'Save' + + page.within '.label-form' do + expect(page).to have_content 'Title has already been taken' + end + end + end + + describe 'edit' do + it 'changes bug label' do + visit edit_admin_label_path(bug_label) + + fill_in 'Title', with: 'fix' + fill_in 'Background color', with: '#F15610' + click_button 'Save' + + page.within '.manage-labels-list' do + expect(page).to have_content('fix') + end + end + end +end From 8607383e1dcdde62a2143890f02a72141dd44af1 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 18:11:18 +0800 Subject: [PATCH 294/386] Just implement it in the block Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20223109 --- lib/gitlab/serialize/ci/variables.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/gitlab/serialize/ci/variables.rb b/lib/gitlab/serialize/ci/variables.rb index 0ca060cd95e..8919f0ccd00 100644 --- a/lib/gitlab/serialize/ci/variables.rb +++ b/lib/gitlab/serialize/ci/variables.rb @@ -12,20 +12,16 @@ module Gitlab object = YAML.safe_load(string, [Symbol]) - object.map(&Variables.method(:convert_key_value_to_string)) + object.map do |variable| + variable[:key] = variable[:key].to_s + variable[:value] = variable[:value].to_s + variable + end end def dump(object) YAML.dump(object) end - - private - - def convert_key_value_to_string(variable) - variable[:key] = variable[:key].to_s - variable[:value] = variable[:value].to_s - variable - end end end end From d6edecdeec9b41b4509775ae5b2e01ee15b6d9dc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 19 Dec 2016 11:26:57 +0100 Subject: [PATCH 295/386] Fix duplicated build token problem and added relevant spec --- changelogs/unreleased/fix-import-export-build-token.yml | 4 ++++ lib/gitlab/import_export/relation_factory.rb | 2 ++ spec/lib/gitlab/import_export/project.json | 4 +++- .../gitlab/import_export/project_tree_restorer_spec.rb | 8 ++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/fix-import-export-build-token.yml diff --git a/changelogs/unreleased/fix-import-export-build-token.yml b/changelogs/unreleased/fix-import-export-build-token.yml new file mode 100644 index 00000000000..622487e6829 --- /dev/null +++ b/changelogs/unreleased/fix-import-export-build-token.yml @@ -0,0 +1,4 @@ +--- +title: Fix Import/Export duplicated builds error +merge_request: +author: diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index a0e80fccad9..9b590bcee8d 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -99,6 +99,8 @@ module Gitlab def generate_imported_object if BUILD_MODELS.include?(@relation_name) # call #trace= method after assigning the other attributes trace = @relation_hash.delete('trace') + @relation_hash.delete('token') + imported_object do |object| object.trace = trace object.commit_id = nil diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index ed9df468ced..1e5901651ae 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -6548,7 +6548,9 @@ "url": null }, "erased_by_id": null, - "erased_at": null + "erased_at": null, + "type": "Ci::Build", + "token": "abcd" }, { "id": 72, diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 3038ab53ad8..13f593fb2db 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -189,6 +189,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end end end + + context 'existing builds' do + it 'do not throw errors' do + create(:ci_build, token: 'abcd') + + expect(restored_project_json).to be true + end + end end end end From 64d7772b6f0594896eb1ac67d5d3f4c33c813fe3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 18:43:06 +0800 Subject: [PATCH 296/386] Use a separate method to skip validation Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8091#note_20222666 --- lib/ci/api/builds.rb | 2 +- lib/ci/api/helpers.rb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 3c4cfccb19a..142bce82286 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -41,7 +41,7 @@ module Ci put ":id" do authenticate_runner! build = Ci::Build.where(runner_id: current_runner.id).running.find(params[:id]) - authenticate_build!(build, verify_token: false) + validate_build!(build) update_runner_info diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 0202b3cf8a3..51b05aa0cb6 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -13,9 +13,14 @@ module Ci forbidden! unless current_runner end - def authenticate_build!(build, verify_token: true) + def authenticate_build!(build) + not_found! unless build + forbidden! if !build_token_valid?(build) + validate_build!(build) + end + + def validate_build!(build) not_found! unless build - forbidden! if verify_token && !build_token_valid?(build) forbidden!('Project has been deleted!') unless build.project forbidden!('Build has been erased!') if build.erased? end From fb23153343f274a29cba1023759f675aaf64251a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 18:50:01 +0800 Subject: [PATCH 297/386] Delete the project when building the build Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8091#note_20222756 --- spec/requests/ci/api/builds_spec.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index d61a9afd12e..2963fe85478 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -332,21 +332,18 @@ describe Ci::API::Builds do context 'when project for the build has been deleted' do let(:build) do - create(:ci_build, - :pending, - :trace, - runner_id: runner.id, - pipeline: pipeline) + result = create(:ci_build, + :pending, + :trace, + runner_id: runner.id, + pipeline: pipeline) + result.project.update(pending_delete: true) + result end it 'responds with forbidden' do expect(response.status).to eq 403 end - - def initial_patch_the_trace - build.project.update(pending_delete: true) - super - end end end From ec003d9eb338f9172696f5540637e759a93f9fcf Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 19:14:21 +0800 Subject: [PATCH 298/386] Prefer unless over if not Feedback: https://gitlab.com/gitlab-org/gitlab-ce/builds/7606797 --- lib/ci/api/helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 51b05aa0cb6..62c10c3b753 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -15,7 +15,7 @@ module Ci def authenticate_build!(build) not_found! unless build - forbidden! if !build_token_valid?(build) + forbidden! unless build_token_valid?(build) validate_build!(build) end From 083e185cdaee03e66dec0ab267a2f3b5d3dab9a7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 19 Dec 2016 13:20:17 +0100 Subject: [PATCH 299/386] Render stage dropdown in separate API call as HTML ``` Endpoint: /group/project/pipelines/id/stage.json?stage=name Call: stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name) ``` --- app/controllers/projects/pipelines_controller.rb | 12 ++++++++++++ app/views/projects/pipelines/_stage.html.haml | 13 +++++++++++++ config/routes/project.rb | 1 + 3 files changed, 26 insertions(+) create mode 100644 app/views/projects/pipelines/_stage.html.haml diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 85188cfdd4c..0147072b0f1 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -8,6 +8,7 @@ class Projects::PipelinesController < Projects::ApplicationController def index @scope = params[:scope] @pipelines = PipelinesFinder.new(project).execute(scope: @scope).page(params[:page]).per(30) + @pipelines = @pipelines.includes(project: :namespace) @running_or_pending_count = PipelinesFinder.new(project).execute(scope: 'running').count @pipelines_count = PipelinesFinder.new(project).execute.count @@ -40,6 +41,17 @@ class Projects::PipelinesController < Projects::ApplicationController end end + def stage + @stage = pipeline.stages.find do |stage| + stage.name == params[:stage] + end + return not_found unless @stage + + respond_to do |format| + format.json { render json: { html: view_to_html_string('projects/pipelines/_stage') } } + end + end + def retry pipeline.retry_failed(current_user) diff --git a/app/views/projects/pipelines/_stage.html.haml b/app/views/projects/pipelines/_stage.html.haml new file mode 100644 index 00000000000..44533b77eba --- /dev/null +++ b/app/views/projects/pipelines/_stage.html.haml @@ -0,0 +1,13 @@ +- detailed_status = @stage.detailed_status(current_user) +- klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status}" +- hasMultipleBuilds = @stage.statuses.count > 1 +- icon_status = "#{detailed_status.icon}_borderless" +- icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status}" +- tooltip = "#{@stage.name}: #{detailed_status.label || 'not found'}" + +.dropdown.inline.build-content + %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } + %span{ class: klass } + %span.mini-pipeline-graph-icon-container + %span{ class: icon_status_klass }= custom_icon(icon_status) + = icon('caret-down', class: 'dropdown-caret') diff --git a/config/routes/project.rb b/config/routes/project.rb index 0754f0ec3b0..909794922ce 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -127,6 +127,7 @@ constraints(ProjectUrlConstrainer.new) do end member do + get :stage post :cancel post :retry get :builds From e6842ff5643c7fc88a65af24b56682f47f4be2de Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 19 Dec 2016 13:32:37 +0100 Subject: [PATCH 300/386] Improve code design --- .../project_services/chat_slash_commands_service.rb | 2 +- .../project_services/slack_slash_commands_service.rb | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/chat_slash_commands_service.rb index 12261e9821e..0bc160af604 100644 --- a/app/models/project_services/chat_slash_commands_service.rb +++ b/app/models/project_services/chat_slash_commands_service.rb @@ -28,7 +28,7 @@ class ChatSlashCommandsService < Service end def trigger(params) - return nil unless valid_token?(params[:token]) + return unless valid_token?(params[:token]) user = find_chat_user(params) unless user diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb index 6782ba5ad0a..cb19ebf4cad 100644 --- a/app/models/project_services/slack_slash_commands_service.rb +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -14,13 +14,15 @@ class SlackSlashCommandsService < ChatSlashCommandsService end def trigger(params) - result = super - # Format messages to be Slack-compatible - if result && result[:text] - result[:text] = Slack::Notifier::LinkFormatter.format(result[:text]) + super.tap do |result| + result[:text] = format(result[:text]) end + end - result + private + + def format(text) + Slack::Notifier::LinkFormatter.format(text) if text end end From b1ccf99e87605216f7d5733d6a4ffb4530d4cfc9 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 19 Dec 2016 13:34:03 +0100 Subject: [PATCH 301/386] Fix previously reverted spec failures --- .../chat_message/build_message_spec.rb | 8 +++--- .../chat_message/issue_message_spec.rb | 10 +++---- .../chat_message/merge_message_spec.rb | 12 ++++----- .../chat_message/note_message_spec.rb | 22 ++++++++-------- .../chat_message/pipeline_message_spec.rb | 8 +++--- .../chat_message/push_message_spec.rb | 26 +++++++++---------- .../chat_message/wiki_page_message_spec.rb | 8 +++--- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/spec/models/project_services/chat_message/build_message_spec.rb b/spec/models/project_services/chat_message/build_message_spec.rb index b71d153f814..50ad5013df9 100644 --- a/spec/models/project_services/chat_message/build_message_spec.rb +++ b/spec/models/project_services/chat_message/build_message_spec.rb @@ -10,7 +10,7 @@ describe ChatMessage::BuildMessage do tag: false, project_name: 'project_name', - project_url: 'example.gitlab.com', + project_url: 'http://example.gitlab.com', commit: { status: status, @@ -48,10 +48,10 @@ describe ChatMessage::BuildMessage do end def build_message(status_text = status) - ":" \ - " Commit :" \ + " Commit " \ - " of branch" \ + " of branch" \ " by hacker #{status_text} in #{duration} #{'second'.pluralize(duration)}" end end diff --git a/spec/models/project_services/chat_message/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb index ebe0ead4408..190ff4c535d 100644 --- a/spec/models/project_services/chat_message/issue_message_spec.rb +++ b/spec/models/project_services/chat_message/issue_message_spec.rb @@ -10,14 +10,14 @@ describe ChatMessage::IssueMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', object_attributes: { title: 'Issue title', id: 10, iid: 100, assignee_id: 1, - url: 'url', + url: 'http://url.com', action: 'open', state: 'opened', description: 'issue description' @@ -40,11 +40,11 @@ describe ChatMessage::IssueMessage, models: true do context 'open' do it 'returns a message regarding opening of issues' do expect(subject.pretext).to eq( - '] Issue opened by test.user') + '[] Issue opened by test.user') expect(subject.attachments).to eq([ { title: "#100 Issue title", - title_link: "url", + title_link: "http://url.com", text: "issue description", color: color, } @@ -60,7 +60,7 @@ describe ChatMessage::IssueMessage, models: true do it 'returns a message regarding closing of issues' do expect(subject.pretext). to eq( - '] Issue closed by test.user') + '[] Issue closed by test.user') expect(subject.attachments).to be_empty end end diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb index 07c414c6ca4..cc154112e90 100644 --- a/spec/models/project_services/chat_message/merge_message_spec.rb +++ b/spec/models/project_services/chat_message/merge_message_spec.rb @@ -10,14 +10,14 @@ describe ChatMessage::MergeMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', object_attributes: { title: "Issue title\nSecond line", id: 10, iid: 100, assignee_id: 1, - url: 'url', + url: 'http://url.com', state: 'opened', description: 'issue description', source_branch: 'source_branch', @@ -31,8 +31,8 @@ describe ChatMessage::MergeMessage, models: true do context 'open' do it 'returns a message regarding opening of merge requests' do expect(subject.pretext).to eq( - 'test.user opened '\ - 'in : *Issue title*') + 'test.user opened '\ + 'in : *Issue title*') expect(subject.attachments).to be_empty end end @@ -43,8 +43,8 @@ describe ChatMessage::MergeMessage, models: true do end it 'returns a message regarding closing of merge requests' do expect(subject.pretext).to eq( - 'test.user closed '\ - 'in : *Issue title*') + 'test.user closed '\ + 'in : *Issue title*') expect(subject.attachments).to be_empty end end diff --git a/spec/models/project_services/chat_message/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb index 31936da40a2..da700a08e57 100644 --- a/spec/models/project_services/chat_message/note_message_spec.rb +++ b/spec/models/project_services/chat_message/note_message_spec.rb @@ -11,15 +11,15 @@ describe ChatMessage::NoteMessage, models: true do avatar_url: 'http://fakeavatar' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', repository: { name: 'project_name', - url: 'somewhere.com', + url: 'http://somewhere.com', }, object_attributes: { id: 10, note: 'comment on a commit', - url: 'url', + url: 'http://url.com', noteable_type: 'Commit' } } @@ -37,8 +37,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on commits' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*Added a commit message*") expected_attachments = [ { @@ -63,8 +63,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on a merge request' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*merge request title*") expected_attachments = [ { @@ -90,8 +90,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on an issue' do message = described_class.new(@args) expect(message.pretext).to eq( - "test.user in : " \ + "test.user in : " \ "*issue title*") expected_attachments = [ { @@ -115,8 +115,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on a project snippet' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*snippet title*") expected_attachments = [ { diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb index eca71db07b6..bf2a9616455 100644 --- a/spec/models/project_services/chat_message/pipeline_message_spec.rb +++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb @@ -15,7 +15,7 @@ describe ChatMessage::PipelineMessage do duration: duration }, project: { path_with_namespace: 'project_name', - web_url: 'example.gitlab.com' }, + web_url: 'http://example.gitlab.com' }, user: user } end @@ -59,9 +59,9 @@ describe ChatMessage::PipelineMessage do end def build_message(status_text = status, name = user[:name]) - ":" \ - " Pipeline " \ - " of branch" \ + ":" \ + " Pipeline " \ + " of branch" \ " by #{name} #{status_text} in #{duration} #{'second'.pluralize(duration)}" end end diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb index b781c4505db..24928873bad 100644 --- a/spec/models/project_services/chat_message/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -10,7 +10,7 @@ describe ChatMessage::PushMessage, models: true do project_name: 'project_name', ref: 'refs/heads/master', user_name: 'test.user', - project_url: 'url' + project_url: 'http://url.com' } end @@ -19,20 +19,20 @@ describe ChatMessage::PushMessage, models: true do context 'push' do before do args[:commits] = [ - { message: 'message1', url: 'url1', id: 'abcdefghijkl', author: { name: 'author1' } }, - { message: 'message2', url: 'url2', id: '123456789012', author: { name: 'author2' } }, + { message: 'message1', url: 'http://url1.com', id: 'abcdefghijkl', author: { name: 'author1' } }, + { message: 'message2', url: 'http://url2.com', id: '123456789012', author: { name: 'author2' } }, ] end it 'returns a message regarding pushes' do expect(subject.pretext).to eq( - 'test.user pushed to branch of '\ - ' ()' + 'test.user pushed to branch of '\ + ' ()' ) expect(subject.attachments).to eq([ { - text: ": message1 - author1\n"\ - ": message2 - author2", + text: ": message1 - author1\n"\ + ": message2 - author2", color: color, } ]) @@ -47,14 +47,14 @@ describe ChatMessage::PushMessage, models: true do project_name: 'project_name', ref: 'refs/tags/new_tag', user_name: 'test.user', - project_url: 'url' + project_url: 'http://url.com' } end it 'returns a message regarding pushes' do expect(subject.pretext).to eq('test.user pushed new tag ' \ - ' to ' \ - '') + ' to ' \ + '') expect(subject.attachments).to be_empty end end @@ -66,8 +66,8 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a new branch' do expect(subject.pretext).to eq( - 'test.user pushed new branch to '\ - '' + 'test.user pushed new branch to '\ + '' ) expect(subject.attachments).to be_empty end @@ -80,7 +80,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a removed branch' do expect(subject.pretext).to eq( - 'test.user removed branch master from ' + 'test.user removed branch master from ' ) expect(subject.attachments).to be_empty end diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb index 94c04dc0865..a2ad61e38e7 100644 --- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -10,10 +10,10 @@ describe ChatMessage::WikiPageMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', object_attributes: { title: 'Wiki page title', - url: 'url', + url: 'http://url.com', content: 'Wiki page description' } } @@ -25,7 +25,7 @@ describe ChatMessage::WikiPageMessage, models: true do it 'returns a message that a new wiki page was created' do expect(subject.pretext).to eq( - 'test.user created in : '\ + 'test.user created in : '\ '*Wiki page title*') end end @@ -35,7 +35,7 @@ describe ChatMessage::WikiPageMessage, models: true do it 'returns a message that a wiki page was updated' do expect(subject.pretext).to eq( - 'test.user edited in : '\ + 'test.user edited in : '\ '*Wiki page title*') end end From f2afdc92b8029f628f6f8ed2d5b9dc1323fd3295 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 21:09:40 +0800 Subject: [PATCH 302/386] Test if expanded_environment_name could expand var with symbols. Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20234245 --- spec/models/build_spec.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 5a47e7ddf0d..e97f6ae3cea 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -1306,11 +1306,25 @@ describe Ci::Build, models: true do describe '#expanded_environment_name' do subject { build.expanded_environment_name } - context 'when environment uses variables' do - let(:build) { create(:ci_build, ref: 'master', environment: 'review/$CI_BUILD_REF_NAME') } + context 'when environment uses $CI_BUILD_REF_NAME' do + let(:build) do + create(:ci_build, + ref: 'master', + environment: 'review/$CI_BUILD_REF_NAME') + end it { is_expected.to eq('review/master') } end + + context 'when environment uses yaml_variables containing symbol keys' do + let(:build) do + create(:ci_build, + yaml_variables: [{key: :APP_HOST, value: 'host'}], + environment: 'review/$APP_HOST') + end + + it { is_expected.to eq('review/host') } + end end describe '#detailed_status' do From 3ea8d983adc467c64c91b2cad91486555678c958 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 21:15:47 +0800 Subject: [PATCH 303/386] Keep the value type for YAML variables Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20235080 --- lib/ci/gitlab_ci_yaml_processor.rb | 2 +- lib/gitlab/serialize/ci/variables.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 8806a506ffa..7463bd719d5 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -118,7 +118,7 @@ module Ci .merge(job_variables(name)) variables.map do |key, value| - { key: key.to_s, value: value.to_s, public: true } + { key: key.to_s, value: value, public: true } end end diff --git a/lib/gitlab/serialize/ci/variables.rb b/lib/gitlab/serialize/ci/variables.rb index 8919f0ccd00..3a9443bfcd9 100644 --- a/lib/gitlab/serialize/ci/variables.rb +++ b/lib/gitlab/serialize/ci/variables.rb @@ -14,7 +14,6 @@ module Gitlab object.map do |variable| variable[:key] = variable[:key].to_s - variable[:value] = variable[:value].to_s variable end end From 527428a78ed4834b936b0aa7a6da430bf66baa2d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 19 Dec 2016 14:38:23 +0100 Subject: [PATCH 304/386] updated spec --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 13f593fb2db..4b07fa53bf5 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -190,8 +190,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end end - context 'existing builds' do - it 'do not throw errors' do + context 'when there is an existing build with build token' do + it 'restores project json correctly' do create(:ci_build, token: 'abcd') expect(restored_project_json).to be true From 1e2e0de9441a2f9777bb989e8a8c275c2b103ca7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 22:01:37 +0800 Subject: [PATCH 305/386] Define actions in let so that it could be overridden Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8091/diffs#note_20236356 --- spec/requests/ci/api/builds_spec.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 2963fe85478..fdb2234d32b 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -249,7 +249,13 @@ describe Ci::API::Builds do end describe 'PATCH /builds/:id/trace.txt' do - let(:build) { create(:ci_build, :pending, :trace, runner_id: runner.id) } + let(:build) do + attributes = {runner_id: runner.id, pipeline: pipeline} + create(:ci_build, :pending, :trace, attributes) do |build| + build.run + end + end + let(:headers) { { Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token, 'Content-Type' => 'text/plain' } } let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) } let(:update_interval) { 10.seconds.to_i } @@ -276,7 +282,6 @@ describe Ci::API::Builds do end before do - build.run! initial_patch_the_trace end @@ -332,17 +337,15 @@ describe Ci::API::Builds do context 'when project for the build has been deleted' do let(:build) do - result = create(:ci_build, - :pending, - :trace, - runner_id: runner.id, - pipeline: pipeline) - result.project.update(pending_delete: true) - result + attributes = {runner_id: runner.id, pipeline: pipeline} + create(:ci_build, :pending, :trace, attributes) do |build| + build.run + build.project.update(pending_delete: true) + end end it 'responds with forbidden' do - expect(response.status).to eq 403 + expect(response.status).to eq(403) end end end From 0aa61e8e6f7d528330a50e64bf27b2d721c4ec5a Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Mon, 19 Dec 2016 20:11:43 +0600 Subject: [PATCH 306/386] hides new issue btn for now loggedin user --- app/views/shared/empty_states/_issues.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml index e939278bc07..07d4927b6c9 100644 --- a/app/views/shared/empty_states/_issues.html.haml +++ b/app/views/shared/empty_states/_issues.html.haml @@ -8,7 +8,7 @@ = render 'shared/empty_states/icons/issues.svg' .col-xs-12{ class: "#{'col-sm-6' if has_button}" } .text-content - - if has_button + - if has_button && current_user %h4 The Issue Tracker is a good place to add things that need to be improved or solved in a project! %p From e6c83b1c1f3db5cea9730ae320f3a6525fd6fe2a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 22:19:03 +0800 Subject: [PATCH 307/386] Just set the status rather than calling event Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8091#note_20239558 --- spec/requests/ci/api/builds_spec.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index fdb2234d32b..5acda0fd729 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -251,9 +251,7 @@ describe Ci::API::Builds do describe 'PATCH /builds/:id/trace.txt' do let(:build) do attributes = {runner_id: runner.id, pipeline: pipeline} - create(:ci_build, :pending, :trace, attributes) do |build| - build.run - end + create(:ci_build, :running, :trace, attributes) end let(:headers) { { Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token, 'Content-Type' => 'text/plain' } } @@ -338,8 +336,7 @@ describe Ci::API::Builds do context 'when project for the build has been deleted' do let(:build) do attributes = {runner_id: runner.id, pipeline: pipeline} - create(:ci_build, :pending, :trace, attributes) do |build| - build.run + create(:ci_build, :running, :trace, attributes) do |build| build.project.update(pending_delete: true) end end From 8e2ea26cc1fff5787c0cf0c4b0160e1815ee1344 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 22:22:57 +0800 Subject: [PATCH 308/386] Spaces for literal hash --- spec/models/build_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index e97f6ae3cea..cd3b6d51545 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -1319,7 +1319,7 @@ describe Ci::Build, models: true do context 'when environment uses yaml_variables containing symbol keys' do let(:build) do create(:ci_build, - yaml_variables: [{key: :APP_HOST, value: 'host'}], + yaml_variables: [{ key: :APP_HOST, value: 'host' }], environment: 'review/$APP_HOST') end From ad39831049a5d645da2df62de4f9a618fa6f80d7 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Mon, 19 Dec 2016 16:29:58 +0500 Subject: [PATCH 309/386] Move admin projects spinach tests to rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- features/admin/projects.feature | 47 ---------- features/steps/admin/projects.rb | 104 --------------------- spec/features/admin/admin_projects_spec.rb | 99 ++++++++++++++++++-- 3 files changed, 91 insertions(+), 159 deletions(-) delete mode 100644 features/admin/projects.feature delete mode 100644 features/steps/admin/projects.rb diff --git a/features/admin/projects.feature b/features/admin/projects.feature deleted file mode 100644 index 8929bcf8d80..00000000000 --- a/features/admin/projects.feature +++ /dev/null @@ -1,47 +0,0 @@ -@admin -Feature: Admin Projects - Background: - Given I sign in as an admin - And there are projects in system - - Scenario: I should see non-archived projects in the list - Given archived project "Archive" - When I visit admin projects page - Then I should see all non-archived projects - And I should not see project "Archive" - - @javascript - Scenario: I should see all projects in the list - Given archived project "Archive" - When I visit admin projects page - And I select "Show archived projects" - Then I should see all projects - And I should see "archived" label - - Scenario: Projects show - When I visit admin projects page - And I click on first project - Then I should see project details - - @javascript - Scenario: Transfer project - Given group 'Web' - And I visit admin project page - When I transfer project to group 'Web' - Then I should see project transfered - - @javascript - Scenario: Signed in admin should be able to add himself to a project - Given "John Doe" owns private project "Enterprise" - When I visit project "Enterprise" members page - When I select current user as "Developer" - Then I should see current user as "Developer" - - @javascript - Scenario: Signed in admin should be able to remove himself from a project - Given "John Doe" owns private project "Enterprise" - And current user is developer of project "Enterprise" - When I visit project "Enterprise" members page - Then I should see current user as "Developer" - When I click on the "Remove User From Project" button for current user - Then I should not see current user as "Developer" diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb deleted file mode 100644 index 2b8cd030ace..00000000000 --- a/features/steps/admin/projects.rb +++ /dev/null @@ -1,104 +0,0 @@ -class Spinach::Features::AdminProjects < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - include SharedProject - include SharedUser - include Select2Helper - - step 'I should see all non-archived projects' do - Project.non_archived.each do |p| - expect(page).to have_content p.name_with_namespace - end - end - - step 'I should see all projects' do - Project.all.each do |p| - expect(page).to have_content p.name_with_namespace - end - end - - step 'I select "Show archived projects"' do - find(:css, '#sort-projects-dropdown').click - click_link 'Show archived projects' - end - - step 'I should see "archived" label' do - expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') - end - - step 'I click on first project' do - click_link Project.first.name_with_namespace - end - - step 'I should see project details' do - project = Project.first - expect(current_path).to eq admin_namespace_project_path(project.namespace, project) - expect(page).to have_content(project.name_with_namespace) - expect(page).to have_content(project.creator.name) - end - - step 'I visit admin project page' do - visit admin_namespace_project_path(project.namespace, project) - end - - step 'I transfer project to group \'Web\'' do - allow_any_instance_of(Projects::TransferService). - to receive(:move_uploads_to_new_namespace).and_return(true) - click_button 'Search for Namespace' - click_link 'group: web' - click_button 'Transfer' - end - - step 'group \'Web\'' do - create(:group, name: 'Web') - end - - step 'I should see project transfered' do - expect(page).to have_content 'Web / ' + project.name - expect(page).to have_content 'Namespace: Web' - end - - step 'I visit project "Enterprise" members page' do - project = Project.find_by!(name: "Enterprise") - visit namespace_project_project_members_path(project.namespace, project) - end - - step 'I select current user as "Developer"' do - page.within ".users-project-form" do - select2(current_user.id, from: "#user_ids", multiple: true) - select "Developer", from: "access_level" - end - - click_button "Add to project" - end - - step 'I should see current user as "Developer"' do - page.within '.content-list' do - expect(page).to have_content(current_user.name) - expect(page).to have_content('Developer') - end - end - - step 'current user is developer of project "Enterprise"' do - project = Project.find_by!(name: "Enterprise") - project.team << [current_user, :developer] - end - - step 'I click on the "Remove User From Project" button for current user' do - find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I should not see current_user as "Developer"' do - expect(page).not_to have_selector(:css, '.content-list') - end - - def project - @project ||= Project.first - end - - def group - Group.find_by(name: 'Web') - end -end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index a36bfd574cb..a5b88812b75 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -1,12 +1,17 @@ require 'spec_helper' describe "Admin::Projects", feature: true do - before do - @project = create(:project) + include Select2Helper + + let(:user) { create :user } + let!(:project) { create(:project) } + let!(:current_user) do login_as :admin end describe "GET /admin/projects" do + let!(:archived_project) { create :project, :public, archived: true } + before do visit admin_projects_path end @@ -15,20 +20,98 @@ describe "Admin::Projects", feature: true do expect(current_path).to eq(admin_projects_path) end - it "has projects list" do - expect(page).to have_content(@project.name) + it 'renders projects list without archived project' do + expect(page).to have_content(project.name) + expect(page).not_to have_content(archived_project.name) + end + + it 'renders all projects', js: true do + find(:css, '#sort-projects-dropdown').click + click_link 'Show archived projects' + + expect(page).to have_content(project.name) + expect(page).to have_content(archived_project.name) + expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') end end - describe "GET /admin/projects/:id" do + describe "GET /admin/projects/:namespace_id/:id" do before do visit admin_projects_path - click_link "#{@project.name}" + click_link "#{project.name}" + end + + it do + expect(current_path).to eq admin_namespace_project_path(project.namespace, project) end it "has project info" do - expect(page).to have_content(@project.path) - expect(page).to have_content(@project.name) + expect(page).to have_content(project.path) + expect(page).to have_content(project.name) + expect(page).to have_content(project.name_with_namespace) + expect(page).to have_content(project.creator.name) + end + end + + describe 'transfer project' do + before do + create(:group, name: 'Web') + + allow_any_instance_of(Projects::TransferService). + to receive(:move_uploads_to_new_namespace).and_return(true) + end + + it 'transfers project to group web', js: true do + visit admin_namespace_project_path(project.namespace, project) + + click_button 'Search for Namespace' + click_link 'group: web' + click_button 'Transfer' + + expect(page).to have_content("Web / #{project.name}") + expect(page).to have_content('Namespace: Web') + end + end + + describe 'add admin himself to a project' do + before do + project.team << [user, :master] + end + + it 'adds admin a to a project as developer', js: true do + visit namespace_project_project_members_path(project.namespace, project) + + page.within '.users-project-form' do + select2(current_user.id, from: '#user_ids', multiple: true) + select 'Developer', from: 'access_level' + end + + click_button 'Add to project' + + page.within '.content-list' do + expect(page).to have_content(current_user.name) + expect(page).to have_content('Developer') + end + end + end + + describe 'admin remove himself from a project' do + before do + project.team << [user, :master] + project.team << [current_user, :developer] + end + + it 'removes admin from the project' do + visit namespace_project_project_members_path(project.namespace, project) + + page.within '.content-list' do + expect(page).to have_content(current_user.name) + expect(page).to have_content('Developer') + end + + find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click + + expect(page).not_to have_selector(:css, '.content-list') end end end From 92af7bfc25bf540fcdd295b3ecfacfa24c5e6d9f Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Mon, 19 Dec 2016 20:35:40 +0600 Subject: [PATCH 310/386] adds changelog files --- .../unreleased/25093-hide-new-issue-btn-non-loggedin-user.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/25093-hide-new-issue-btn-non-loggedin-user.yml diff --git a/changelogs/unreleased/25093-hide-new-issue-btn-non-loggedin-user.yml b/changelogs/unreleased/25093-hide-new-issue-btn-non-loggedin-user.yml new file mode 100644 index 00000000000..18836e7a90b --- /dev/null +++ b/changelogs/unreleased/25093-hide-new-issue-btn-non-loggedin-user.yml @@ -0,0 +1,4 @@ +--- +title: Hides new issue button for non loggedin user +merge_request: 8175 +author: From 298d05a5c3cc3c2f1daa4d77c45f9c90b53248df Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 19 Dec 2016 15:40:06 +0100 Subject: [PATCH 311/386] Improve after feedback --- features/project/service.feature | 6 ++-- features/steps/project/services.rb | 8 +++--- lib/api/services.rb | 9 +++++- lib/gitlab/chat_commands/help.rb | 28 ------------------- lib/gitlab/chat_commands/presenter.rb | 2 +- .../services/slack_slash_command_spec.rb | 18 ++++++------ .../mattermost_slash_commands_service_spec.rb | 4 +-- .../slack_slash_commands_service.rb | 6 ++-- .../chat_slash_commands_shared_examples.rb | 2 +- 9 files changed, 32 insertions(+), 51 deletions(-) delete mode 100644 lib/gitlab/chat_commands/help.rb diff --git a/features/project/service.feature b/features/project/service.feature index 3a7b8308524..892db48d785 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -39,9 +39,9 @@ Feature: Project Services Scenario: Activate Slack service When I visit project "Shop" services page - And I click Slack service link - And I fill Slack settings - Then I should see Slack service settings saved + And I click Slack Notifications service link + And I fill Slack Notifications settings + Then I should see Slack Notifications service settings saved Scenario: Activate Pushover service When I visit project "Shop" services page diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index bd6466f3686..06a1afedbd9 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -137,17 +137,17 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(find_field('Colorize messages').value).to eq '1' end - step 'I click Slack service link' do - click_link 'Slack' + step 'I click Slack Notifications service link' do + click_link 'Slack Notifications' end - step 'I fill Slack settings' do + step 'I fill Slack Notifications settings' do check 'Active' fill_in 'Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' click_button 'Save' end - step 'I should see Slack service settings saved' do + step 'I should see Slack Notifications service settings saved' do expect(find_field('Webhook').value).to eq 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' end diff --git a/lib/api/services.rb b/lib/api/services.rb index 59232c84c24..aa97f6af0b2 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -378,7 +378,6 @@ module API desc: 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)' }, ], - 'mattermost-slash-commands' => [ { required: true, @@ -387,6 +386,14 @@ module API desc: 'The Mattermost token' } ], + 'slack-slash-commands' => [ + { + required: true, + name: :token, + type: String, + desc: 'The Slack token' + } + ], 'pipelines-email' => [ { required: true, diff --git a/lib/gitlab/chat_commands/help.rb b/lib/gitlab/chat_commands/help.rb deleted file mode 100644 index e76733f5445..00000000000 --- a/lib/gitlab/chat_commands/help.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Gitlab - module ChatCommands - class Help < BaseCommand - # This class has to be used last, as it always matches. It has to match - # because other commands were not triggered and we want to show the help - # command - def self.match(_text) - true - end - - def self.help_message - 'help' - end - - def self.allowed?(_project, _user) - true - end - - def execute(commands) - Gitlab::ChatCommands::Presenters::Help.new(commands).present(trigger) - end - - def trigger - params[:command] - end - end - end -end diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index b4c4dc252ca..caceaa25391 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -1,7 +1,7 @@ module Gitlab module ChatCommands class Presenter - include Gitlab::Routing.url_helpers + include Gitlab::Routing def authorize_chat_name(url) message = if url diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb index dee43d69895..70e203efcf5 100644 --- a/spec/features/projects/services/slack_slash_command_spec.rb +++ b/spec/features/projects/services/slack_slash_command_spec.rb @@ -1,18 +1,18 @@ require 'spec_helper' -feature 'Setup Slack slash commands', feature: true do +feature 'Slack slash commands', feature: true do include WaitForAjax - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:service) { project.create_slack_slash_commands_service } + given(:user) { create(:user) } + given(:project) { create(:project) } + given(:service) { project.create_slack_slash_commands_service } - before do + background do project.team << [user, :master] login_as(user) end - describe 'user visits the slack slash command config page', js: true do + scenario 'user visits the slack slash command config page', js: true do it 'shows a help message' do visit edit_namespace_project_service_path(project.namespace, project, service) @@ -22,8 +22,8 @@ feature 'Setup Slack slash commands', feature: true do end end - describe 'saving a token' do - let(:token) { ('a'..'z').to_a.join } + scenario 'saving a token' do + given(:token) { ('a'..'z').to_a.join } it 'shows the token after saving' do visit edit_namespace_project_service_path(project.namespace, project, service) @@ -37,7 +37,7 @@ feature 'Setup Slack slash commands', feature: true do end end - describe 'the trigger url' do + scenario 'the trigger url' do it 'shows the correct url' do visit edit_namespace_project_service_path(project.namespace, project, service) diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index 5c34cb6b4cf..1ae1483e2a4 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe MattermostSlashCommandsService, models: true do - it_behaves_like "chat slash commands" +describe MattermostSlashCommandsService, :models do + it_behaves_like "chat slash commands service" end diff --git a/spec/models/project_services/slack_slash_commands_service.rb b/spec/models/project_services/slack_slash_commands_service.rb index c3fa80caebe..5775e439906 100644 --- a/spec/models/project_services/slack_slash_commands_service.rb +++ b/spec/models/project_services/slack_slash_commands_service.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackSlashCommandsService, models: true do - it_behaves_like "chat slash commands" +describe SlackSlashCommandsService, :models do + it_behaves_like "chat slash commands service" describe '#trigger' do context 'when an auth url is generated' do @@ -15,11 +15,13 @@ describe SlackSlashCommandsService, models: true do token: 'token' } end + let(:service) do project.create_slack_slash_commands_service( properties: { token: 'token' } ) end + let(:authorize_url) do 'http://authorize.example.com/' end diff --git a/spec/support/chat_slash_commands_shared_examples.rb b/spec/support/chat_slash_commands_shared_examples.rb index 96130b45235..4dfa29849ee 100644 --- a/spec/support/chat_slash_commands_shared_examples.rb +++ b/spec/support/chat_slash_commands_shared_examples.rb @@ -1,4 +1,4 @@ -RSpec.shared_examples 'chat slash commands' do +RSpec.shared_examples 'chat slash commands service' do describe "Associations" do it { is_expected.to respond_to :token } it { is_expected.to have_many :chat_names } From 7269df2822de786716636f757dee0f3780c5b08b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Dec 2016 15:10:00 +0000 Subject: [PATCH 312/386] Use group name for css classes --- app/views/projects/ci/pipelines/_pipeline.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 2e2bac9cd32..74ad9557130 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -44,10 +44,10 @@ - pipeline.stages.each do |stage| - if stage.status - detailed_status = stage.detailed_status(current_user) - - klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status}" + - klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status.group}" - hasMultipleBuilds = stage.statuses.count > 1 - icon_status = "#{detailed_status.icon}_borderless" - - icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status}" + - icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status.group}" - tooltip = "#{stage.name}: #{detailed_status.label || 'not found'}" .stage-container.mini-pipeline-graph From a0690c4c01fceccef9efca70a9256f5790fba9c7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 23:20:22 +0800 Subject: [PATCH 313/386] Spaces around literal hash Feedback: https://gitlab.com/gitlab-org/gitlab-ce/builds/7617209 --- spec/requests/ci/api/builds_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 5acda0fd729..79f12ace999 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -250,7 +250,7 @@ describe Ci::API::Builds do describe 'PATCH /builds/:id/trace.txt' do let(:build) do - attributes = {runner_id: runner.id, pipeline: pipeline} + attributes = { runner_id: runner.id, pipeline: pipeline } create(:ci_build, :running, :trace, attributes) end @@ -335,7 +335,7 @@ describe Ci::API::Builds do context 'when project for the build has been deleted' do let(:build) do - attributes = {runner_id: runner.id, pipeline: pipeline} + attributes = { runner_id: runner.id, pipeline: pipeline } create(:ci_build, :running, :trace, attributes) do |build| build.project.update(pending_delete: true) end From 4d8a2bc987dbdd417f7ae971c884b084a8982f0a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 19 Dec 2016 23:32:09 +0800 Subject: [PATCH 314/386] Fix tests because now we don't convert values --- spec/lib/gitlab/serialize/ci/variables_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/gitlab/serialize/ci/variables_spec.rb b/spec/lib/gitlab/serialize/ci/variables_spec.rb index 797baef640c..7ea74da5252 100644 --- a/spec/lib/gitlab/serialize/ci/variables_spec.rb +++ b/spec/lib/gitlab/serialize/ci/variables_spec.rb @@ -10,9 +10,9 @@ describe Gitlab::Serialize::Ci::Variables do { key: 'wee', value: 1, public: false }] end - it 'converts key and values into strings' do + it 'converts keys into strings' do is_expected.to eq([ { key: 'key', value: 'value', public: true }, - { key: 'wee', value: '1', public: false }]) + { key: 'wee', value: 1, public: false }]) end end From 786457ca3e77e6af02ecabcfd6d5eae6a31837c1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 16 Dec 2016 16:13:46 +0100 Subject: [PATCH 315/386] Fix for missing service when importing from EE to CE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../fix-import-export-ee-services.yml | 4 ++++ .../import_export/project_tree_restorer.rb | 2 +- lib/gitlab/import_export/relation_factory.rb | 7 ++++++ spec/lib/gitlab/import_export/project.json | 22 +++++++++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/fix-import-export-ee-services.yml diff --git a/changelogs/unreleased/fix-import-export-ee-services.yml b/changelogs/unreleased/fix-import-export-ee-services.yml new file mode 100644 index 00000000000..c0aacbc96f8 --- /dev/null +++ b/changelogs/unreleased/fix-import-export-ee-services.yml @@ -0,0 +1,4 @@ +--- +title: Fix missing service error importing from EE to CE +merge_request: 8144 +author: diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index c551321c18d..cda6ddf0443 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -120,7 +120,7 @@ module Gitlab members_mapper: members_mapper, user: @user, project_id: restored_project.id) - end + end.compact relation_hash_list.is_a?(Array) ? relation_array : relation_array.first end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 9b590bcee8d..b4f0f761add 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -40,6 +40,8 @@ module Gitlab # the relation_hash, updating references with new object IDs, mapping users using # the "members_mapper" object, also updating notes if required. def create + return nil if unknown_service? + setup_models generate_imported_object @@ -217,6 +219,11 @@ module Gitlab existing_object end end + + def unknown_service? + @relation_name == :services && parsed_relation_hash['type'] && + !Object.const_defined?(parsed_relation_hash['type']) + end end end end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 1e5901651ae..400fc2a1f91 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -7411,6 +7411,28 @@ "category": "common", "default": false, "wiki_page_events": true + }, + { + "id": 101, + "title": "JenkinsDeprecated", + "project_id": 5, + "created_at": "2016-06-14T15:01:51.031Z", + "updated_at": "2016-06-14T15:01:51.031Z", + "active": false, + "properties": { + + }, + "template": false, + "push_events": true, + "issues_events": true, + "merge_requests_events": true, + "tag_push_events": true, + "note_events": true, + "build_events": true, + "category": "common", + "default": false, + "wiki_page_events": true, + "type": "JenkinsDeprecatedService" } ], "hooks": [ From d80b2677319e5ba5570be4b691a07cbbaea2fd47 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 19 Dec 2016 00:18:06 -0500 Subject: [PATCH 316/386] Check if selected object is valid before passing to calback --- app/assets/javascripts/gl_dropdown.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 57dabfe05e4..bb516b3d2df 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -343,16 +343,18 @@ selector = ".dropdown-page-one .dropdown-content a"; } this.dropdown.on("click", selector, function(e) { - var $el, selected; + var $el, selected, selectedObj, isMarking; $el = $(this); selected = self.rowClicked($el); + selectedObj = selected ? selected[0] : null; + isMarking = selected ? selected[1] : null; if (self.options.clicked) { - self.options.clicked(selected[0], $el, e, selected[1]); + self.options.clicked(selectedObj, $el, e, isMarking); } // Update label right after all modifications in dropdown has been done if (self.options.toggleLabel) { - self.updateLabel(selected[0], $el, self); + self.updateLabel(selectedObj, $el, self); } $el.trigger('blur'); From 0631c7bcdce5dcb2aac94b1b755e63022a62469f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 16 Dec 2016 09:13:58 +0100 Subject: [PATCH 317/386] Fix MR issue to do with merge user --- app/models/merge_request.rb | 2 +- changelogs/unreleased/fix-import-export-mr-error.yml | 4 ++++ lib/gitlab/import_export/relation_factory.rb | 2 +- spec/lib/gitlab/import_export/project.json | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/fix-import-export-mr-error.yml diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b73d7acefea..b1914df3020 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -97,7 +97,7 @@ class MergeRequest < ActiveRecord::Base validates :source_branch, presence: true validates :target_project, presence: true validates :target_branch, presence: true - validates :merge_user, presence: true, if: :merge_when_build_succeeds? + validates :merge_user, presence: true, if: :merge_when_build_succeeds?, unless: :importing? validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?] validate :validate_fork, unless: :closed_without_fork? diff --git a/changelogs/unreleased/fix-import-export-mr-error.yml b/changelogs/unreleased/fix-import-export-mr-error.yml new file mode 100644 index 00000000000..e1137bca131 --- /dev/null +++ b/changelogs/unreleased/fix-import-export-mr-error.yml @@ -0,0 +1,4 @@ +--- +title: Fix Import/Export merge requests error while importing +merge_request: +author: diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index a0e80fccad9..66e54e86ea9 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -14,7 +14,7 @@ module Gitlab priorities: :label_priorities, label: :project_label }.freeze - USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id].freeze + USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id].freeze PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index ed9df468ced..19bd15bffbc 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -2517,7 +2517,7 @@ "merge_params": { "force_remove_source_branch": null }, - "merge_when_build_succeeds": false, + "merge_when_build_succeeds": true, "merge_user_id": null, "merge_commit_sha": null, "deleted_at": null, From f73193c328b871a9a3af803012c10d9bc1bd0904 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Dec 2016 17:31:58 +0100 Subject: [PATCH 318/386] Smarter refreshing of authorized projects Prior to this commit the refreshing of authorized projects was done in two steps: 1. Remove existing authorizations 2. Insert a new list of all authorizations This can lead to a high amount of dead tuples as every time all rows are being replaced. For example, if a user with 100 authorizations is given access to a new project this would lead to: * 100 rows being removed * 101 new rows being inserted This commit changes the way this system works so it only removes/inserts what is necessary. Using the above example this would lead to only 1 new row being inserted, with the initial 100 being left untouched. Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/25257 --- app/models/project_authorization.rb | 13 ++ app/models/user.rb | 34 +--- .../refresh_authorized_projects_service.rb | 128 ++++++++++++ app/workers/authorized_projects_worker.rb | 22 +-- spec/models/project_authorization_spec.rb | 25 +++ ...efresh_authorized_projects_service_spec.rb | 185 ++++++++++++++++++ .../authorized_projects_worker_spec.rb | 14 +- 7 files changed, 362 insertions(+), 59 deletions(-) create mode 100644 app/services/users/refresh_authorized_projects_service.rb create mode 100644 spec/models/project_authorization_spec.rb create mode 100644 spec/services/users/refresh_authorized_projects_service_spec.rb diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb index a00d43773d9..4c7f4f5a429 100644 --- a/app/models/project_authorization.rb +++ b/app/models/project_authorization.rb @@ -5,4 +5,17 @@ class ProjectAuthorization < ActiveRecord::Base validates :project, presence: true validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true validates :user, uniqueness: { scope: [:project, :access_level] }, presence: true + + def self.insert_authorizations(rows, per_batch = 1000) + rows.each_slice(per_batch) do |slice| + tuples = slice.map do |tuple| + tuple.map { |value| connection.quote(value) } + end + + connection.execute <<-EOF.strip_heredoc + INSERT INTO project_authorizations (user_id, project_id, access_level) + VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')} + EOF + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index 3a17c98eff6..9f5cc149361 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -443,22 +443,16 @@ class User < ActiveRecord::Base end def refresh_authorized_projects - transaction do - project_authorizations.delete_all + Users::RefreshAuthorizedProjectsService.new(self).execute + end - # project_authorizations_union can return multiple records for the same - # project/user with different access_level so we take row with the maximum - # access_level - project_authorizations.connection.execute <<-SQL - INSERT INTO project_authorizations (user_id, project_id, access_level) - SELECT user_id, project_id, MAX(access_level) AS access_level - FROM (#{project_authorizations_union.to_sql}) sub - GROUP BY user_id, project_id - SQL + def remove_project_authorizations(project_ids) + project_authorizations.where(id: project_ids).delete_all + end - unless authorized_projects_populated - update_column(:authorized_projects_populated, true) - end + def set_authorized_projects_column + unless authorized_projects_populated + update_column(:authorized_projects_populated, true) end end @@ -905,18 +899,6 @@ class User < ActiveRecord::Base private - # Returns a union query of projects that the user is authorized to access - def project_authorizations_union - relations = [ - personal_projects.select("#{id} AS user_id, projects.id AS project_id, #{Gitlab::Access::MASTER} AS access_level"), - groups_projects.select_for_project_authorization, - projects.select_for_project_authorization, - groups.joins(:shared_projects).select_for_project_authorization - ] - - Gitlab::SQL::Union.new(relations) - end - def ci_projects_union scope = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] } groups = groups_projects.where(members: scope) diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb new file mode 100644 index 00000000000..7d38ac3a374 --- /dev/null +++ b/app/services/users/refresh_authorized_projects_service.rb @@ -0,0 +1,128 @@ +module Users + # Service for refreshing the authorized projects of a user. + # + # This particular service class can not be used to update data for the same + # user concurrently. Doing so could lead to an incorrect state. To ensure this + # doesn't happen a caller must synchronize access (e.g. using + # `Gitlab::ExclusiveLease`). + # + # Usage: + # + # user = User.find_by(username: 'alice') + # service = Users::RefreshAuthorizedProjectsService.new(some_user) + # service.execute + class RefreshAuthorizedProjectsService + attr_reader :user + + LEASE_TIMEOUT = 1.minute.to_i + + # user - The User for which to refresh the authorized projects. + def initialize(user) + @user = user + + # We need an up to date User object that has access to all relations that + # may have been created earlier. The only way to ensure this is to reload + # the User object. + user.reload + end + + # This method returns the updated User object. + def execute + current = current_authorizations_per_project + fresh = fresh_access_levels_per_project + + remove = current.each_with_object([]) do |(project_id, row), array| + # rows not in the new list or with a different access level should be + # removed. + if !fresh[project_id] || fresh[project_id] != row.access_level + array << row.id + end + end + + add = fresh.each_with_object([]) do |(project_id, level), array| + # rows not in the old list or with a different access level should be + # added. + if !current[project_id] || current[project_id].access_level != level + array << [user.id, project_id, level] + end + end + + update_with_lease(remove, add) + end + + # Updates the list of authorizations using an exclusive lease. + def update_with_lease(remove = [], add = []) + lease_key = "refresh_authorized_projects:#{user.id}" + lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT) + + until uuid = lease.try_obtain + # Keep trying until we obtain the lease. If we don't do so we may end up + # not updating the list of authorized projects properly. To prevent + # hammering Redis too much we'll wait for a bit between retries. + sleep(1) + end + + begin + update_authorizations(remove, add) + ensure + Gitlab::ExclusiveLease.cancel(lease_key, uuid) + end + end + + # Updates the list of authorizations for the current user. + # + # remove - The IDs of the authorization rows to remove. + # add - Rows to insert in the form `[user id, project id, access level]` + def update_authorizations(remove = [], add = []) + return if remove.empty? && add.empty? + + User.transaction do + user.remove_project_authorizations(remove) unless remove.empty? + ProjectAuthorization.insert_authorizations(add) unless add.empty? + user.set_authorized_projects_column + end + + # Since we batch insert authorization rows, Rails' associations may get + # out of sync. As such we force a reload of the User object. + user.reload + end + + def fresh_access_levels_per_project + fresh_authorizations.each_with_object({}) do |row, hash| + hash[row.project_id] = row.access_level + end + end + + def current_authorizations_per_project + current_authorizations.each_with_object({}) do |row, hash| + hash[row.project_id] = row + end + end + + def current_authorizations + user.project_authorizations.select(:id, :project_id, :access_level) + end + + def fresh_authorizations + ProjectAuthorization. + unscoped. + select('project_id, MAX(access_level) AS access_level'). + from("(#{project_authorizations_union.to_sql}) #{ProjectAuthorization.table_name}"). + group(:project_id) + end + + private + + # Returns a union query of projects that the user is authorized to access + def project_authorizations_union + relations = [ + user.personal_projects.select("#{user.id} AS user_id, projects.id AS project_id, #{Gitlab::Access::MASTER} AS access_level"), + user.groups_projects.select_for_project_authorization, + user.projects.select_for_project_authorization, + user.groups.joins(:shared_projects).select_for_project_authorization + ] + + Gitlab::SQL::Union.new(relations) + end + end +end diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb index fccddb70d18..2badd0680fb 100644 --- a/app/workers/authorized_projects_worker.rb +++ b/app/workers/authorized_projects_worker.rb @@ -2,8 +2,6 @@ class AuthorizedProjectsWorker include Sidekiq::Worker include DedicatedSidekiqQueue - LEASE_TIMEOUT = 1.minute.to_i - def self.bulk_perform_async(args_list) Sidekiq::Client.push_bulk('class' => self, 'args' => args_list) end @@ -11,24 +9,6 @@ class AuthorizedProjectsWorker def perform(user_id) user = User.find_by(id: user_id) - refresh(user) if user - end - - def refresh(user) - lease_key = "refresh_authorized_projects:#{user.id}" - lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT) - - until uuid = lease.try_obtain - # Keep trying until we obtain the lease. If we don't do so we may end up - # not updating the list of authorized projects properly. To prevent - # hammering Redis too much we'll wait for a bit between retries. - sleep(1) - end - - begin - user.refresh_authorized_projects - ensure - Gitlab::ExclusiveLease.cancel(lease_key, uuid) - end + user.refresh_authorized_projects if user end end diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb new file mode 100644 index 00000000000..33ef67f97a7 --- /dev/null +++ b/spec/models/project_authorization_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe ProjectAuthorization do + let(:user) { create(:user) } + let(:project1) { create(:empty_project) } + let(:project2) { create(:empty_project) } + + describe '.insert_authorizations' do + it 'inserts the authorizations' do + described_class. + insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]]) + + expect(user.project_authorizations.count).to eq(1) + end + + it 'inserts rows in batches' do + described_class.insert_authorizations([ + [user.id, project1.id, Gitlab::Access::MASTER], + [user.id, project2.id, Gitlab::Access::MASTER], + ], 1) + + expect(user.project_authorizations.count).to eq(2) + end + end +end diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb new file mode 100644 index 00000000000..72c8f7cd8ec --- /dev/null +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -0,0 +1,185 @@ +require 'spec_helper' + +describe Users::RefreshAuthorizedProjectsService do + let(:project) { create(:empty_project) } + let(:user) { project.namespace.owner } + let(:service) { described_class.new(user) } + + def create_authorization(project, user, access_level = Gitlab::Access::MASTER) + ProjectAuthorization. + create!(project: project, user: user, access_level: access_level) + end + + describe '#execute' do + before do + user.project_authorizations.delete_all + end + + it 'updates the authorized projects of the user' do + project2 = create(:empty_project) + to_remove = create_authorization(project2, user) + + expect(service).to receive(:update_with_lease). + with([to_remove.id], [[user.id, project.id, Gitlab::Access::MASTER]]) + + service.execute + end + + it 'sets the access level of a project to the highest available level' do + to_remove = create_authorization(project, user, Gitlab::Access::DEVELOPER) + + expect(service).to receive(:update_with_lease). + with([to_remove.id], [[user.id, project.id, Gitlab::Access::MASTER]]) + + service.execute + end + + it 'returns a User' do + expect(service.execute).to be_an_instance_of(User) + end + end + + describe '#update_with_lease', :redis do + it 'refreshes the authorizations using a lease' do + expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain). + and_return('foo') + + expect(Gitlab::ExclusiveLease).to receive(:cancel). + with(an_instance_of(String), 'foo') + + expect(service).to receive(:update_authorizations).with([1], []) + + service.update_with_lease([1]) + end + end + + describe '#update_authorizations' do + it 'does nothing when there are no rows to add and remove' do + expect(user).not_to receive(:remove_project_authorizations) + expect(ProjectAuthorization).not_to receive(:insert_authorizations) + expect(user).not_to receive(:set_authorized_projects_column) + + service.update_authorizations([], []) + end + + it 'removes authorizations that should be removed' do + authorization = create_authorization(project, user) + + service.update_authorizations([authorization.id]) + + expect(user.project_authorizations).to be_empty + end + + it 'inserts authorizations that should be added' do + service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MASTER]]) + + authorizations = user.project_authorizations + + expect(authorizations.length).to eq(1) + expect(authorizations[0].user_id).to eq(user.id) + expect(authorizations[0].project_id).to eq(project.id) + expect(authorizations[0].access_level).to eq(Gitlab::Access::MASTER) + end + + it 'populates the authorized projects column' do + # make sure we start with a nil value no matter what the default in the + # factory may be. + user.update(authorized_projects_populated: nil) + + service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MASTER]]) + + expect(user.authorized_projects_populated).to eq(true) + end + end + + describe '#fresh_access_levels_per_project' do + let(:hash) { service.fresh_access_levels_per_project } + + it 'returns a Hash' do + expect(hash).to be_an_instance_of(Hash) + end + + it 'sets the keys to the project IDs' do + expect(hash.keys).to eq([project.id]) + end + + it 'sets the values to the access levels' do + expect(hash.values).to eq([Gitlab::Access::MASTER]) + end + end + + describe '#current_authorizations_per_project' do + before { create_authorization(project, user) } + + let(:hash) { service.current_authorizations_per_project } + + it 'returns a Hash' do + expect(hash).to be_an_instance_of(Hash) + end + + it 'sets the keys to the project IDs' do + expect(hash.keys).to eq([project.id]) + end + + it 'sets the values to the project authorization rows' do + expect(hash.values).to eq([ProjectAuthorization.first]) + end + end + + describe '#current_authorizations' do + context 'without authorizations' do + it 'returns an empty list' do + expect(service.current_authorizations.empty?).to eq(true) + end + end + + context 'with an authorization' do + before { create_authorization(project, user) } + + let(:row) { service.current_authorizations.take } + + it 'returns the currently authorized projects' do + expect(service.current_authorizations.length).to eq(1) + end + + it 'includes the row ID for every row' do + expect(row.id).to be_a_kind_of(Numeric) + end + + it 'includes the project ID for every row' do + expect(row.project_id).to eq(project.id) + end + + it 'includes the access level for every row' do + expect(row.access_level).to eq(Gitlab::Access::MASTER) + end + end + end + + describe '#fresh_authorizations' do + it 'returns the new authorized projects' do + expect(service.fresh_authorizations.length).to eq(1) + end + + it 'returns the highest access level' do + project.team.add_guest(user) + + rows = service.fresh_authorizations.to_a + + expect(rows.length).to eq(1) + expect(rows.first.access_level).to eq(Gitlab::Access::MASTER) + end + + context 'every returned row' do + let(:row) { service.fresh_authorizations.take } + + it 'includes the project ID' do + expect(row.project_id).to eq(project.id) + end + + it 'includes the access level' do + expect(row.access_level).to eq(Gitlab::Access::MASTER) + end + end + end +end diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb index 95e2458da35..b6591f272f6 100644 --- a/spec/workers/authorized_projects_worker_spec.rb +++ b/spec/workers/authorized_projects_worker_spec.rb @@ -7,27 +7,17 @@ describe AuthorizedProjectsWorker do it "refreshes user's authorized projects" do user = create(:user) - expect(worker).to receive(:refresh).with(an_instance_of(User)) + expect_any_instance_of(User).to receive(:refresh_authorized_projects) worker.perform(user.id) end context "when the user is not found" do it "does nothing" do - expect(worker).not_to receive(:refresh) + expect_any_instance_of(User).not_to receive(:refresh_authorized_projects) described_class.new.perform(-1) end end end - - describe '#refresh', redis: true do - it 'refreshes the authorized projects of the user' do - user = create(:user) - - expect(user).to receive(:refresh_authorized_projects) - - worker.refresh(user) - end - end end From e5a8160e85dc72028c6463e77081493c68970868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 11:38:21 +0100 Subject: [PATCH 319/386] Add doc for the Gitea importer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/workflow/importing/README.md | 2 +- .../import_projects_from_gitea_new_import.png | Bin 0 -> 27805 bytes ...t_projects_from_gitea_new_project_page.png | Bin 0 -> 52976 bytes .../importing/import_projects_from_gitea.md | 80 ++++++++++++++++++ .../importing/import_projects_from_github.md | 8 +- 5 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 doc/workflow/importing/img/import_projects_from_gitea_new_import.png create mode 100644 doc/workflow/importing/img/import_projects_from_gitea_new_project_page.png create mode 100644 doc/workflow/importing/import_projects_from_gitea.md diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md index 18e5d950866..2d91bee0e94 100644 --- a/doc/workflow/importing/README.md +++ b/doc/workflow/importing/README.md @@ -4,6 +4,7 @@ 1. [GitHub](import_projects_from_github.md) 1. [GitLab.com](import_projects_from_gitlab_com.md) 1. [FogBugz](import_projects_from_fogbugz.md) +1. [Gitea](import_projects_from_gitea.md) 1. [SVN](migrating_from_svn.md) In addition to the specific migration documentation above, you can import any @@ -14,4 +15,3 @@ repository is too large the import can timeout. You can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported. - diff --git a/doc/workflow/importing/img/import_projects_from_gitea_new_import.png b/doc/workflow/importing/img/import_projects_from_gitea_new_import.png new file mode 100644 index 0000000000000000000000000000000000000000..8d76abf102bf34489ff395be3797b3fd3953a295 GIT binary patch literal 27805 zcmc%wRaD%~69x$5?h=9pcMa|^xHHJ$Zovue5)^p165QPh5Zv8;N#6hW-R;@E z*t5$GeX9FcUDaLv^iw_E-+_u!s7M4z5D*ZkGScEI5D+kbARr)j5un~Bpl0(d2nZWf z8F7G`JLK__R8lbONB=45pODfbqLhDNh+yHR0Z=MzZ$sr72R^%Ns0UAHvGngBK0`p^ zzW@97SMC4%C4Uk??z{PRXgmAQDP%{xnl{qA_rfQRCOdd*+Px#khnBC$F|QlKt(&gbL|1*XNZl zf`Wa@kUv^68F_z-uabe29SS(te-vc=blY%A@I!8GXJ<)MdN=0X_`@8(SA5gU7~xk| zsa;{X?$Ec_C;MfQd+WW*(YVQt=K)*v`GN-5%(tUw7}tRb_Rav*$Z>_Upc*-5Wml*# z_jy^cuS9EJqmfO;8VY)*X*}I67?$euD(JVj*Ne-{;RTG6zKPIvtStSRLfyQ6;&c=GPF23l&OxIBetiLP03;-!{H0n zGzl8+Ls1$z{dT~nkp3G|qX!*Vw0zLN(1ZK4aa-b*{ZdPPL-nWB8IY(5+}w&RrH)a{ zr<4!Lo_USNdtrO{Hx5b7-SqOs6*Q(*$ z;!DeTOoIP<38x&BnN5=h6@JWnC@r6?{JgsqfYgp-+L}!YKF$of{G{)2Js!2SpXv97 z>)M2=j}y;#xXt0QFcy*Qyds&N^oV{-4sv|hp{5Q0ZMg{kug;&J*+s*6efWI1^Ac1W z3h5vKs~}(>HcZ1O3zG3eHU)(CLmG@4oVVz}jV+TEfxI7*D@W(d?xNB~56b_4--E;y z0zn~fieG|y6yF|?s=wwRbiKXu>rFMDtoywY>+eu$$Y`vyfY+j2cPsj<>%_{_AH&qL z@T+l0>SaU9L7=&)X>;AN(IxIozo+%U(?k`$W(5j<+uyhF3!MpL7o9G+2wO){S+pYt zlhLOf6<_`Efn{^%NcZ?U7s%H2uf071haU#!rb_Nd@9$fkWr?LK=XXbAid7x6GY&-x zTG!Sx4Ln(p^zIIAZrj#uvOj)tCX`f9b~@57bJfI86EA{z9Q|Gzm5;LAb6j=c7^w{n-_7RHLt ze;=u*hqc*1IVI(JCl7OzL*8~{2{3cq$L+oLKu(_oS7;-G_%sf0n6_j?U>fj+c+`?waan3OXL-|O4w?x9EGgM-L39o z#aVh^bhAq9=I~|ZBy#2sIuupwdwCK$SQXC~4@x^VsN~fDXjv1DWlJRds!Sk?ZEplvA&$d2n}GH~F)+6E3Se;p}EF z4~}}RZ|C>Z6RVZ*A14U5?BMn)2aSSf?H)D%8vp_nc1<%L^IgicU&2b`_(GW{3Pl=w zj~ykR(s>D!p#5{L08eIftr^+NX1R3$=fQ~;?dkmLj^XUstz~72nN1e8viQ=&N^7cm z?Vkm!<0gbMi)==)1~+IvL9RqOMSv{KvpJPikH$gV`URS$*6tv4$BGKyYVePV+?#*V zVmy_DO3M((DKZ?}viz{|fqXh~0IxM)+Nzob%R$s9VI}YwO9_waU2U9EE25UiZKbc8k?XvdgW3kO#%j+-9%ML>UIp0V(o zc)>O6wXJGxQakWDE&Dr%0<5s6TyvgXI3r6qImH>3d0*TQN|(E96e|jgxJ8{KQOVWR z1TrRh;MBhFzKc$=jaHR$K|7NDsI${NAO$ojEzb@V*jYS6f)N&CT* z(fhs&K?*jCumndrmtCwh+Af9=C~^zZSvSinrXc!lfCADFul9tKXmA`}{rBO9u#B}Z z0b_Gt$)FV^Z#2^mP@P<~qG#~!2A3Y&njo^9~bay@Si0C4TVn1F*C4%qcYLcGjj%hsEaGIx`!rThFEBrLu|dTD`s`4 zh)O##jEZ0CiE&Xw%m6!4#U`tCsn)jZ+Jt>rvP(->uk4PX7((QGw#VsTXaNLTkN4j5 z+egjcf;t6`z8r^-4A#uRDi+5TUcmqL5&~m378A8iUMz0yKpLo8OXbo@AT+9@;`emY z)M$H$H`^D=Dm9H!OnaHepjDPgLD+Wxm# z0Kr0;vZ{trl8ynMzO%Jk0ru?j+^T8) z#x?~74fbZ1M~`P_-(P0uKS2#P)j7~z=Ee{T5{@rWRnibUb#9#LfKAFS>1zMQLhLPI zBz>xwF+egbahAeAVUcX&!J;+C&nMvd9jmE93YJr&qhX$i*ZX5VW8HSRXpHH{UX-Q| zue3DF?{TI-t=zU4X2!om0KE<3AV{x7X5$*wRmz}MWNm-DifJx249XU*-kNy{d>kaD zsH+!aWodUle0;oRks7qV4b}Cy;&NO>4~vW2i+w&JgaGt$~3lpwX~8F%SQ7l(B1bcK6lLPtd)Qii^ z))PN&daYl7b<*c?Zj9mkFLRn+hFx2+5}GF_LpVR9J881)pi#(czBqpN3hE_La}nLLn=f)}i==Go!P z=tC5k_48}ZDGZ1`PrcX3|=YO#*3f!9Q z(24C2pjjDAS`v7b7LD&FywiV=P}`CCsO`J#mcuC7#&|uq&KF8_%V+Al;ySB{dtxv` zhd%HQ^>5sM8mlE%pA3#de-E2_H5H&Q8pYlQ^oW9cI8Sg1hd^LI~Z!2yigGA+NSgiQqH(8 z{c%o^Amo0zoNfd3+x|k;DU?a9^zz32gg5&V_%~*dwWaVlxb}Q8Oh39Y3F0f^qEA%J z`?el6(d4u9dDZ@hN+Lj-gmu3CzVgT{!onO{$zpC*)B4|G9Nbne>0+VU<^t~-Qn=8o z$BIVY4whx&2zD=f(aD{ebyo67sVueV z9(D58oY^p*&Yp|4J-4f5GX9rHqP4morkPBkQd20mzd&Jd{!LbNuLi`U7k-f=UF>W# zVm#$Xx|B!f^|SM~&n<~uyW>SAJOvTXyaIKy_)V>}>LW+4mSkf_$JA7J|4{ti5*Al{ znsY5`Ufapk`88W8i&JbJJDS_uAp@E9eMb$c6mPglqA}q&4NJOwR10@40=cw zF$~2>?pyCO1~w$ED~P46Pwvw=aQ;pxh8n^lKlJ1ky~jy0-aoYei-@AYZ^ZTSNx8VT ztp~w>o4x;$f-%(oSSPnTIOy*j{U>?{5Hj!|$D;!s)JpV!Nd8YDz0%qrjM=*k0IrL5 zF*?eBZQnm0ch{Kv`pY7^&-ra)oOtH1PL!*&bQnTws`HbL^_uy2$^SmA`PByn(ZXuRd4xE6Rf|I8=K0FGmFW(vV=Xi!&C= zXSXR)vRUgSNfos4sl}RfQ2vkl{YOPSdr<^B8qtfuns|RY6 zCO&xDVLyg4HzvEb`>7!;UqUzVJm{--%m{Ey~|Ls8~C zxwn@i;kTFa`o=fzs!zI^zqOwP=Z`TjsWLj0$TNp=QtK*@7 zCQZ8W7)2XGCow-Saw<0tG~h%kpB+Tz7@BaKkVJff)>Ojmxp99!OpKq*Eyd;g*g5J)21aww(?)5Fs@cbXo-pxAbf(8Qk zsc@^Kc;${2vN!pBpvFA-{I!`OZfZQ_$Y!TcKYuM_3en6Q0+pSvek1o0aIY759Q39} z-qW^P+Y);XWKz@1eK$$7j2SoawimMDSsDU&px+m=6de-0%@C>k71MzE4oQTm5Mnlz zMs1++IFZLy4Aeg{8?wQ3tHxNq7pim}TnoZ)8Y)8PLPt`%pjpd=0>Y~B_>sBl$(VpQ z+?#$0$U-lCE-NKTY8}%hgacrxeCEgXPk%Gapvav^ZQ2vDQLWxIflcLQdBX9396sb* zZ(CW9?|HEEJ7%P4PI+F&-_~=11V6cTp;b8hj%TV!*LSV19y;3+t*kaA)$}FLAD_A& zmP{D5g?@p}{h$J2g|?|Ay!U1T#eC5NA6HCrvsJSGZdA)(qh@WBNkM{t@PAvD@$3iz zl)J)mg`N#F@Nyfy6|<_8O*{qg-mPe@_x<^Bvjul{RHc4HnYjtb($I$1-JMUYpQ`kK#A-4kA?&WE?_74$97QB`BH(eU-qo2h+xopKeSu zlQ%j#TFi(oQI{cXodVQVCXwS!zeQ?!XWeXljEQGlp&b$|C+i}o4q7dFU%^=pLjJNt zV400(rP75u)E=Un#vLqrXfSZTX{=DMzz?cAkS??LoI;KuS*)99qzKiAvcDH6wvR*W z2a&Az#9h1iTg{u!SDKgx)7LffuZj+5F?L~J6>rlo&LDT5clp$(Ta~e~E8X?JDU(OG z&?m$VL!QNuS>dNCOV!m4DzsC^aE&13^Fayr6oz^i6wCWJNl6Ndy9?sn+YJM3`t>tw zrfR5}b5L}Ro=YG>uLrSPh8e=SNGvf?hxb8c8+%CL%dL-gDgw@t-wCe;YG&L!E>6rp zCLh6HURc$XP5bl3RNL4_Dp9Q=1{z59XdXS?D0+2Vz4oMJ_2f8RvJw-$@iZ$KvN!L$ z39p=>_gr;lvlmeb`{2JO5s2#H&k}Uou55ny+(z$Q&O6;xb;^f?n3C?}~iIf+dZ@ zeX{0Fw)*4b(()R~GJte1)JtaLs6gmtQScS_Vj|(J8r;=Yv{I0O-M~vV!hh1Xx~{dC ze^`^!=;B@qlbaob#Tn(zlg{+&-js1K{2Xh&VX++=`tobB;kNO*=E#m0Ug@DnoD4QM zq^e&pO?67QrRjRI!U?Gi|I>}mm7_~@aeui^gLitz;0IWw;A>~Qr!s{MM{@ee#2HSQ zs#1C<8(haLD>b5t(&7l~<~w`E{dC+@%jAKW4NX)<=)(^VH+=>_4D*Yn0`*ll)J#LL z{0Y)^o60%dSqy5dxpqIUq42T{cJNi(6fFno3|W$6JUIS=*h!Ylglkf6Cik<)Q z{S%gtmY}iCQo_{qVJ3C<)lhh~hZ3=>z>^$!HAc$*U=OIWXP%IZS=49AV5i^%TD%7 z+AM}nG4h@IakR)`Xj}`bzv)>+vD2(_D&m%tpz~xlKJ7eQ8nn-+J>hY;K+q4qt;Xy> zU-78CJokJ?QCE);^hi|FY3A0q>*&)8yt$TnG8k=Zg^rt!-eeA5SDYeM#E|ejzlOsPFm%T@IVHY9w>ymbbO)U$(paB z>X5m$ZG3&b^vGUe9S%Xv0r&c^{`7Cou_QJmEV+*l(0`{#mR-CCzr;Z&e+ClV0#0Yi z?fj8Xmvp1S`y5iR3fl*{!2=I*vqwLX!dh1#{l+&-zD<~N&m%7 zx4)+kYt2xzY-~-2v5_46SpNBQ?As9@6zupC*Bh=pTjm~ znkTw+uYQDw6G+o>Cu%Jl1p`OsA@9vES{crFuN;H8o0Z zsxJnd*ifcDj>WKA`Ng&x!EL1F9QF4B?6MGv$N9pLR=f}ZImb&twEbYTlw@&!wqQe2 zSzTQ>X+!bBL+_ogaPH>rPd-_Cm_5acnX;so@wBCW-z4C0$-e;s$C=5Jk`XFEu>}tt2H075O8?!kIy8s6XWA zDC5(^#5;)~=v&Koq6u*0_35>XG$}Zp?mpBwA#Fv^)xf3qteHo187cDjjovsDN@43` z+kaD+H;{&=e?|Dj;Uz?teFW>UyBNmN&WlpXfg(!q$4O`PavvOOF-mL+nxW!|`ziDA zm8uBEg;3RvjR-4RUTV5n)dychf;zy4KEaP~TX^x+L!=Lpek-j7;$J*$tTYl%^C%vd zejc5$@eg_>h=R(qwqnfPEkA_vC*aNLmo9L@{8``N4CBbs8VQ%u71P8|b?PV!2Q;v; z;e#)EDR?1yS#ip@#U5N<6C#Lo>w4}CU{t8?Dg+x+;07EKC9DU&=}B~4hN>JaOw9zBgo_;mxCZXK_8nGhyoLICfr?84yKf2Oul z%C|ZZTT5IuSjDl%*J-1uWPZGToFynygIc*#9F}F67vd$%CW(8gPnvi2yHf9Igz!IWbQx|Qk)KV7ueI72GDTW08Yx$GSHQ42@ zjqMmcKev`Jln48dav3$VIb?;^CLgkX~p+v@=ZVXMy&) zf>K+=zVN1I^SB^JdteJ5fSApaOIFkXl&}k49&>I@bE{GhD7is^LWL^g3vr3@#xOW5&W*~IX)cyofAVQo3;pB;;*$*wBNv|~ zvia$s!xL4;)}=VXf={_yxjupi)VhU^%}s&zKWw0fGGl$IWmLocQb5ZEN1mO-((H% z?K|7W)Vq<7KcARAdwS%s0Hj7nHT-F)-e+O>67FfOlta`=�PAv38S-9vBkEH*Z6r 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 A$N&HU literal 0 HcmV?d00001 diff --git a/doc/workflow/importing/img/import_projects_from_gitea_new_project_page.png b/doc/workflow/importing/img/import_projects_from_gitea_new_project_page.png new file mode 100644 index 0000000000000000000000000000000000000000..91dcf5a6c022ac65b2bfd169d136479820f232da GIT binary patch literal 52976 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%0DcPz0C-~2oK_F)8u0_$g^Wle2WE5iIUY1;~TcN|t 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 VC3^u=}~W-^EDE_jtD_H@D^kg0VIhkNWpY_tcM; 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 X1n=K;R+)PIr$c!eW$6k@{eb@mFWsbT From f5d456c87e36829f0a17ecfbbd7b205f2e5ad834 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 16 Dec 2016 16:25:53 +0100 Subject: [PATCH 321/386] Fix link to img --- doc/workflow/importing/import_projects_from_gitea.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/importing/import_projects_from_gitea.md b/doc/workflow/importing/import_projects_from_gitea.md index f14f8806d8d..936cee89f45 100644 --- a/doc/workflow/importing/import_projects_from_gitea.md +++ b/doc/workflow/importing/import_projects_from_gitea.md @@ -33,7 +33,7 @@ namespace that started the import process. The importer page is visible when you create a new project. -![New project page on GitLab](img/import_projects_from_gitea_new_project_page.png) +![New project page on GitLab](img/import_projects_from_new_project_page.png) Click on the **Gitea** link and the import authorization process will start. From 5d4531db2555d3051fc47e9268728a670ece95f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Mon, 17 Oct 2016 17:58:57 +0200 Subject: [PATCH 322/386] Gogs Importer --- app/assets/images/gogs-logo.svg | 1 + app/controllers/application_controller.rb | 6 +- app/controllers/import/gogs_controller.rb | 76 +++++++++++++++++++ app/helpers/import_helper.rb | 8 ++ app/services/projects/import_service.rb | 1 + app/views/import/gogs/new.html.haml | 17 +++++ app/views/import/gogs/status.html.haml | 64 ++++++++++++++++ app/views/projects/new.html.haml | 5 ++ changelogs/unreleased/22348-gogs-importer.yml | 4 + config/initializers/1_settings.rb | 2 +- config/routes/import.rb | 6 ++ lib/gitlab/current_settings.rb | 2 +- lib/gitlab/github_import/client.rb | 14 +++- lib/gitlab/github_import/importer.rb | 16 ++-- lib/gitlab/github_import/issue_formatter.rb | 2 +- .../github_import/milestone_formatter.rb | 8 +- lib/gitlab/github_import/project_creator.rb | 9 ++- .../github_import/pull_request_formatter.rb | 2 +- lib/gitlab/gogs_import/importer.rb | 54 +++++++++++++ lib/gitlab/gogs_import/milestone_formatter.rb | 9 +++ lib/gitlab/import_sources.rb | 1 + 21 files changed, 284 insertions(+), 23 deletions(-) create mode 100644 app/assets/images/gogs-logo.svg create mode 100644 app/controllers/import/gogs_controller.rb create mode 100644 app/views/import/gogs/new.html.haml create mode 100644 app/views/import/gogs/status.html.haml create mode 100644 changelogs/unreleased/22348-gogs-importer.yml create mode 100644 lib/gitlab/gogs_import/importer.rb create mode 100644 lib/gitlab/gogs_import/milestone_formatter.rb diff --git a/app/assets/images/gogs-logo.svg b/app/assets/images/gogs-logo.svg new file mode 100644 index 00000000000..60a18263033 --- /dev/null +++ b/app/assets/images/gogs-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4df80195ae1..3ac4975f815 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :can?, :current_application_settings - helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled? + helper_method :import_sources_enabled?, :github_import_enabled?, :gogs_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -245,6 +245,10 @@ class ApplicationController < ActionController::Base current_application_settings.import_sources.include?('github') end + def gogs_import_enabled? + current_application_settings.import_sources.include?('gogs') + end + def github_import_configured? Gitlab::OAuth::Provider.enabled?(:github) end diff --git a/app/controllers/import/gogs_controller.rb b/app/controllers/import/gogs_controller.rb new file mode 100644 index 00000000000..4caf7d2605a --- /dev/null +++ b/app/controllers/import/gogs_controller.rb @@ -0,0 +1,76 @@ +class Import::GogsController < Import::BaseController + before_action :verify_gogs_import_enabled + before_action :gogs_auth, only: [:status, :jobs, :create] + + rescue_from Octokit::Unauthorized, with: :gogs_unauthorized + + helper_method :logged_in_with_gogs? + + def new + if session[:gogs_access_token] + redirect_to status_import_gogs_url + end + end + + def personal_access_token + session[:gogs_access_token] = params[:personal_access_token] + session[:gogs_host_url] = params[:gogs_host_url] + redirect_to status_import_gogs_url + end + + def status + @repos = client.repos + @already_added_projects = current_user.created_projects.where(import_type: "gogs") + already_added_projects_names = @already_added_projects.pluck(:import_source) + + @gogs_root_url = session[:gogs_host_url] + + @repos.reject!{ |repo| already_added_projects_names.include? repo.full_name } + end + + def jobs + jobs = current_user.created_projects.where(import_type: "gogs").to_json(only: [:id, :import_status]) + render json: jobs + end + + def create + @repo_id = params[:repo_id].to_i + repo = client.repo(@repo_id) + @project_name = params[:new_name].presence || repo.name + namespace_path = params[:target_namespace].presence || current_user.namespace_path + @target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path) + + if current_user.can?(:create_projects, @target_namespace) + @project = Gitlab::GithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params, type: 'gogs').execute + else + render 'unauthorized' + end + end + + private + + def client + @client ||= Gitlab::GithubImport::Client.new(session[:gogs_access_token], host: session[:gogs_host_url], api_version: 'v1') + end + + def verify_gogs_import_enabled + render_404 unless gogs_import_enabled? + end + + def gogs_auth + if session[:gogs_access_token].blank? || session[:gogs_host_url].blank? + redirect_to new_import_gogs_url, + alert: 'You need to specify both an Access Token and a Host URL.' + end + end + + def gogs_unauthorized + session[:gogs_access_token] = nil + redirect_to new_import_gogs_url, + alert: 'Access denied to your Gogs account.' + end + + def access_params + { github_access_token: session[:gogs_access_token] } + end +end diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index 021d2b14718..29df2703d52 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -8,6 +8,10 @@ module ImportHelper link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank' end + def gogs_project_link(path_with_namespace) + link_to path_with_namespace, gogs_project_url(path_with_namespace), target: '_blank' + end + private def github_project_url(path_with_namespace) @@ -20,4 +24,8 @@ module ImportHelper provider = Gitlab.config.omniauth.providers.find { |p| p.name == 'github' } @github_url = provider.fetch('url', 'https://github.com') if provider end + + def gogs_project_url(path_with_namespace) + "#{@gogs_root_url}/#{path_with_namespace}" + end end diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index d7221fe993c..8cac0b01881 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -5,6 +5,7 @@ module Projects class Error < StandardError; end ALLOWED_TYPES = [ + 'gogs', 'bitbucket', 'fogbugz', 'gitlab', diff --git a/app/views/import/gogs/new.html.haml b/app/views/import/gogs/new.html.haml new file mode 100644 index 00000000000..e1ae1be283c --- /dev/null +++ b/app/views/import/gogs/new.html.haml @@ -0,0 +1,17 @@ +- page_title "Gogs Import" +- header_title "Projects", root_path + +%h3.page-title + = image_tag(image_path('gogs-logo.svg'), alt: 'Gogs', size: "16x16") + Gogs + +%p + To import a Gogs project, you can use a + = succeed '.' do + = link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token' + += form_tag personal_access_token_import_gogs_path, method: :post, class: 'form-inline' do + .form-group + = text_field_tag :personal_access_token, '', class: 'form-control', placeholder: "Personal Access Token", size: 40 + = text_field_tag :gogs_host_url, '', class: 'form-control', placeholder: "Gogs Host URL", size: 128 + = submit_tag 'List Your Gogs Repositories', class: 'btn btn-success' diff --git a/app/views/import/gogs/status.html.haml b/app/views/import/gogs/status.html.haml new file mode 100644 index 00000000000..86ccc79efc8 --- /dev/null +++ b/app/views/import/gogs/status.html.haml @@ -0,0 +1,64 @@ +- page_title "Gogs import" +- header_title "Projects", root_path +%h3.page-title + %i.fa.fa-github + Import projects from Gogs + +%p.light + Select projects you want to import. +%hr +%p + = button_tag class: "btn btn-import btn-success js-import-all" do + Import all projects + = icon("spinner spin", class: "loading-icon") + +.table-responsive + %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col + %thead + %tr + %th From Gogs + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = gogs_project_link(project.import_source) + %td + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name + + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = gogs_project_link(repo.full_name) + %td.import-target + %fieldset.row + .input-group + .project-path.input-group-btn + - if current_user.can_select_namespace? + - selected = params[:namespace_id] || :current_user + - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {} + = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } + - else + = text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true + %span.input-group-addon / + = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true + %td.import-actions.job-status + = button_tag class: "btn btn-import js-add-to-import" do + Import + = icon("spinner spin", class: "loading-icon") + +.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gogs_path}", import_path: "#{import_gogs_path}" } } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 0788924d44a..5edb9b69ed2 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -48,6 +48,11 @@ - if github_import_enabled? = link_to new_import_github_path, class: 'btn import_github' do = icon('github', text: 'GitHub') + %div + - if gogs_import_enabled? + = link_to new_import_gogs_url, class: 'btn import_gogs' do + = image_tag(image_path('gogs-logo.svg'), alt: 'Gogs', size: "14x14") + Gogs %div - if bitbucket_import_enabled? = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}", "data-no-turbolink" => "true" do diff --git a/changelogs/unreleased/22348-gogs-importer.yml b/changelogs/unreleased/22348-gogs-importer.yml new file mode 100644 index 00000000000..9543d2a4d26 --- /dev/null +++ b/changelogs/unreleased/22348-gogs-importer.yml @@ -0,0 +1,4 @@ +--- +title: Gogs importer +merge_request: 6945 +author: Kim Carlbäcker diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index ddea325c6ca..c0dbf62bfa2 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -213,7 +213,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil? Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab['domain_whitelist'] ||= [] -Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project] +Settings.gitlab['import_sources'] ||= %w[gogs github bitbucket gitlab google_code fogbugz git gitlab_project] Settings.gitlab['trusted_proxies'] ||= [] Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml')) diff --git a/config/routes/import.rb b/config/routes/import.rb index 89f3b3f6378..a0427dade99 100644 --- a/config/routes/import.rb +++ b/config/routes/import.rb @@ -6,6 +6,12 @@ namespace :import do get :jobs end + resource :gogs, only: [:create, :new], controller: :gogs do + post :personal_access_token + get :status + get :jobs + end + resource :gitlab, only: [:create], controller: :gitlab do get :status get :callback diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index c6bb8f9c8ed..eb3d9f29451 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -45,7 +45,7 @@ module Gitlab default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], domain_whitelist: Settings.gitlab['domain_whitelist'], - import_sources: %w[github bitbucket gitlab google_code fogbugz git gitlab_project], + import_sources: %w[gogs github bitbucket gitlab google_code fogbugz git gitlab_project], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], max_artifacts_size: Settings.artifacts['max_size'], require_two_factor_authentication: false, diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index 85df6547a67..a96e0bc63dd 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -4,24 +4,30 @@ module Gitlab GITHUB_SAFE_REMAINING_REQUESTS = 100 GITHUB_SAFE_SLEEP_TIME = 500 - attr_reader :access_token + attr_reader :access_token, :host, :api_version - def initialize(access_token) + def initialize(access_token, host: nil, api_version: 'v3') @access_token = access_token + @host = host + @api_version = api_version if access_token ::Octokit.auto_paginate = false end end + def api_endpoint + host.present? && api_version.present? ? "#{host}/api/#{api_version}" : github_options[:site] + end + def api @api ||= ::Octokit::Client.new( access_token: access_token, - api_endpoint: github_options[:site], + api_endpoint: api_endpoint, # If there is no config, we're connecting to github.com and we # should verify ssl. connection_options: { - ssl: { verify: config ? config['verify_ssl'] : true } + ssl: { verify: config ? config['verify_ssl'] : false } } ) end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 281b65bdeba..c32e78cae03 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -60,7 +60,7 @@ module Gitlab fetch_resources(:labels, repo, per_page: 100) do |labels| labels.each do |raw| begin - LabelFormatter.new(project, raw).create! + GithubImport::LabelFormatter.new(project, raw).create! rescue => e errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } end @@ -74,7 +74,7 @@ module Gitlab fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones| milestones.each do |raw| begin - MilestoneFormatter.new(project, raw).create! + GithubImport::MilestoneFormatter.new(project, raw).create! rescue => e errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } end @@ -85,7 +85,7 @@ module Gitlab def import_issues fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues| issues.each do |raw| - gh_issue = IssueFormatter.new(project, raw) + gh_issue = GithubImport::IssueFormatter.new(project, raw) begin issuable = @@ -106,7 +106,7 @@ module Gitlab def import_pull_requests fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests| pull_requests.each do |raw| - pull_request = PullRequestFormatter.new(project, raw) + pull_request = GithubImport::PullRequestFormatter.new(project, raw) next unless pull_request.valid? begin @@ -179,7 +179,7 @@ module Gitlab ActiveRecord::Base.no_touching do comments.each do |raw| begin - comment = CommentFormatter.new(project, raw) + comment = GithubImport::CommentFormatter.new(project, raw) # GH does not return info about comment's parent, so we guess it by checking its URL! *_, parent, iid = URI(raw.html_url).path.split('/') issuable_class = parent == 'issues' ? Issue : MergeRequest @@ -198,7 +198,7 @@ module Gitlab last_note_attrs = nil cut_off_index = comments.find_index do |raw| - comment = CommentFormatter.new(project, raw) + comment = GithubImport::CommentFormatter.new(project, raw) comment_attrs = comment.attributes last_note_attrs ||= last_note.slice(*comment_attrs.keys) @@ -214,7 +214,7 @@ module Gitlab def import_wiki unless project.wiki.repository_exists? - wiki = WikiFormatter.new(project) + wiki = GithubImport::WikiFormatter.new(project) gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) end rescue Gitlab::Shell::Error => e @@ -230,7 +230,7 @@ module Gitlab fetch_resources(:releases, repo, per_page: 100) do |releases| releases.each do |raw| begin - gh_release = ReleaseFormatter.new(project, raw) + gh_release = GithubImport::ReleaseFormatter.new(project, raw) gh_release.create! if gh_release.valid? rescue => e errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb index 887690bcc7c..21a3dee203b 100644 --- a/lib/gitlab/github_import/issue_formatter.rb +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -70,7 +70,7 @@ module Gitlab def milestone if raw_data.milestone.present? - project.milestones.find_by(iid: raw_data.milestone.number) + project.milestones.find_by(iid: raw_data.milestone.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr)) end end diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb index 401dd962521..678d56b830b 100644 --- a/lib/gitlab/github_import/milestone_formatter.rb +++ b/lib/gitlab/github_import/milestone_formatter.rb @@ -3,7 +3,7 @@ module Gitlab class MilestoneFormatter < BaseFormatter def attributes { - iid: raw_data.number, + iid: raw_data.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr), project: project, title: raw_data.title, description: raw_data.description, @@ -19,7 +19,11 @@ module Gitlab end def find_condition - { iid: raw_data.number } + { iid: raw_data.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr) } + end + + def self.iid_attr + :number end private diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index a2410068845..3f635be22ba 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -1,14 +1,15 @@ module Gitlab module GithubImport class ProjectCreator - attr_reader :repo, :name, :namespace, :current_user, :session_data + attr_reader :repo, :name, :namespace, :current_user, :session_data, :type - def initialize(repo, name, namespace, current_user, session_data) + def initialize(repo, name, namespace, current_user, session_data, type: 'github') @repo = repo @name = name @namespace = namespace @current_user = current_user @session_data = session_data + @type = type end def execute @@ -19,7 +20,7 @@ module Gitlab description: repo.description, namespace_id: namespace.id, visibility_level: visibility_level, - import_type: "github", + import_type: type, import_source: repo.full_name, import_url: import_url, skip_wiki: skip_wiki @@ -29,7 +30,7 @@ module Gitlab private def import_url - repo.clone_url.sub('https://', "https://#{session_data[:github_access_token]}@") + repo.clone_url.sub('://', "://#{session_data[:github_access_token]}@") end def visibility_level diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index b9a227fb11a..ea8768fded7 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -98,7 +98,7 @@ module Gitlab def milestone if raw_data.milestone.present? - project.milestones.find_by(iid: raw_data.milestone.number) + project.milestones.find_by(iid: raw_data.milestone.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr)) end end diff --git a/lib/gitlab/gogs_import/importer.rb b/lib/gitlab/gogs_import/importer.rb new file mode 100644 index 00000000000..604e31d35a3 --- /dev/null +++ b/lib/gitlab/gogs_import/importer.rb @@ -0,0 +1,54 @@ +require 'uri' + +module Gitlab + module GogsImport + class Importer < Gitlab::GithubImport::Importer + include Gitlab::ShellAdapter + + attr_reader :client, :errors, :project, :repo, :repo_url + + def initialize(project) + @project = project + @repo = project.import_source + @repo_url = project.import_url + @errors = [] + @labels = {} + + if credentials + uri = URI.parse(project.import_url) + host = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}".sub(/[\w-]+\/[\w-]+\.git\z/, '') + @client = GithubImport::Client.new(credentials[:user], host: host, api_version: 'v1') + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end + end + + def execute + import_labels + import_milestones + import_pull_requests + import_issues + import_comments(:issues) + import_comments(:pull_requests) + import_wiki + # NOTE: this is commented out since Gogs doesn't have release-API yet + # import_releases + handle_errors + + true + end + + def import_milestones + fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones| + milestones.each do |raw| + begin + GogsImport::MilestoneFormatter.new(project, raw).create! + rescue => e + errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + end + end + end + end + end + end +end diff --git a/lib/gitlab/gogs_import/milestone_formatter.rb b/lib/gitlab/gogs_import/milestone_formatter.rb new file mode 100644 index 00000000000..990e792929a --- /dev/null +++ b/lib/gitlab/gogs_import/milestone_formatter.rb @@ -0,0 +1,9 @@ +module Gitlab + module GogsImport + class MilestoneFormatter < GithubImport::MilestoneFormatter + def self.iid_attr + :id + end + end + end +end diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 94261b7eeed..9564c4cc134 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -14,6 +14,7 @@ module Gitlab def options { + 'Gogs' => 'gogs', 'GitHub' => 'github', 'Bitbucket' => 'bitbucket', 'GitLab.com' => 'gitlab', From 103114e3d73819f76bed9d8ad1bbdb8964875579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 15 Dec 2016 17:31:14 +0100 Subject: [PATCH 323/386] Rename Gogs to Gitea, DRY the controller and improve views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/assets/images/gogs-logo.svg | 1 - app/controllers/application_controller.rb | 6 +- app/controllers/import/gitea_controller.rb | 41 ++++ app/controllers/import/github_controller.rb | 91 ++++--- app/controllers/import/gogs_controller.rb | 76 ------ app/helpers/import_helper.rb | 8 +- app/services/projects/import_service.rb | 29 ++- app/views/import/gitea/new.html.haml | 23 ++ .../import/{gogs => gitea}/status.html.haml | 14 +- app/views/import/gogs/new.html.haml | 17 -- app/views/projects/new.html.haml | 10 +- app/views/shared/icons/_go_logo.svg.erb | 1 + .../unreleased/22348-gitea-importer.yml | 4 + changelogs/unreleased/22348-gogs-importer.yml | 4 - config/initializers/1_settings.rb | 2 +- config/routes/import.rb | 2 +- lib/gitlab/current_settings.rb | 2 +- lib/gitlab/gogs_import/importer.rb | 54 ----- lib/gitlab/gogs_import/milestone_formatter.rb | 9 - lib/gitlab/import_sources.rb | 4 +- .../import/gitea_controller_spec.rb | 43 ++++ .../import/github_controller_spec.rb | 218 +---------------- spec/routing/import_routing_spec.rb | 166 +++++++++++++ ...hubish_import_controller_shared_context.rb | 10 + ...ubish_import_controller_shared_examples.rb | 228 ++++++++++++++++++ spec/support/import_spec_helper.rb | 4 + 26 files changed, 633 insertions(+), 434 deletions(-) delete mode 100644 app/assets/images/gogs-logo.svg create mode 100644 app/controllers/import/gitea_controller.rb delete mode 100644 app/controllers/import/gogs_controller.rb create mode 100644 app/views/import/gitea/new.html.haml rename app/views/import/{gogs => gitea}/status.html.haml (86%) delete mode 100644 app/views/import/gogs/new.html.haml create mode 100644 app/views/shared/icons/_go_logo.svg.erb create mode 100644 changelogs/unreleased/22348-gitea-importer.yml delete mode 100644 changelogs/unreleased/22348-gogs-importer.yml delete mode 100644 lib/gitlab/gogs_import/importer.rb delete mode 100644 lib/gitlab/gogs_import/milestone_formatter.rb create mode 100644 spec/controllers/import/gitea_controller_spec.rb create mode 100644 spec/routing/import_routing_spec.rb create mode 100644 spec/support/githubish_import_controller_shared_context.rb create mode 100644 spec/support/githubish_import_controller_shared_examples.rb diff --git a/app/assets/images/gogs-logo.svg b/app/assets/images/gogs-logo.svg deleted file mode 100644 index 60a18263033..00000000000 --- a/app/assets/images/gogs-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3ac4975f815..bb47e2a8bf7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :can?, :current_application_settings - helper_method :import_sources_enabled?, :github_import_enabled?, :gogs_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled? + helper_method :import_sources_enabled?, :github_import_enabled?, :gitea_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -245,8 +245,8 @@ class ApplicationController < ActionController::Base current_application_settings.import_sources.include?('github') end - def gogs_import_enabled? - current_application_settings.import_sources.include?('gogs') + def gitea_import_enabled? + current_application_settings.import_sources.include?('gitea') end def github_import_configured? diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb new file mode 100644 index 00000000000..c82a20be04c --- /dev/null +++ b/app/controllers/import/gitea_controller.rb @@ -0,0 +1,41 @@ +class Import::GiteaController < Import::GithubController + def new + if session[:access_token].present? && session[:host_url].present? + redirect_to status_import_url + end + end + + def personal_access_token + session[:host_url] = params[:gitea_host_url] + super + end + + def status + @gitea_root_url = session[:host_url] + super + end + + private + + # Overriden methods + def provider + :gitea + end + + # Gitea is not yet an OAuth provider + # See https://github.com/go-gitea/gitea/issues/27 + def logged_in_with_provider? + false + end + + def provider_auth + if session[:access_token].blank? || session[:host_url].blank? + redirect_to new_import_gitea_url, + alert: 'You need to specify both an Access Token and a Host URL.' + end + end + + def client_options + { host: session[:host_url], api_version: 'v1' } + end +end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index ee7d498c59c..343ca51e510 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -1,39 +1,37 @@ class Import::GithubController < Import::BaseController - before_action :verify_github_import_enabled - before_action :github_auth, only: [:status, :jobs, :create] + before_action :verify_import_enabled + before_action :provider_auth, only: [:status, :jobs, :create] - rescue_from Octokit::Unauthorized, with: :github_unauthorized - - helper_method :logged_in_with_github? + rescue_from Octokit::Unauthorized, with: :provider_unauthorized def new - if logged_in_with_github? - go_to_github_for_permissions - elsif session[:github_access_token] - redirect_to status_import_github_url + if logged_in_with_provider? + go_to_provider_for_permissions + elsif session[:access_token] + redirect_to status_import_url end end def callback - session[:github_access_token] = client.get_token(params[:code]) - redirect_to status_import_github_url + session[:access_token] = client.get_token(params[:code]) + redirect_to status_import_url end def personal_access_token - session[:github_access_token] = params[:personal_access_token] - redirect_to status_import_github_url + session[:access_token] = params[:personal_access_token] + redirect_to status_import_url end def status @repos = client.repos - @already_added_projects = current_user.created_projects.where(import_type: "github") + @already_added_projects = current_user.created_projects.where(import_type: provider) already_added_projects_names = @already_added_projects.pluck(:import_source) - @repos.reject!{ |repo| already_added_projects_names.include? repo.full_name } + @repos.reject! { |repo| already_added_projects_names.include? repo.full_name } end def jobs - jobs = current_user.created_projects.where(import_type: "github").to_json(only: [:id, :import_status]) + jobs = current_user.created_projects.where(import_type: provider).to_json(only: [:id, :import_status]) render json: jobs end @@ -44,8 +42,8 @@ class Import::GithubController < Import::BaseController namespace_path = params[:target_namespace].presence || current_user.namespace_path @target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path) - if current_user.can?(:create_projects, @target_namespace) - @project = Gitlab::GithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params).execute + if can?(current_user, :create_projects, @target_namespace) + @project = Gitlab::GithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params, type: provider).execute else render 'unauthorized' end @@ -54,34 +52,59 @@ class Import::GithubController < Import::BaseController private def client - @client ||= Gitlab::GithubImport::Client.new(session[:github_access_token]) + @client ||= Gitlab::GithubImport::Client.new(session[:access_token], client_options) end - def verify_github_import_enabled - render_404 unless github_import_enabled? + def verify_import_enabled + render_404 unless import_enabled? end - def github_auth - if session[:github_access_token].blank? - go_to_github_for_permissions - end + def go_to_provider_for_permissions + redirect_to client.authorize_url(callback_import_url) end - def go_to_github_for_permissions - redirect_to client.authorize_url(callback_import_github_url) + def import_enabled? + __send__("#{provider}_import_enabled?") end - def github_unauthorized - session[:github_access_token] = nil - redirect_to new_import_github_url, - alert: 'Access denied to your GitHub account.' + def new_import_url + public_send("new_import_#{provider}_url") end - def logged_in_with_github? - current_user.identities.exists?(provider: 'github') + def status_import_url + public_send("status_import_#{provider}_url") + end + + def callback_import_url + public_send("callback_import_#{provider}_url") + end + + def provider_unauthorized + session[:access_token] = nil + redirect_to new_import_url, + alert: "Access denied to your #{Gitlab::ImportSources.options.key(provider.to_s)} account." end def access_params - { github_access_token: session[:github_access_token] } + { github_access_token: session[:access_token] } + end + + # The following methods are overriden in subclasses + def provider + :github + end + + def logged_in_with_provider? + current_user.identities.exists?(provider: provider) + end + + def provider_auth + if session[:access_token].blank? + go_to_provider_for_permissions + end + end + + def client_options + {} end end diff --git a/app/controllers/import/gogs_controller.rb b/app/controllers/import/gogs_controller.rb deleted file mode 100644 index 4caf7d2605a..00000000000 --- a/app/controllers/import/gogs_controller.rb +++ /dev/null @@ -1,76 +0,0 @@ -class Import::GogsController < Import::BaseController - before_action :verify_gogs_import_enabled - before_action :gogs_auth, only: [:status, :jobs, :create] - - rescue_from Octokit::Unauthorized, with: :gogs_unauthorized - - helper_method :logged_in_with_gogs? - - def new - if session[:gogs_access_token] - redirect_to status_import_gogs_url - end - end - - def personal_access_token - session[:gogs_access_token] = params[:personal_access_token] - session[:gogs_host_url] = params[:gogs_host_url] - redirect_to status_import_gogs_url - end - - def status - @repos = client.repos - @already_added_projects = current_user.created_projects.where(import_type: "gogs") - already_added_projects_names = @already_added_projects.pluck(:import_source) - - @gogs_root_url = session[:gogs_host_url] - - @repos.reject!{ |repo| already_added_projects_names.include? repo.full_name } - end - - def jobs - jobs = current_user.created_projects.where(import_type: "gogs").to_json(only: [:id, :import_status]) - render json: jobs - end - - def create - @repo_id = params[:repo_id].to_i - repo = client.repo(@repo_id) - @project_name = params[:new_name].presence || repo.name - namespace_path = params[:target_namespace].presence || current_user.namespace_path - @target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path) - - if current_user.can?(:create_projects, @target_namespace) - @project = Gitlab::GithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params, type: 'gogs').execute - else - render 'unauthorized' - end - end - - private - - def client - @client ||= Gitlab::GithubImport::Client.new(session[:gogs_access_token], host: session[:gogs_host_url], api_version: 'v1') - end - - def verify_gogs_import_enabled - render_404 unless gogs_import_enabled? - end - - def gogs_auth - if session[:gogs_access_token].blank? || session[:gogs_host_url].blank? - redirect_to new_import_gogs_url, - alert: 'You need to specify both an Access Token and a Host URL.' - end - end - - def gogs_unauthorized - session[:gogs_access_token] = nil - redirect_to new_import_gogs_url, - alert: 'Access denied to your Gogs account.' - end - - def access_params - { github_access_token: session[:gogs_access_token] } - end -end diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index 29df2703d52..fb79e2a4eef 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -8,8 +8,8 @@ module ImportHelper link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank' end - def gogs_project_link(path_with_namespace) - link_to path_with_namespace, gogs_project_url(path_with_namespace), target: '_blank' + def gitea_project_link(root_url, path_with_namespace) + link_to path_with_namespace, gitea_project_url(root_url, path_with_namespace), target: '_blank' end private @@ -25,7 +25,7 @@ module ImportHelper @github_url = provider.fetch('url', 'https://github.com') if provider end - def gogs_project_url(path_with_namespace) - "#{@gogs_root_url}/#{path_with_namespace}" + def gitea_project_url(root_url, path_with_namespace) + "#{root_url}/#{path_with_namespace}" end end diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index 8cac0b01881..287c0a4257f 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -5,13 +5,13 @@ module Projects class Error < StandardError; end ALLOWED_TYPES = [ - 'gogs', - 'bitbucket', - 'fogbugz', - 'gitlab', 'github', + 'bitbucket', + 'gitlab', 'google_code', - 'gitlab_project' + 'fogbugz', + 'gitlab_project', + 'gitea' ] def execute @@ -71,8 +71,23 @@ module Projects def importer return Gitlab::ImportExport::Importer.new(project) if @project.gitlab_project_import? - class_name = "Gitlab::#{project.import_type.camelize}Import::Importer" - class_name.constantize.new(project) + class_name = + case project.import_type + when 'github', 'gitea' + Gitlab::GithubImport::Importer + when 'bitbucket' + Gitlab::BitbucketImport::Importer + when 'gitlab' + Gitlab::GitlabImport::Importer + when 'google_code' + Gitlab::GoogleCodeImport::Importer + when 'fogbugz' + Gitlab::FogbugzImport::Importer + else + raise 'Unknown importer type!' + end + + class_name.new(project) end def unknown_url? diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml new file mode 100644 index 00000000000..02a116f996b --- /dev/null +++ b/app/views/import/gitea/new.html.haml @@ -0,0 +1,23 @@ +- page_title "Gitea Import" +- header_title "Projects", root_path + +%h3.page-title + = custom_icon('go_logo') + Import Projects from Gitea + +%p + To get started, please enter your Gitea Host URL and a + = succeed '.' do + = link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token' + += form_tag personal_access_token_import_gitea_path, class: 'form-horizontal' do + .form-group + = label_tag :gitea_host_url, 'Gitea Host URL', class: 'control-label' + .col-sm-4 + = text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control' + .form-group + = label_tag :personal_access_token, 'Personal Access Token', class: 'control-label' + .col-sm-4 + = text_field_tag :personal_access_token, nil, class: 'form-control' + .form-actions + = submit_tag 'List Your Gitea Repositories', class: 'btn btn-create' diff --git a/app/views/import/gogs/status.html.haml b/app/views/import/gitea/status.html.haml similarity index 86% rename from app/views/import/gogs/status.html.haml rename to app/views/import/gitea/status.html.haml index 86ccc79efc8..2b25892c0da 100644 --- a/app/views/import/gogs/status.html.haml +++ b/app/views/import/gitea/status.html.haml @@ -1,8 +1,8 @@ -- page_title "Gogs import" +- page_title "Gitea import" - header_title "Projects", root_path %h3.page-title - %i.fa.fa-github - Import projects from Gogs + = custom_icon('go_logo') + Import projects from Gitea %p.light Select projects you want to import. @@ -19,14 +19,14 @@ %colgroup.import-jobs-status-col %thead %tr - %th From Gogs + %th From Gitea %th To GitLab %th Status %tbody - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} %td - = gogs_project_link(project.import_source) + = gitea_project_link(@gitea_root_url, project.import_source) %td = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status @@ -43,7 +43,7 @@ - @repos.each do |repo| %tr{id: "repo_#{repo.id}"} %td - = gogs_project_link(repo.full_name) + = gitea_project_link(@gitea_root_url, repo.full_name) %td.import-target %fieldset.row .input-group @@ -61,4 +61,4 @@ Import = icon("spinner spin", class: "loading-icon") -.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gogs_path}", import_path: "#{import_gogs_path}" } } +.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitea_path}", import_path: "#{import_gitea_path}" } } diff --git a/app/views/import/gogs/new.html.haml b/app/views/import/gogs/new.html.haml deleted file mode 100644 index e1ae1be283c..00000000000 --- a/app/views/import/gogs/new.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -- page_title "Gogs Import" -- header_title "Projects", root_path - -%h3.page-title - = image_tag(image_path('gogs-logo.svg'), alt: 'Gogs', size: "16x16") - Gogs - -%p - To import a Gogs project, you can use a - = succeed '.' do - = link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token' - -= form_tag personal_access_token_import_gogs_path, method: :post, class: 'form-inline' do - .form-group - = text_field_tag :personal_access_token, '', class: 'form-control', placeholder: "Personal Access Token", size: 40 - = text_field_tag :gogs_host_url, '', class: 'form-control', placeholder: "Gogs Host URL", size: 128 - = submit_tag 'List Your Gogs Repositories', class: 'btn btn-success' diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 5edb9b69ed2..866b278ce57 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -48,11 +48,6 @@ - if github_import_enabled? = link_to new_import_github_path, class: 'btn import_github' do = icon('github', text: 'GitHub') - %div - - if gogs_import_enabled? - = link_to new_import_gogs_url, class: 'btn import_gogs' do - = image_tag(image_path('gogs-logo.svg'), alt: 'Gogs', size: "14x14") - Gogs %div - if bitbucket_import_enabled? = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}", "data-no-turbolink" => "true" do @@ -73,6 +68,11 @@ - if fogbugz_import_enabled? = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do = icon('bug', text: 'Fogbugz') + %div + - if gitea_import_enabled? + = link_to new_import_gitea_url, class: 'btn import_gitea' do + = custom_icon('go_logo') + Gitea %div - if git_import_enabled? = link_to "#", class: 'btn js-toggle-button import_git' do diff --git a/app/views/shared/icons/_go_logo.svg.erb b/app/views/shared/icons/_go_logo.svg.erb new file mode 100644 index 00000000000..5052651c110 --- /dev/null +++ b/app/views/shared/icons/_go_logo.svg.erb @@ -0,0 +1 @@ + diff --git a/changelogs/unreleased/22348-gitea-importer.yml b/changelogs/unreleased/22348-gitea-importer.yml new file mode 100644 index 00000000000..ce81a3cfefb --- /dev/null +++ b/changelogs/unreleased/22348-gitea-importer.yml @@ -0,0 +1,4 @@ +--- +title: New Gitea importer +merge_request: 6945 +author: diff --git a/changelogs/unreleased/22348-gogs-importer.yml b/changelogs/unreleased/22348-gogs-importer.yml deleted file mode 100644 index 9543d2a4d26..00000000000 --- a/changelogs/unreleased/22348-gogs-importer.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Gogs importer -merge_request: 6945 -author: Kim Carlbäcker diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index c0dbf62bfa2..ee97b4e42b9 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -213,7 +213,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil? Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab['domain_whitelist'] ||= [] -Settings.gitlab['import_sources'] ||= %w[gogs github bitbucket gitlab google_code fogbugz git gitlab_project] +Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project gitea] Settings.gitlab['trusted_proxies'] ||= [] Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml')) diff --git a/config/routes/import.rb b/config/routes/import.rb index a0427dade99..c378253bf15 100644 --- a/config/routes/import.rb +++ b/config/routes/import.rb @@ -6,7 +6,7 @@ namespace :import do get :jobs end - resource :gogs, only: [:create, :new], controller: :gogs do + resource :gitea, only: [:create, :new], controller: :gitea do post :personal_access_token get :status get :jobs diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index eb3d9f29451..9d142f1b82e 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -45,7 +45,7 @@ module Gitlab default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], domain_whitelist: Settings.gitlab['domain_whitelist'], - import_sources: %w[gogs github bitbucket gitlab google_code fogbugz git gitlab_project], + import_sources: %w[gitea github bitbucket gitlab google_code fogbugz git gitlab_project], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], max_artifacts_size: Settings.artifacts['max_size'], require_two_factor_authentication: false, diff --git a/lib/gitlab/gogs_import/importer.rb b/lib/gitlab/gogs_import/importer.rb deleted file mode 100644 index 604e31d35a3..00000000000 --- a/lib/gitlab/gogs_import/importer.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'uri' - -module Gitlab - module GogsImport - class Importer < Gitlab::GithubImport::Importer - include Gitlab::ShellAdapter - - attr_reader :client, :errors, :project, :repo, :repo_url - - def initialize(project) - @project = project - @repo = project.import_source - @repo_url = project.import_url - @errors = [] - @labels = {} - - if credentials - uri = URI.parse(project.import_url) - host = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}".sub(/[\w-]+\/[\w-]+\.git\z/, '') - @client = GithubImport::Client.new(credentials[:user], host: host, api_version: 'v1') - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" - end - end - - def execute - import_labels - import_milestones - import_pull_requests - import_issues - import_comments(:issues) - import_comments(:pull_requests) - import_wiki - # NOTE: this is commented out since Gogs doesn't have release-API yet - # import_releases - handle_errors - - true - end - - def import_milestones - fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones| - milestones.each do |raw| - begin - GogsImport::MilestoneFormatter.new(project, raw).create! - rescue => e - errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } - end - end - end - end - end - end -end diff --git a/lib/gitlab/gogs_import/milestone_formatter.rb b/lib/gitlab/gogs_import/milestone_formatter.rb deleted file mode 100644 index 990e792929a..00000000000 --- a/lib/gitlab/gogs_import/milestone_formatter.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module GogsImport - class MilestoneFormatter < GithubImport::MilestoneFormatter - def self.iid_attr - :id - end - end - end -end diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 9564c4cc134..34587582bd1 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -14,14 +14,14 @@ module Gitlab def options { - 'Gogs' => 'gogs', 'GitHub' => 'github', 'Bitbucket' => 'bitbucket', 'GitLab.com' => 'gitlab', 'Google Code' => 'google_code', 'FogBugz' => 'fogbugz', 'Repo by URL' => 'git', - 'GitLab export' => 'gitlab_project' + 'GitLab export' => 'gitlab_project', + 'Gitea' => 'gitea' } end end diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb new file mode 100644 index 00000000000..3064d1dd58a --- /dev/null +++ b/spec/controllers/import/gitea_controller_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Import::GiteaController do + include ImportSpecHelper + + let(:provider) { :gitea } + let(:host_url) { 'https://try.gitea.io' } + + include_context 'a GitHub-ish import controller' + + def assign_host_url + session[:host_url] = host_url + end + + describe "GET new" do + it_behaves_like 'a GitHub-ish import controller: GET new' do + before do + assign_host_url + end + end + end + + describe "POST personal_access_token" do + it_behaves_like 'a GitHub-ish import controller: POST personal_access_token' + end + + describe "GET status" do + it_behaves_like 'a GitHub-ish import controller: GET status' do + before do + assign_host_url + end + let(:extra_assign_expectations) { { gitea_root_url: host_url } } + end + end + + describe 'POST create' do + it_behaves_like 'a GitHub-ish import controller: POST create' do + before do + assign_host_url + end + end + end +end diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 4f96567192d..55820a7cc65 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -3,35 +3,19 @@ require 'spec_helper' describe Import::GithubController do include ImportSpecHelper - let(:user) { create(:user) } - let(:token) { "asdasd12345" } - let(:access_params) { { github_access_token: token } } + let(:provider) { :github } - def assign_session_token - session[:github_access_token] = token - end - - before do - sign_in(user) - allow(controller).to receive(:github_import_enabled?).and_return(true) - end + include_context 'a GitHub-ish import controller' describe "GET new" do + it_behaves_like 'a GitHub-ish import controller: GET new' + it "redirects to GitHub for an access token if logged in with GitHub" do - allow(controller).to receive(:logged_in_with_github?).and_return(true) - expect(controller).to receive(:go_to_github_for_permissions) + allow(controller).to receive(:logged_in_with_provider?).and_return(true) + expect(controller).to receive(:go_to_provider_for_permissions) get :new end - - it "redirects to status if we already have a token" do - assign_session_token - allow(controller).to receive(:logged_in_with_github?).and_return(false) - - get :new - - expect(controller).to redirect_to(status_import_github_url) - end end describe "GET callback" do @@ -45,202 +29,20 @@ describe Import::GithubController do get :callback - expect(session[:github_access_token]).to eq(token) + expect(session[:access_token]).to eq(token) expect(controller).to redirect_to(status_import_github_url) end end describe "POST personal_access_token" do - it "updates access token" do - token = "asdfasdf9876" - - allow_any_instance_of(Gitlab::GithubImport::Client). - to receive(:user).and_return(true) - - post :personal_access_token, personal_access_token: token - - expect(session[:github_access_token]).to eq(token) - expect(controller).to redirect_to(status_import_github_url) - end + it_behaves_like 'a GitHub-ish import controller: POST personal_access_token' end describe "GET status" do - before do - @repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim') - @org = OpenStruct.new(login: 'company') - @org_repo = OpenStruct.new(login: 'company', full_name: 'company/repo') - assign_session_token - end - - it "assigns variables" do - @project = create(:project, import_type: 'github', creator_id: user.id) - stub_client(repos: [@repo, @org_repo], orgs: [@org], org_repos: [@org_repo]) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([@repo, @org_repo]) - end - - it "does not show already added project" do - @project = create(:project, import_type: 'github', creator_id: user.id, import_source: 'asd/vim') - stub_client(repos: [@repo], orgs: []) - - get :status - - expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([]) - end - - it "handles an invalid access token" do - allow_any_instance_of(Gitlab::GithubImport::Client). - to receive(:repos).and_raise(Octokit::Unauthorized) - - get :status - - expect(session[:github_access_token]).to eq(nil) - expect(controller).to redirect_to(new_import_github_url) - expect(flash[:alert]).to eq('Access denied to your GitHub account.') - end + it_behaves_like 'a GitHub-ish import controller: GET status' end describe "POST create" do - let(:github_username) { user.username } - let(:github_user) { OpenStruct.new(login: github_username) } - let(:github_repo) do - OpenStruct.new( - name: 'vim', - full_name: "#{github_username}/vim", - owner: OpenStruct.new(login: github_username) - ) - end - - before do - stub_client(user: github_user, repo: github_repo) - assign_session_token - end - - context "when the repository owner is the GitHub user" do - context "when the GitHub user and GitLab user's usernames match" do - it "takes the current user's namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params). - and_return(double(execute: true)) - - post :create, format: :js - end - end - - context "when the GitHub user and GitLab user's usernames don't match" do - let(:github_username) { "someone_else" } - - it "takes the current user's namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params). - and_return(double(execute: true)) - - post :create, format: :js - end - end - end - - context "when the repository owner is not the GitHub user" do - let(:other_username) { "someone_else" } - - before do - github_repo.owner = OpenStruct.new(login: other_username) - assign_session_token - end - - context "when a namespace with the GitHub user's username already exists" do - let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) } - - context "when the namespace is owned by the GitLab user" do - it "takes the existing namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, github_repo.name, existing_namespace, user, access_params). - and_return(double(execute: true)) - - post :create, format: :js - end - end - - context "when the namespace is not owned by the GitLab user" do - before do - existing_namespace.owner = create(:user) - existing_namespace.save - end - - it "creates a project using user's namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params). - and_return(double(execute: true)) - - post :create, format: :js - end - end - end - - context "when a namespace with the GitHub user's username doesn't exist" do - context "when current user can create namespaces" do - it "creates the namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).and_return(double(execute: true)) - - expect { post :create, target_namespace: github_repo.name, format: :js }.to change(Namespace, :count).by(1) - end - - it "takes the new namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, github_repo.name, an_instance_of(Group), user, access_params). - and_return(double(execute: true)) - - post :create, target_namespace: github_repo.name, format: :js - end - end - - context "when current user can't create namespaces" do - before do - user.update_attribute(:can_create_group, false) - end - - it "doesn't create the namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).and_return(double(execute: true)) - - expect { post :create, format: :js }.not_to change(Namespace, :count) - end - - it "takes the current user's namespace" do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params). - and_return(double(execute: true)) - - post :create, format: :js - end - end - end - - context 'user has chosen a namespace and name for the project' do - let(:test_namespace) { create(:namespace, name: 'test_namespace', owner: user) } - let(:test_name) { 'test_name' } - - it 'takes the selected namespace and name' do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, test_name, test_namespace, user, access_params). - and_return(double(execute: true)) - - post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js } - end - - it 'takes the selected name and default namespace' do - expect(Gitlab::GithubImport::ProjectCreator). - to receive(:new).with(github_repo, test_name, user.namespace, user, access_params). - and_return(double(execute: true)) - - post :create, { new_name: test_name, format: :js } - end - end - end + it_behaves_like 'a GitHub-ish import controller: POST create' end end diff --git a/spec/routing/import_routing_spec.rb b/spec/routing/import_routing_spec.rb new file mode 100644 index 00000000000..f1234ff470b --- /dev/null +++ b/spec/routing/import_routing_spec.rb @@ -0,0 +1,166 @@ +require 'spec_helper' + +# Shared examples for a resource inside a Project +# +# By default it tests all the default REST actions: index, create, new, edit, +# show, update, and destroy. You can remove actions by customizing the +# `actions` variable. +# +# It also expects a `controller` variable to be available which defines both +# the path to the resource as well as the controller name. +# +# Examples +# +# # Default behavior +# it_behaves_like 'RESTful project resources' do +# let(:controller) { 'issues' } +# end +# +# # Customizing actions +# it_behaves_like 'RESTful project resources' do +# let(:actions) { [:index] } +# let(:controller) { 'issues' } +# end +shared_examples 'importer routing' do + let(:except_actions) { [] } + + it 'to #create' do + expect(post("/import/#{provider}")).to route_to("import/#{provider}#create") unless except_actions.include?(:create) + end + + it 'to #new' do + expect(get("/import/#{provider}/new")).to route_to("import/#{provider}#new") unless except_actions.include?(:new) + end + + it 'to #status' do + expect(get("/import/#{provider}/status")).to route_to("import/#{provider}#status") unless except_actions.include?(:status) + end + + it 'to #callback' do + expect(get("/import/#{provider}/callback")).to route_to("import/#{provider}#callback") unless except_actions.include?(:callback) + end + + it 'to #jobs' do + expect(get("/import/#{provider}/jobs")).to route_to("import/#{provider}#jobs") unless except_actions.include?(:jobs) + end +end + +# personal_access_token_import_github POST /import/github/personal_access_token(.:format) import/github#personal_access_token +# status_import_github GET /import/github/status(.:format) import/github#status +# callback_import_github GET /import/github/callback(.:format) import/github#callback +# jobs_import_github GET /import/github/jobs(.:format) import/github#jobs +# import_github POST /import/github(.:format) import/github#create +# new_import_github GET /import/github/new(.:format) import/github#new +describe Import::GithubController, 'routing' do + it_behaves_like 'importer routing' do + let(:provider) { 'github' } + end + + it 'to #personal_access_token' do + expect(post('/import/github/personal_access_token')).to route_to('import/github#personal_access_token') + end +end + +# personal_access_token_import_gitea POST /import/gitea/personal_access_token(.:format) import/gitea#personal_access_token +# status_import_gitea GET /import/gitea/status(.:format) import/gitea#status +# jobs_import_gitea GET /import/gitea/jobs(.:format) import/gitea#jobs +# import_gitea POST /import/gitea(.:format) import/gitea#create +# new_import_gitea GET /import/gitea/new(.:format) import/gitea#new +describe Import::GiteaController, 'routing' do + it_behaves_like 'importer routing' do + let(:except_actions) { [:callback] } + let(:provider) { 'gitea' } + end + + it 'to #personal_access_token' do + expect(post('/import/gitea/personal_access_token')).to route_to('import/gitea#personal_access_token') + end + +end + +# status_import_gitlab GET /import/gitlab/status(.:format) import/gitlab#status +# callback_import_gitlab GET /import/gitlab/callback(.:format) import/gitlab#callback +# jobs_import_gitlab GET /import/gitlab/jobs(.:format) import/gitlab#jobs +# import_gitlab POST /import/gitlab(.:format) import/gitlab#create +describe Import::GitlabController, 'routing' do + it_behaves_like 'importer routing' do + let(:except_actions) { [:new] } + let(:provider) { 'gitlab' } + end +end + +# status_import_bitbucket GET /import/bitbucket/status(.:format) import/bitbucket#status +# callback_import_bitbucket GET /import/bitbucket/callback(.:format) import/bitbucket#callback +# jobs_import_bitbucket GET /import/bitbucket/jobs(.:format) import/bitbucket#jobs +# import_bitbucket POST /import/bitbucket(.:format) import/bitbucket#create +describe Import::BitbucketController, 'routing' do + it_behaves_like 'importer routing' do + let(:except_actions) { [:new] } + let(:provider) { 'bitbucket' } + end +end + +# status_import_google_code GET /import/google_code/status(.:format) import/google_code#status +# callback_import_google_code POST /import/google_code/callback(.:format) import/google_code#callback +# jobs_import_google_code GET /import/google_code/jobs(.:format) import/google_code#jobs +# new_user_map_import_google_code GET /import/google_code/user_map(.:format) import/google_code#new_user_map +# create_user_map_import_google_code POST /import/google_code/user_map(.:format) import/google_code#create_user_map +# import_google_code POST /import/google_code(.:format) import/google_code#create +# new_import_google_code GET /import/google_code/new(.:format) import/google_code#new +describe Import::GoogleCodeController, 'routing' do + it_behaves_like 'importer routing' do + let(:except_actions) { [:callback] } + let(:provider) { 'google_code' } + end + + it 'to #callback' do + expect(post("/import/google_code/callback")).to route_to("import/google_code#callback") + end + + it 'to #new_user_map' do + expect(get('/import/google_code/user_map')).to route_to('import/google_code#new_user_map') + end + + it 'to #create_user_map' do + expect(post('/import/google_code/user_map')).to route_to('import/google_code#create_user_map') + end +end + +# status_import_fogbugz GET /import/fogbugz/status(.:format) import/fogbugz#status +# callback_import_fogbugz POST /import/fogbugz/callback(.:format) import/fogbugz#callback +# jobs_import_fogbugz GET /import/fogbugz/jobs(.:format) import/fogbugz#jobs +# new_user_map_import_fogbugz GET /import/fogbugz/user_map(.:format) import/fogbugz#new_user_map +# create_user_map_import_fogbugz POST /import/fogbugz/user_map(.:format) import/fogbugz#create_user_map +# import_fogbugz POST /import/fogbugz(.:format) import/fogbugz#create +# new_import_fogbugz GET /import/fogbugz/new(.:format) import/fogbugz#new +describe Import::FogbugzController, 'routing' do + it_behaves_like 'importer routing' do + let(:except_actions) { [:callback] } + let(:provider) { 'fogbugz' } + end + + it 'to #callback' do + expect(post("/import/fogbugz/callback")).to route_to("import/fogbugz#callback") + end + + it 'to #new_user_map' do + expect(get('/import/fogbugz/user_map')).to route_to('import/fogbugz#new_user_map') + end + + it 'to #create_user_map' do + expect(post('/import/fogbugz/user_map')).to route_to('import/fogbugz#create_user_map') + end +end + +# import_gitlab_project POST /import/gitlab_project(.:format) import/gitlab_projects#create +# POST /import/gitlab_project(.:format) import/gitlab_projects#create +# new_import_gitlab_project GET /import/gitlab_project/new(.:format) import/gitlab_projects#new +describe Import::GitlabProjectsController, 'routing' do + it 'to #create' do + expect(post('/import/gitlab_project')).to route_to('import/gitlab_projects#create') + end + + it 'to #new' do + expect(get('/import/gitlab_project/new')).to route_to('import/gitlab_projects#new') + end +end diff --git a/spec/support/githubish_import_controller_shared_context.rb b/spec/support/githubish_import_controller_shared_context.rb new file mode 100644 index 00000000000..e71994edec6 --- /dev/null +++ b/spec/support/githubish_import_controller_shared_context.rb @@ -0,0 +1,10 @@ +shared_context 'a GitHub-ish import controller' do + let(:user) { create(:user) } + let(:token) { "asdasd12345" } + let(:access_params) { { github_access_token: token } } + + before do + sign_in(user) + allow(controller).to receive(:"#{provider}_import_enabled?").and_return(true) + end +end diff --git a/spec/support/githubish_import_controller_shared_examples.rb b/spec/support/githubish_import_controller_shared_examples.rb new file mode 100644 index 00000000000..aa2d8aed0bd --- /dev/null +++ b/spec/support/githubish_import_controller_shared_examples.rb @@ -0,0 +1,228 @@ +# Specifications for behavior common to all objects with an email attribute. +# Takes a list of email-format attributes and requires: +# - subject { "the object with a attribute= setter" } +# Note: You have access to `email_value` which is the email address value +# being currently tested). + +shared_examples 'a GitHub-ish import controller: POST personal_access_token' do + let(:status_import_url) { public_send("status_import_#{provider}_url") } + + it "updates access token" do + token = 'asdfasdf9876' + + allow_any_instance_of(Gitlab::GithubImport::Client). + to receive(:user).and_return(true) + + post :personal_access_token, personal_access_token: token + + expect(session[:access_token]).to eq(token) + expect(controller).to redirect_to(status_import_url) + end +end + +shared_examples 'a GitHub-ish import controller: GET new' do + let(:status_import_url) { public_send("status_import_#{provider}_url") } + + it "redirects to status if we already have a token" do + assign_session_token + allow(controller).to receive(:logged_in_with_provider?).and_return(false) + + get :new + + expect(controller).to redirect_to(status_import_url) + end + + it "renders the :new page if no token is present in session" do + get :new + + expect(response).to render_template(:new) + end +end + +shared_examples 'a GitHub-ish import controller: GET status' do + let(:new_import_url) { public_send("new_import_#{provider}_url") } + let(:user) { create(:user) } + let(:repo) { OpenStruct.new(login: 'vim', full_name: 'asd/vim') } + let(:org) { OpenStruct.new(login: 'company') } + let(:org_repo) { OpenStruct.new(login: 'company', full_name: 'company/repo') } + let(:extra_assign_expectations) { {} } + + before do + assign_session_token + end + + it "assigns variables" do + project = create(:empty_project, import_type: provider, creator_id: user.id) + stub_client(repos: [repo, org_repo], orgs: [org], org_repos: [org_repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([project]) + expect(assigns(:repos)).to eq([repo, org_repo]) + extra_assign_expectations.each do |key, value| + expect(assigns(key)).to eq(value) + end + end + + it "does not show already added project" do + project = create(:empty_project, import_type: provider, creator_id: user.id, import_source: 'asd/vim') + stub_client(repos: [repo], orgs: []) + + get :status + + expect(assigns(:already_added_projects)).to eq([project]) + expect(assigns(:repos)).to eq([]) + end + + it "handles an invalid access token" do + allow_any_instance_of(Gitlab::GithubImport::Client). + to receive(:repos).and_raise(Octokit::Unauthorized) + + get :status + + expect(session[:access_token]).to eq(nil) + expect(controller).to redirect_to(new_import_url) + expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.options.key(provider.to_s)} account.") + end +end + +shared_examples 'a GitHub-ish import controller: POST create' do + let(:user) { create(:user) } + let(:provider_username) { user.username } + let(:provider_user) { OpenStruct.new(login: provider_username) } + let(:provider_repo) do + OpenStruct.new( + name: 'vim', + full_name: "#{provider_username}/vim", + owner: OpenStruct.new(login: provider_username) + ) + end + + before do + stub_client(user: provider_user, repo: provider_repo) + assign_session_token + end + + context "when the repository owner is the Gitea user" do + context "when the Gitea user and GitLab user's usernames match" do + it "takes the current user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + + context "when the Gitea user and GitLab user's usernames don't match" do + let(:provider_username) { "someone_else" } + + it "takes the current user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + end + + context "when the repository owner is not the Gitea user" do + let(:other_username) { "someone_else" } + + before do + provider_repo.owner = OpenStruct.new(login: other_username) + assign_session_token + end + + context "when a namespace with the Gitea user's username already exists" do + let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) } + + context "when the namespace is owned by the GitLab user" do + it "takes the existing namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + + context "when the namespace is not owned by the GitLab user" do + before do + existing_namespace.owner = create(:user) + existing_namespace.save + end + + it "creates a project using user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + end + + context "when a namespace with the Gitea user's username doesn't exist" do + context "when current user can create namespaces" do + it "creates the namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).and_return(double(execute: true)) + + expect { post :create, target_namespace: provider_repo.name, format: :js }.to change(Namespace, :count).by(1) + end + + it "takes the new namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, target_namespace: provider_repo.name, format: :js + end + end + + context "when current user can't create namespaces" do + before do + user.update_attribute(:can_create_group, false) + end + + it "doesn't create the namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).and_return(double(execute: true)) + + expect { post :create, format: :js }.not_to change(Namespace, :count) + end + + it "takes the current user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + end + + context 'user has chosen a namespace and name for the project' do + let(:test_namespace) { create(:namespace, name: 'test_namespace', owner: user) } + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js } + end + + it 'takes the selected name and default namespace' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { new_name: test_name, format: :js } + end + end + end +end diff --git a/spec/support/import_spec_helper.rb b/spec/support/import_spec_helper.rb index 6710962f082..cd25e05ac4b 100644 --- a/spec/support/import_spec_helper.rb +++ b/spec/support/import_spec_helper.rb @@ -30,4 +30,8 @@ module ImportSpecHelper ) allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider]) end + + def assign_session_token + session[:access_token] = 'asdasd12345' + end end From 99ddd1dcbed35b642d7bd8a52cc6e5e5453b9f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 15 Dec 2016 17:36:53 +0100 Subject: [PATCH 324/386] Modify GithubImport to support Gitea MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reason is that Gitea plan to be GitHub-compatible so it makes sense to just modify GitHubImport a bit for now, and hopefully we can change it to GitHubishImport once Gitea is 100%-compatible. Signed-off-by: Rémy Coutable --- app/controllers/import/gitea_controller.rb | 2 +- app/helpers/import_helper.rb | 8 +- app/views/import/gitea/status.html.haml | 8 +- app/views/import/github/status.html.haml | 5 +- .../unreleased/22348-gitea-importer.yml | 2 +- lib/gitlab/github_import/base_formatter.rb | 4 + lib/gitlab/github_import/client.rb | 16 +- lib/gitlab/github_import/importer.rb | 80 ++-- .../github_import/issuable_formatter.rb | 60 +++ lib/gitlab/github_import/issue_formatter.rb | 52 +-- .../github_import/milestone_formatter.rb | 12 +- .../github_import/pull_request_formatter.rb | 62 +-- .../import/gitea_controller_spec.rb | 2 +- spec/lib/gitlab/github_import/client_spec.rb | 50 ++- .../lib/gitlab/github_import/importer_spec.rb | 392 +++++++++++------- .../github_import/issuable_formatter_spec.rb | 21 + .../github_import/issue_formatter_spec.rb | 38 +- .../github_import/milestone_formatter_spec.rb | 27 +- .../pull_request_formatter_spec.rb | 34 +- spec/routing/import_routing_spec.rb | 1 - ...hubish_import_controller_shared_context.rb | 0 ...ubish_import_controller_shared_examples.rb | 2 +- 22 files changed, 527 insertions(+), 351 deletions(-) create mode 100644 lib/gitlab/github_import/issuable_formatter.rb create mode 100644 spec/lib/gitlab/github_import/issuable_formatter_spec.rb rename spec/support/{ => controllers}/githubish_import_controller_shared_context.rb (100%) rename spec/support/{ => controllers}/githubish_import_controller_shared_examples.rb (99%) diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb index c82a20be04c..3bc21e62a1e 100644 --- a/app/controllers/import/gitea_controller.rb +++ b/app/controllers/import/gitea_controller.rb @@ -11,7 +11,7 @@ class Import::GiteaController < Import::GithubController end def status - @gitea_root_url = session[:host_url] + @gitea_host_url = session[:host_url] super end diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index fb79e2a4eef..f52a0f176e9 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -8,8 +8,8 @@ module ImportHelper link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank' end - def gitea_project_link(root_url, path_with_namespace) - link_to path_with_namespace, gitea_project_url(root_url, path_with_namespace), target: '_blank' + def gitea_project_link(path_with_namespace) + link_to path_with_namespace, gitea_project_url(path_with_namespace), target: '_blank' end private @@ -25,7 +25,7 @@ module ImportHelper @github_url = provider.fetch('url', 'https://github.com') if provider end - def gitea_project_url(root_url, path_with_namespace) - "#{root_url}/#{path_with_namespace}" + def gitea_project_url(path_with_namespace) + "#{@gitea_host_url.sub(%r{/+\z}, '')}/#{path_with_namespace}" end end diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml index 2b25892c0da..a7321632994 100644 --- a/app/views/import/gitea/status.html.haml +++ b/app/views/import/gitea/status.html.haml @@ -1,8 +1,8 @@ -- page_title "Gitea import" +- page_title "Gitea Import" - header_title "Projects", root_path %h3.page-title = custom_icon('go_logo') - Import projects from Gitea + Import Projects from Gitea %p.light Select projects you want to import. @@ -26,7 +26,7 @@ - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} %td - = gitea_project_link(@gitea_root_url, project.import_source) + = gitea_project_link(project.import_source) %td = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status @@ -43,7 +43,7 @@ - @repos.each do |repo| %tr{id: "repo_#{repo.id}"} %td - = gitea_project_link(@gitea_root_url, repo.full_name) + = gitea_project_link(repo.full_name) %td.import-target %fieldset.row .input-group diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 4c721d40b55..70b18ee217d 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -1,8 +1,7 @@ -- page_title "GitHub import" +- page_title "GitHub Import" - header_title "Projects", root_path %h3.page-title - %i.fa.fa-github - Import projects from GitHub + = icon 'github', text: 'Import Projects from GitHub' %p.light Select projects you want to import. diff --git a/changelogs/unreleased/22348-gitea-importer.yml b/changelogs/unreleased/22348-gitea-importer.yml index ce81a3cfefb..2aeefb0b259 100644 --- a/changelogs/unreleased/22348-gitea-importer.yml +++ b/changelogs/unreleased/22348-gitea-importer.yml @@ -1,4 +1,4 @@ --- title: New Gitea importer -merge_request: 6945 +merge_request: 8116 author: diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb index 6dbae64a9fe..95dba9a327b 100644 --- a/lib/gitlab/github_import/base_formatter.rb +++ b/lib/gitlab/github_import/base_formatter.rb @@ -15,6 +15,10 @@ module Gitlab end end + def url + raw_data.url || '' + end + private def gitlab_user_id(github_id) diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index a96e0bc63dd..ba869faa92e 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -8,7 +8,7 @@ module Gitlab def initialize(access_token, host: nil, api_version: 'v3') @access_token = access_token - @host = host + @host = host.to_s.sub(%r{/+\z}, '') @api_version = api_version if access_token @@ -16,10 +16,6 @@ module Gitlab end end - def api_endpoint - host.present? && api_version.present? ? "#{host}/api/#{api_version}" : github_options[:site] - end - def api @api ||= ::Octokit::Client.new( access_token: access_token, @@ -27,7 +23,7 @@ module Gitlab # If there is no config, we're connecting to github.com and we # should verify ssl. connection_options: { - ssl: { verify: config ? config['verify_ssl'] : false } + ssl: { verify: config ? config['verify_ssl'] : true } } ) end @@ -70,6 +66,14 @@ module Gitlab private + def api_endpoint + if host.present? && api_version.present? + "#{host}/api/#{api_version}" + else + github_options[:site] + end + end + def config Gitlab.config.omniauth.providers.find { |provider| provider.name == "github" } end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index c32e78cae03..c53cd1a928d 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -3,7 +3,7 @@ module Gitlab class Importer include Gitlab::ShellAdapter - attr_reader :client, :errors, :project, :repo, :repo_url + attr_reader :errors, :project, :repo, :repo_url def initialize(project) @project = project @@ -11,12 +11,27 @@ module Gitlab @repo_url = project.import_url @errors = [] @labels = {} + end - if credentials - @client = Client.new(credentials[:user]) - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + def client + return @client if defined?(@client) + unless credentials + raise Projects::ImportService::Error, + "Unable to find project import data credentials for project ID: #{@project.id}" end + + opts = {} + # Gitea plan to be GitHub compliant + if project.import_type == 'gitea' + uri = URI.parse(project.import_url) + host = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}".sub(%r{/?[\w-]+/[\w-]+\.git\z}, '') + opts = { + host: host, + api_version: 'v1' + } + end + + @client = Client.new(credentials[:user], opts) end def execute @@ -35,7 +50,13 @@ module Gitlab import_comments(:issues) import_comments(:pull_requests) import_wiki - import_releases + + # Gitea doesn't have a Release API yet + # See https://github.com/go-gitea/gitea/issues/330 + unless project.import_type == 'gitea' + import_releases + end + handle_errors true @@ -44,7 +65,9 @@ module Gitlab private def credentials - @credentials ||= project.import_data.credentials if project.import_data + return @credentials if defined?(@credentials) + + @credentials = project.import_data ? project.import_data.credentials : nil end def handle_errors @@ -60,9 +83,10 @@ module Gitlab fetch_resources(:labels, repo, per_page: 100) do |labels| labels.each do |raw| begin - GithubImport::LabelFormatter.new(project, raw).create! + gh_label = LabelFormatter.new(project, raw) + gh_label.create! rescue => e - errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message } end end end @@ -74,9 +98,10 @@ module Gitlab fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones| milestones.each do |raw| begin - GithubImport::MilestoneFormatter.new(project, raw).create! + gh_milestone = MilestoneFormatter.new(project, raw) + gh_milestone.create! rescue => e - errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message } end end end @@ -85,7 +110,7 @@ module Gitlab def import_issues fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues| issues.each do |raw| - gh_issue = GithubImport::IssueFormatter.new(project, raw) + gh_issue = IssueFormatter.new(project, raw) begin issuable = @@ -97,7 +122,7 @@ module Gitlab apply_labels(issuable, raw) rescue => e - errors << { type: :issue, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :issue, url: Gitlab::UrlSanitizer.sanitize(gh_issue.url), errors: e.message } end end end @@ -106,18 +131,23 @@ module Gitlab def import_pull_requests fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests| pull_requests.each do |raw| - pull_request = GithubImport::PullRequestFormatter.new(project, raw) - next unless pull_request.valid? + gh_pull_request = PullRequestFormatter.new(project, raw) + next unless gh_pull_request.valid? begin - restore_source_branch(pull_request) unless pull_request.source_branch_exists? - restore_target_branch(pull_request) unless pull_request.target_branch_exists? + restore_source_branch(gh_pull_request) unless gh_pull_request.source_branch_exists? + restore_target_branch(gh_pull_request) unless gh_pull_request.target_branch_exists? - pull_request.create! + merge_request = gh_pull_request.create! + + # Gitea doesn't return PR in the Issue API endpoint, so labels must be assigned at this stage + if project.import_type == 'gitea' + apply_labels(merge_request, raw) + end rescue => e - errors << { type: :pull_request, url: Gitlab::UrlSanitizer.sanitize(pull_request.url), errors: e.message } + errors << { type: :pull_request, url: Gitlab::UrlSanitizer.sanitize(gh_pull_request.url), errors: e.message } ensure - clean_up_restored_branches(pull_request) + clean_up_restored_branches(gh_pull_request) end end end @@ -179,7 +209,7 @@ module Gitlab ActiveRecord::Base.no_touching do comments.each do |raw| begin - comment = GithubImport::CommentFormatter.new(project, raw) + comment = CommentFormatter.new(project, raw) # GH does not return info about comment's parent, so we guess it by checking its URL! *_, parent, iid = URI(raw.html_url).path.split('/') issuable_class = parent == 'issues' ? Issue : MergeRequest @@ -198,7 +228,7 @@ module Gitlab last_note_attrs = nil cut_off_index = comments.find_index do |raw| - comment = GithubImport::CommentFormatter.new(project, raw) + comment = CommentFormatter.new(project, raw) comment_attrs = comment.attributes last_note_attrs ||= last_note.slice(*comment_attrs.keys) @@ -214,7 +244,7 @@ module Gitlab def import_wiki unless project.wiki.repository_exists? - wiki = GithubImport::WikiFormatter.new(project) + wiki = WikiFormatter.new(project) gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) end rescue Gitlab::Shell::Error => e @@ -230,10 +260,10 @@ module Gitlab fetch_resources(:releases, repo, per_page: 100) do |releases| releases.each do |raw| begin - gh_release = GithubImport::ReleaseFormatter.new(project, raw) + gh_release = ReleaseFormatter.new(project, raw) gh_release.create! if gh_release.valid? rescue => e - errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message } end end end diff --git a/lib/gitlab/github_import/issuable_formatter.rb b/lib/gitlab/github_import/issuable_formatter.rb new file mode 100644 index 00000000000..256f360efc7 --- /dev/null +++ b/lib/gitlab/github_import/issuable_formatter.rb @@ -0,0 +1,60 @@ +module Gitlab + module GithubImport + class IssuableFormatter < BaseFormatter + def project_association + raise NotImplementedError + end + + def number + raw_data.number + end + + def find_condition + { iid: number } + end + + private + + def state + raw_data.state == 'closed' ? 'closed' : 'opened' + end + + def assigned? + raw_data.assignee.present? + end + + def assignee_id + if assigned? + gitlab_user_id(raw_data.assignee.id) + end + end + + def author + raw_data.user.login + end + + def author_id + gitlab_author_id || project.creator_id + end + + def body + raw_data.body || "" + end + + def description + if gitlab_author_id + body + else + formatter.author_line(author) + body + end + end + + def milestone + if raw_data.milestone.present? + milestone = MilestoneFormatter.new(project, raw_data.milestone) + project.milestones.find_by(milestone.find_condition) + end + end + end + end +end diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb index 21a3dee203b..6f5ac4dac0d 100644 --- a/lib/gitlab/github_import/issue_formatter.rb +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -1,6 +1,6 @@ module Gitlab module GithubImport - class IssueFormatter < BaseFormatter + class IssueFormatter < IssuableFormatter def attributes { iid: number, @@ -24,59 +24,9 @@ module Gitlab :issues end - def find_condition - { iid: number } - end - - def number - raw_data.number - end - def pull_request? raw_data.pull_request.present? end - - private - - def assigned? - raw_data.assignee.present? - end - - def assignee_id - if assigned? - gitlab_user_id(raw_data.assignee.id) - end - end - - def author - raw_data.user.login - end - - def author_id - gitlab_author_id || project.creator_id - end - - def body - raw_data.body || "" - end - - def description - if gitlab_author_id - body - else - formatter.author_line(author) + body - end - end - - def milestone - if raw_data.milestone.present? - project.milestones.find_by(iid: raw_data.milestone.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr)) - end - end - - def state - raw_data.state == 'closed' ? 'closed' : 'opened' - end end end end diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb index 678d56b830b..fe172b854d5 100644 --- a/lib/gitlab/github_import/milestone_formatter.rb +++ b/lib/gitlab/github_import/milestone_formatter.rb @@ -3,7 +3,7 @@ module Gitlab class MilestoneFormatter < BaseFormatter def attributes { - iid: raw_data.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr), + iid: number, project: project, title: raw_data.title, description: raw_data.description, @@ -19,11 +19,15 @@ module Gitlab end def find_condition - { iid: raw_data.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr) } + { iid: number } end - def self.iid_attr - :number + def number + if project.import_type == 'gitea' + raw_data.id + else + raw_data.number + end end private diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index ea8768fded7..4ea0200e89b 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -1,6 +1,6 @@ module Gitlab module GithubImport - class PullRequestFormatter < BaseFormatter + class PullRequestFormatter < IssuableFormatter delegate :exists?, :project, :ref, :repo, :sha, to: :source_branch, prefix: true delegate :exists?, :project, :ref, :repo, :sha, to: :target_branch, prefix: true @@ -28,14 +28,6 @@ module Gitlab :merge_requests end - def find_condition - { iid: number } - end - - def number - raw_data.number - end - def valid? source_branch.valid? && target_branch.valid? end @@ -60,56 +52,14 @@ module Gitlab end end - def url - raw_data.url - end - private - def assigned? - raw_data.assignee.present? - end - - def assignee_id - if assigned? - gitlab_user_id(raw_data.assignee.id) - end - end - - def author - raw_data.user.login - end - - def author_id - gitlab_author_id || project.creator_id - end - - def body - raw_data.body || "" - end - - def description - if gitlab_author_id - body - else - formatter.author_line(author) + body - end - end - - def milestone - if raw_data.milestone.present? - project.milestones.find_by(iid: raw_data.milestone.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr)) - end - end - def state - @state ||= if raw_data.state == 'closed' && raw_data.merged_at.present? - 'merged' - elsif raw_data.state == 'closed' - 'closed' - else - 'opened' - end + if raw_data.state == 'closed' && raw_data.merged_at.present? + 'merged' + else + super + end end end end diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb index 3064d1dd58a..3643386ffbc 100644 --- a/spec/controllers/import/gitea_controller_spec.rb +++ b/spec/controllers/import/gitea_controller_spec.rb @@ -29,7 +29,7 @@ describe Import::GiteaController do before do assign_host_url end - let(:extra_assign_expectations) { { gitea_root_url: host_url } } + let(:extra_assign_expectations) { { gitea_host_url: host_url } } end end diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb index e829b936343..21f2a9e225b 100644 --- a/spec/lib/gitlab/github_import/client_spec.rb +++ b/spec/lib/gitlab/github_import/client_spec.rb @@ -45,20 +45,46 @@ describe Gitlab::GithubImport::Client, lib: true do end end - context 'when provider does not specity an API endpoint' do - it 'uses GitHub root API endpoint' do - expect(client.api.api_endpoint).to eq 'https://api.github.com/' - end - end - - context 'when provider specify a custom API endpoint' do - before do - github_provider['args']['client_options']['site'] = 'https://github.company.com/' + describe '#api_endpoint' do + context 'when provider does not specity an API endpoint' do + it 'uses GitHub root API endpoint' do + expect(client.api.api_endpoint).to eq 'https://api.github.com/' + end end - it 'uses the custom API endpoint' do - expect(OmniAuth::Strategies::GitHub).not_to receive(:default_options) - expect(client.api.api_endpoint).to eq 'https://github.company.com/' + context 'when provider specify a custom API endpoint' do + before do + github_provider['args']['client_options']['site'] = 'https://github.company.com/' + end + + it 'uses the custom API endpoint' do + expect(OmniAuth::Strategies::GitHub).not_to receive(:default_options) + expect(client.api.api_endpoint).to eq 'https://github.company.com/' + end + end + + context 'when given a host' do + subject(:client) { described_class.new(token, host: 'https://try.gitea.io/') } + + it 'builds a endpoint with the given host and the default API version' do + expect(client.api.api_endpoint).to eq 'https://try.gitea.io/api/v3/' + end + end + + context 'when given an API version' do + subject(:client) { described_class.new(token, api_version: 'v3') } + + it 'does not use the API version without a host' do + expect(client.api.api_endpoint).to eq 'https://api.github.com/' + end + end + + context 'when given a host and version' do + subject(:client) { described_class.new(token, host: 'https://try.gitea.io/', api_version: 'v3') } + + it 'builds a endpoint with the given options' do + expect(client.api.api_endpoint).to eq 'https://try.gitea.io/api/v3/' + end end end diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index 9e027839f59..0a03b7353f6 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -1,169 +1,251 @@ require 'spec_helper' describe Gitlab::GithubImport::Importer, lib: true do - describe '#execute' do + shared_examples 'Gitlab::GithubImport::Importer#execute' do + let(:expected_not_called) { [] } + before do - allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + allow(project).to receive(:import_data).and_return(double.as_null_object) end - context 'when an error occurs' do - let(:project) { create(:project, import_url: 'https://github.com/octocat/Hello-World.git', wiki_access_level: ProjectFeature::DISABLED) } - let(:octocat) { double(id: 123456, login: 'octocat') } - let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } - let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } - let(:repository) { double(id: 1, fork: false) } - let(:source_sha) { create(:commit, project: project).id } - let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) } - let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } - let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) } + it 'calls import methods' do + importer = described_class.new(project) - let(:label1) do - double( - name: 'Bug', - color: 'ff0000', - url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug' + expected_called = [ + :import_labels, :import_milestones, :import_pull_requests, :import_issues, + :import_wiki, :import_releases, :handle_errors + ] + + expected_called -= expected_not_called + + aggregate_failures do + expected_called.each do |method_name| + expect(importer).to receive(method_name) + end + + expect(importer).to receive(:import_comments).with(:issues) + expect(importer).to receive(:import_comments).with(:pull_requests) + + expected_not_called.each do |method_name| + expect(importer).not_to receive(method_name) + end + end + + importer.execute + end + end + + shared_examples 'Gitlab::GithubImport::Importer#execute an error occurs' do + before do + allow(project).to receive(:import_data).and_return(double.as_null_object) + + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + + allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound) + allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error) + + allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2]) + allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone]) + allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2]) + allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request]) + allow_any_instance_of(Octokit::Client).to receive(:issues_comments).and_return([]) + allow_any_instance_of(Octokit::Client).to receive(:pull_requests_comments).and_return([]) + allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil })) + allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2]) + end + let(:octocat) { double(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + let(:label1) do + double( + name: 'Bug', + color: 'ff0000', + url: "#{api_root}/repos/octocat/Hello-World/labels/bug" + ) + end + + let(:label2) do + double( + name: nil, + color: 'ff0000', + url: "#{api_root}/repos/octocat/Hello-World/labels/bug" + ) + end + + let(:milestone) do + double( + id: 1347, # For Gitea + number: 1347, + state: 'open', + title: '1.0', + description: 'Version 1.0', + due_on: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/milestones/1" + ) + end + + let(:issue1) do + double( + number: 1347, + milestone: nil, + state: 'open', + title: 'Found a bug', + body: "I'm having a problem with this.", + assignee: nil, + user: octocat, + comments: 0, + pull_request: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/issues/1347", + labels: [double(name: 'Label #1')] + ) + end + + let(:issue2) do + double( + number: 1348, + milestone: nil, + state: 'open', + title: nil, + body: "I'm having a problem with this.", + assignee: nil, + user: octocat, + comments: 0, + pull_request: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/issues/1348", + labels: [double(name: 'Label #2')] + ) + end + + let(:repository) { double(id: 1, fork: false) } + let(:source_sha) { create(:commit, project: project).id } + let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) } + let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } + let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) } + let(:pull_request) do + double( + number: 1347, + milestone: nil, + state: 'open', + title: 'New feature', + body: 'Please pull these awesome changes', + head: source_branch, + base: target_branch, + assignee: nil, + user: octocat, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + merged_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/pulls/1347", + labels: [double(name: 'Label #2')] + ) + end + + let(:release1) do + double( + tag_name: 'v1.0.0', + name: 'First release', + body: 'Release v1.0.0', + draft: false, + created_at: created_at, + updated_at: updated_at, + url: "#{api_root}/repos/octocat/Hello-World/releases/1" + ) + end + + let(:release2) do + double( + tag_name: 'v2.0.0', + name: 'Second release', + body: nil, + draft: false, + created_at: created_at, + updated_at: updated_at, + url: "#{api_root}/repos/octocat/Hello-World/releases/2" + ) + end + + it 'returns true' do + expect(described_class.new(project).execute).to eq true + end + + it 'does not raise an error' do + expect { described_class.new(project).execute }.not_to raise_error + end + + it 'stores error messages' do + error = { + message: 'The remote data could not be fully imported.', + errors: [ + { type: :label, url: "#{api_root}/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, + { type: :issue, url: "#{api_root}/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, + { type: :wiki, errors: "Gitlab::Shell::Error" } + ] + } + + unless project.import_type == 'gitea' + error[:errors] << { type: :release, url: "#{api_root}/repos/octocat/Hello-World/releases/2", errors: "Validation failed: Description can't be blank" } + end + + described_class.new(project).execute + + expect(project.import_error).to eq error.to_json + end + end + + let(:project) { create(:project, import_url: "#{repo_root}/octocat/Hello-World.git", wiki_access_level: ProjectFeature::DISABLED) } + let(:credentials) { { user: 'joe' } } + + context 'when importing a GitHub project' do + let(:api_root) { 'https://api.github.com' } + let(:repo_root) { 'https://github.com' } + + it_behaves_like 'Gitlab::GithubImport::Importer#execute' + it_behaves_like 'Gitlab::GithubImport::Importer#execute an error occurs' + + describe '#client' do + it 'instantiates a Client' do + allow(project).to receive(:import_data).and_return(double(credentials: credentials)) + expect(Gitlab::GithubImport::Client).to receive(:new).with( + credentials[:user], + {} ) - end - let(:label2) do - double( - name: nil, - color: 'ff0000', - url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug' + described_class.new(project).client + end + end + end + + context 'when importing a Gitea project' do + let(:api_root) { 'https://try.gitea.io/api/v1' } + let(:repo_root) { 'https://try.gitea.io' } + before do + project.update(import_type: 'gitea', import_url: "#{repo_root}/foo/group/project.git") + end + + it_behaves_like 'Gitlab::GithubImport::Importer#execute' do + let(:expected_not_called) { [:import_releases] } + end + it_behaves_like 'Gitlab::GithubImport::Importer#execute an error occurs' + + describe '#client' do + it 'instantiates a Client' do + allow(project).to receive(:import_data).and_return(double(credentials: credentials)) + expect(Gitlab::GithubImport::Client).to receive(:new).with( + credentials[:user], + { host: "#{repo_root}:443/foo", api_version: 'v1' } ) - end - let(:milestone) do - double( - number: 1347, - state: 'open', - title: '1.0', - description: 'Version 1.0', - due_on: nil, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/milestones/1' - ) - end - - let(:issue1) do - double( - number: 1347, - milestone: nil, - state: 'open', - title: 'Found a bug', - body: "I'm having a problem with this.", - assignee: nil, - user: octocat, - comments: 0, - pull_request: nil, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/issues/1347', - labels: [double(name: 'Label #1')], - ) - end - - let(:issue2) do - double( - number: 1348, - milestone: nil, - state: 'open', - title: nil, - body: "I'm having a problem with this.", - assignee: nil, - user: octocat, - comments: 0, - pull_request: nil, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/issues/1348', - labels: [double(name: 'Label #2')], - ) - end - - let(:pull_request) do - double( - number: 1347, - milestone: nil, - state: 'open', - title: 'New feature', - body: 'Please pull these awesome changes', - head: source_branch, - base: target_branch, - assignee: nil, - user: octocat, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - merged_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/pulls/1347', - ) - end - - let(:release1) do - double( - tag_name: 'v1.0.0', - name: 'First release', - body: 'Release v1.0.0', - draft: false, - created_at: created_at, - updated_at: updated_at, - url: 'https://api.github.com/repos/octocat/Hello-World/releases/1' - ) - end - - let(:release2) do - double( - tag_name: 'v2.0.0', - name: 'Second release', - body: nil, - draft: false, - created_at: created_at, - updated_at: updated_at, - url: 'https://api.github.com/repos/octocat/Hello-World/releases/2' - ) - end - - before do - allow(project).to receive(:import_data).and_return(double.as_null_object) - allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound) - allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2]) - allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone]) - allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2]) - allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request]) - allow_any_instance_of(Octokit::Client).to receive(:issues_comments).and_return([]) - allow_any_instance_of(Octokit::Client).to receive(:pull_requests_comments).and_return([]) - allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil })) - allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2]) - allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error) - end - - it 'returns true' do - expect(described_class.new(project).execute).to eq true - end - - it 'does not raise an error' do - expect { described_class.new(project).execute }.not_to raise_error - end - - it 'stores error messages' do - error = { - message: 'The remote data could not be fully imported.', - errors: [ - { type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, - { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, - { type: :wiki, errors: "Gitlab::Shell::Error" }, - { type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" } - ] - } - - described_class.new(project).execute - - expect(project.import_error).to eq error.to_json + described_class.new(project).client end end end diff --git a/spec/lib/gitlab/github_import/issuable_formatter_spec.rb b/spec/lib/gitlab/github_import/issuable_formatter_spec.rb new file mode 100644 index 00000000000..6bc5f98ed2c --- /dev/null +++ b/spec/lib/gitlab/github_import/issuable_formatter_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::IssuableFormatter, lib: true do + let(:raw_data) do + double(number: 42) + end + let(:project) { double(import_type: 'github') } + let(:issuable_formatter) { described_class.new(project, raw_data) } + + describe '#project_association' do + it { expect { issuable_formatter.project_association }.to raise_error(NotImplementedError) } + end + + describe '#number' do + it { expect(issuable_formatter.number).to eq(42) } + end + + describe '#find_condition' do + it { expect(issuable_formatter.find_condition).to eq({ iid: 42 }) } + end +end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index 95339e2f128..e31ed9c1fa0 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -23,9 +23,9 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do } end - subject(:issue) { described_class.new(project, raw_data)} + subject(:issue) { described_class.new(project, raw_data) } - describe '#attributes' do + shared_examples 'Gitlab::GithubImport::IssueFormatter#attributes' do context 'when issue is open' do let(:raw_data) { double(base_data.merge(state: 'open')) } @@ -83,7 +83,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when it has a milestone' do - let(:milestone) { double(number: 45) } + let(:milestone) { double(id: 42, number: 42) } let(:raw_data) { double(base_data.merge(milestone: milestone)) } it 'returns nil when milestone does not exist' do @@ -91,7 +91,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end it 'returns milestone when it exists' do - milestone = create(:milestone, project: project, iid: 45) + milestone = create(:milestone, project: project, iid: 42) expect(issue.attributes.fetch(:milestone)).to eq milestone end @@ -118,6 +118,28 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end end + shared_examples 'Gitlab::GithubImport::IssueFormatter#number' do + let(:raw_data) { double(base_data.merge(number: 1347)) } + + it 'returns issue number' do + expect(issue.number).to eq 1347 + end + end + + context 'when importing a GitHub project' do + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#number' + end + + context 'when importing a Gitea project' do + before do + project.update(import_type: 'gitea') + end + + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#number' + end + describe '#has_comments?' do context 'when number of comments is greater than zero' do let(:raw_data) { double(base_data.merge(comments: 1)) } @@ -136,14 +158,6 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end end - describe '#number' do - let(:raw_data) { double(base_data.merge(number: 1347)) } - - it 'returns pull request number' do - expect(issue.number).to eq 1347 - end - end - describe '#pull_request?' do context 'when mention a pull request' do let(:raw_data) { double(base_data.merge(pull_request: double)) } diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb index 09337c99a07..6d38041c468 100644 --- a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb @@ -6,7 +6,6 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:base_data) do { - number: 1347, state: 'open', title: '1.0', description: 'Version 1.0', @@ -16,12 +15,15 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do closed_at: nil } end + let(:iid_attr) { :number } - subject(:formatter) { described_class.new(project, raw_data)} + subject(:formatter) { described_class.new(project, raw_data) } + + shared_examples 'Gitlab::GithubImport::MilestoneFormatter#attributes' do + let(:data) { base_data.merge(iid_attr => 1347) } - describe '#attributes' do context 'when milestone is open' do - let(:raw_data) { double(base_data.merge(state: 'open')) } + let(:raw_data) { double(data.merge(state: 'open')) } it 'returns formatted attributes' do expected = { @@ -40,7 +42,7 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do end context 'when milestone is closed' do - let(:raw_data) { double(base_data.merge(state: 'closed')) } + let(:raw_data) { double(data.merge(state: 'closed')) } it 'returns formatted attributes' do expected = { @@ -60,7 +62,7 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do context 'when milestone has a due date' do let(:due_date) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { double(base_data.merge(due_on: due_date)) } + let(:raw_data) { double(data.merge(due_on: due_date)) } it 'returns formatted attributes' do expected = { @@ -78,4 +80,17 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do end end end + + context 'when importing a GitHub project' do + it_behaves_like 'Gitlab::GithubImport::MilestoneFormatter#attributes' + end + + context 'when importing a Gitea project' do + let(:iid_attr) { :id } + before do + project.update(import_type: 'gitea') + end + + it_behaves_like 'Gitlab::GithubImport::MilestoneFormatter#attributes' + end end diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index 302f0fc0623..2b3256edcb2 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -32,9 +32,9 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do } end - subject(:pull_request) { described_class.new(project, raw_data)} + subject(:pull_request) { described_class.new(project, raw_data) } - describe '#attributes' do + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#attributes' do context 'when pull request is open' do let(:raw_data) { double(base_data.merge(state: 'open')) } @@ -149,7 +149,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when it has a milestone' do - let(:milestone) { double(number: 45) } + let(:milestone) { double(id: 42, number: 42) } let(:raw_data) { double(base_data.merge(milestone: milestone)) } it 'returns nil when milestone does not exist' do @@ -157,22 +157,22 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end it 'returns milestone when it exists' do - milestone = create(:milestone, project: project, iid: 45) + milestone = create(:milestone, project: project, iid: 42) expect(pull_request.attributes.fetch(:milestone)).to eq milestone end end end - describe '#number' do - let(:raw_data) { double(base_data.merge(number: 1347)) } + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#number' do + let(:raw_data) { double(base_data) } it 'returns pull request number' do expect(pull_request.number).to eq 1347 end end - describe '#source_branch_name' do + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#source_branch_name' do context 'when source branch exists' do let(:raw_data) { double(base_data) } @@ -190,7 +190,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end end - describe '#target_branch_name' do + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#target_branch_name' do context 'when source branch exists' do let(:raw_data) { double(base_data) } @@ -208,6 +208,24 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end end + context 'when importing a GitHub project' do + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#number' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#source_branch_name' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#target_branch_name' + end + + context 'when importing a Gitea project' do + before do + project.update(import_type: 'gitea') + end + + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#number' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#source_branch_name' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#target_branch_name' + end + describe '#valid?' do context 'when source, and target repos are not a fork' do let(:raw_data) { double(base_data) } diff --git a/spec/routing/import_routing_spec.rb b/spec/routing/import_routing_spec.rb index f1234ff470b..78ff9c6e6fd 100644 --- a/spec/routing/import_routing_spec.rb +++ b/spec/routing/import_routing_spec.rb @@ -75,7 +75,6 @@ describe Import::GiteaController, 'routing' do it 'to #personal_access_token' do expect(post('/import/gitea/personal_access_token')).to route_to('import/gitea#personal_access_token') end - end # status_import_gitlab GET /import/gitlab/status(.:format) import/gitlab#status diff --git a/spec/support/githubish_import_controller_shared_context.rb b/spec/support/controllers/githubish_import_controller_shared_context.rb similarity index 100% rename from spec/support/githubish_import_controller_shared_context.rb rename to spec/support/controllers/githubish_import_controller_shared_context.rb diff --git a/spec/support/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb similarity index 99% rename from spec/support/githubish_import_controller_shared_examples.rb rename to spec/support/controllers/githubish_import_controller_shared_examples.rb index aa2d8aed0bd..e11ab802095 100644 --- a/spec/support/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -82,7 +82,7 @@ shared_examples 'a GitHub-ish import controller: GET status' do expect(session[:access_token]).to eq(nil) expect(controller).to redirect_to(new_import_url) - expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.options.key(provider.to_s)} account.") + expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.") end end From 8fc63d1f648fa38eac9e5422dd42667d8e7f1b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 09:15:30 +0100 Subject: [PATCH 325/386] Improve Gitlab::ImportSources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/controllers/import/github_controller.rb | 2 +- app/services/projects/import_service.rb | 32 +------ lib/gitlab/import_sources.rb | 42 ++++++--- spec/lib/gitlab/import_sources_spec.rb | 94 +++++++++++++++++++++ 4 files changed, 126 insertions(+), 44 deletions(-) create mode 100644 spec/lib/gitlab/import_sources_spec.rb diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 343ca51e510..4ae121ec482 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -82,7 +82,7 @@ class Import::GithubController < Import::BaseController def provider_unauthorized session[:access_token] = nil redirect_to new_import_url, - alert: "Access denied to your #{Gitlab::ImportSources.options.key(provider.to_s)} account." + alert: "Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account." end def access_params diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index 287c0a4257f..cd230528743 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -4,16 +4,6 @@ module Projects class Error < StandardError; end - ALLOWED_TYPES = [ - 'github', - 'bitbucket', - 'gitlab', - 'google_code', - 'fogbugz', - 'gitlab_project', - 'gitea' - ] - def execute add_repository_to_project unless project.gitlab_project_import? @@ -65,29 +55,11 @@ module Projects end def has_importer? - ALLOWED_TYPES.include?(project.import_type) + Gitlab::ImportSources.importer_names.include?(project.import_type) end def importer - return Gitlab::ImportExport::Importer.new(project) if @project.gitlab_project_import? - - class_name = - case project.import_type - when 'github', 'gitea' - Gitlab::GithubImport::Importer - when 'bitbucket' - Gitlab::BitbucketImport::Importer - when 'gitlab' - Gitlab::GitlabImport::Importer - when 'google_code' - Gitlab::GoogleCodeImport::Importer - when 'fogbugz' - Gitlab::FogbugzImport::Importer - else - raise 'Unknown importer type!' - end - - class_name.new(project) + Gitlab::ImportSources.importer(project.import_type).new(project) end def unknown_url? diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 34587582bd1..45958710c13 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -7,22 +7,38 @@ module Gitlab module ImportSources extend CurrentSettings + ImportSource = Struct.new(:name, :title, :importer) + + ImportTable = [ + ImportSource.new('github', 'GitHub', Gitlab::GithubImport::Importer), + ImportSource.new('bitbucket', 'Bitbucket', Gitlab::BitbucketImport::Importer), + ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer), + ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer), + ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), + ImportSource.new('git', 'Repo by URL', nil), + ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer), + ImportSource.new('gitea', 'Gitea', Gitlab::GithubImport::Importer) + ].freeze + class << self - def values - options.values + def options + @options ||= Hash[ImportTable.map { |importer| [importer.title, importer.name] }] end - def options - { - 'GitHub' => 'github', - 'Bitbucket' => 'bitbucket', - 'GitLab.com' => 'gitlab', - 'Google Code' => 'google_code', - 'FogBugz' => 'fogbugz', - 'Repo by URL' => 'git', - 'GitLab export' => 'gitlab_project', - 'Gitea' => 'gitea' - } + def values + @values ||= ImportTable.map(&:name) + end + + def importer_names + @importer_names ||= ImportTable.select(&:importer).map(&:name) + end + + def importer(name) + ImportTable.find { |import_source| import_source.name == name }.importer + end + + def title(name) + options.key(name) end end end diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb new file mode 100644 index 00000000000..8cea38e9ff8 --- /dev/null +++ b/spec/lib/gitlab/import_sources_spec.rb @@ -0,0 +1,94 @@ +require 'spec_helper' + +describe Gitlab::ImportSources do + describe '.options' do + it 'returns a hash' do + expected = + { + 'GitHub' => 'github', + 'Bitbucket' => 'bitbucket', + 'GitLab.com' => 'gitlab', + 'Google Code' => 'google_code', + 'FogBugz' => 'fogbugz', + 'Repo by URL' => 'git', + 'GitLab export' => 'gitlab_project', + 'Gitea' => 'gitea' + } + + expect(described_class.options).to eq(expected) + end + end + + describe '.values' do + it 'returns an array' do + expected = + [ + 'github', + 'bitbucket', + 'gitlab', + 'google_code', + 'fogbugz', + 'git', + 'gitlab_project', + 'gitea' + ] + + expect(described_class.values).to eq(expected) + end + end + + describe '.importer_names' do + it 'returns an array of importer names' do + expected = + [ + 'github', + 'bitbucket', + 'gitlab', + 'google_code', + 'fogbugz', + 'gitlab_project', + 'gitea' + ] + + expect(described_class.importer_names).to eq(expected) + end + end + + describe '.importer' do + import_sources = { + 'github' => Gitlab::GithubImport::Importer, + 'bitbucket' => Gitlab::BitbucketImport::Importer, + 'gitlab' => Gitlab::GitlabImport::Importer, + 'google_code' => Gitlab::GoogleCodeImport::Importer, + 'fogbugz' => Gitlab::FogbugzImport::Importer, + 'git' => nil, + 'gitlab_project' => Gitlab::ImportExport::Importer, + 'gitea' => Gitlab::GithubImport::Importer + } + + import_sources.each do |name, klass| + it "returns #{klass} when given #{name}" do + expect(described_class.importer(name)).to eq(klass) + end + end + end + + describe '.title' do + import_sources = { + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab.com', + 'google_code' => 'Google Code', + 'fogbugz' => 'FogBugz', + 'git' => 'Repo by URL', + 'gitlab_project' => 'GitLab export', + 'gitea' => 'Gitea' + } + + import_sources.each do |name, title| + it "returns #{title} when given #{name}" do + expect(described_class.title(name)).to eq(title) + end + end + end +end From 20aff5cd2b782fa47fe6c15aad07a547179ee147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 10:47:26 +0100 Subject: [PATCH 326/386] Reduce duplication for GitHubish import status view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/helpers/import_helper.rb | 8 +-- app/views/import/_githubish_status.html.haml | 61 ++++++++++++++++++++ app/views/import/gitea/status.html.haml | 59 +------------------ app/views/import/github/status.html.haml | 59 +------------------ spec/helpers/import_helper_spec.rb | 35 +++++++---- 5 files changed, 90 insertions(+), 132 deletions(-) create mode 100644 app/views/import/_githubish_status.html.haml diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index f52a0f176e9..a0642a1894b 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -4,12 +4,10 @@ module ImportHelper "#{namespace}/#{name}" end - def github_project_link(path_with_namespace) - link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank' - end + def provider_project_link(provider, path_with_namespace) + url = __send__("#{provider}_project_url", path_with_namespace) - def gitea_project_link(path_with_namespace) - link_to path_with_namespace, gitea_project_url(path_with_namespace), target: '_blank' + link_to path_with_namespace, url, target: '_blank' end private diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml new file mode 100644 index 00000000000..f12f9482a51 --- /dev/null +++ b/app/views/import/_githubish_status.html.haml @@ -0,0 +1,61 @@ +- provider = local_assigns.fetch(:provider) +- provider_title = Gitlab::ImportSources.title(provider) + +%p.light + Select projects you want to import. +%hr +%p + = button_tag class: "btn btn-import btn-success js-import-all" do + Import all projects + = icon("spinner spin", class: "loading-icon") + +.table-responsive + %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col + %thead + %tr + %th= "From #{provider_title}" + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = provider_project_link(provider, project.import_source) + %td + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name + + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = provider_project_link(provider, repo.full_name) + %td.import-target + %fieldset.row + .input-group + .project-path.input-group-btn + - if current_user.can_select_namespace? + - selected = params[:namespace_id] || :current_user + - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {} + = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } + - else + = text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true + %span.input-group-addon / + = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true + %td.import-actions.job-status + = button_tag class: "btn btn-import js-add-to-import" do + Import + = icon("spinner spin", class: "loading-icon") + +.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}", import_path: "#{url_for([:import, provider])}" } } diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml index a7321632994..589ca27e45d 100644 --- a/app/views/import/gitea/status.html.haml +++ b/app/views/import/gitea/status.html.haml @@ -4,61 +4,4 @@ = custom_icon('go_logo') Import Projects from Gitea -%p.light - Select projects you want to import. -%hr -%p - = button_tag class: "btn btn-import btn-success js-import-all" do - Import all projects - = icon("spinner spin", class: "loading-icon") - -.table-responsive - %table.table.import-jobs - %colgroup.import-jobs-from-col - %colgroup.import-jobs-to-col - %colgroup.import-jobs-status-col - %thead - %tr - %th From Gitea - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = gitea_project_link(project.import_source) - %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name - - - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = gitea_project_link(repo.full_name) - %td.import-target - %fieldset.row - .input-group - .project-path.input-group-btn - - if current_user.can_select_namespace? - - selected = params[:namespace_id] || :current_user - - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {} - = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } - - else - = text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true - %span.input-group-addon / - = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true - %td.import-actions.job-status - = button_tag class: "btn btn-import js-add-to-import" do - Import - = icon("spinner spin", class: "loading-icon") - -.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitea_path}", import_path: "#{import_gitea_path}" } } += render 'import/githubish_status', provider: 'gitea' diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 70b18ee217d..0fe578a0036 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -3,61 +3,4 @@ %h3.page-title = icon 'github', text: 'Import Projects from GitHub' -%p.light - Select projects you want to import. -%hr -%p - = button_tag class: "btn btn-import btn-success js-import-all" do - Import all projects - = icon("spinner spin", class: "loading-icon") - -.table-responsive - %table.table.import-jobs - %colgroup.import-jobs-from-col - %colgroup.import-jobs-to-col - %colgroup.import-jobs-status-col - %thead - %tr - %th From GitHub - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = github_project_link(project.import_source) - %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name - - - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = github_project_link(repo.full_name) - %td.import-target - %fieldset.row - .input-group - .project-path.input-group-btn - - if current_user.can_select_namespace? - - selected = params[:namespace_id] || :current_user - - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {} - = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 } - - else - = text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true - %span.input-group-addon / - = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true - %td.import-actions.job-status - = button_tag class: "btn btn-import js-add-to-import" do - Import - = icon("spinner spin", class: "loading-icon") - -.js-importer-status{ data: { jobs_import_path: "#{jobs_import_github_path}", import_path: "#{import_github_path}" } } += render 'import/githubish_status', provider: 'github' diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb index 187b891b927..10f293cddf5 100644 --- a/spec/helpers/import_helper_spec.rb +++ b/spec/helpers/import_helper_spec.rb @@ -25,24 +25,37 @@ describe ImportHelper do end end - describe '#github_project_link' do - context 'when provider does not specify a custom URL' do - it 'uses default GitHub URL' do - allow(Gitlab.config.omniauth).to receive(:providers). + describe '#provider_project_link' do + context 'when provider is "github"' do + context 'when provider does not specify a custom URL' do + it 'uses default GitHub URL' do + allow(Gitlab.config.omniauth).to receive(:providers). and_return([Settingslogic.new('name' => 'github')]) - expect(helper.github_project_link('octocat/Hello-World')). + expect(helper.provider_project_link('github', 'octocat/Hello-World')). to include('href="https://github.com/octocat/Hello-World"') + end + end + + context 'when provider specify a custom URL' do + it 'uses custom URL' do + allow(Gitlab.config.omniauth).to receive(:providers). + and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')]) + + expect(helper.provider_project_link('github', 'octocat/Hello-World')). + to include('href="https://github.company.com/octocat/Hello-World"') + end end end - context 'when provider specify a custom URL' do - it 'uses custom URL' do - allow(Gitlab.config.omniauth).to receive(:providers). - and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')]) + context 'when provider is "gitea"' do + before do + assign(:gitea_host_url, 'https://try.gitea.io/') + end - expect(helper.github_project_link('octocat/Hello-World')). - to include('href="https://github.company.com/octocat/Hello-World"') + it 'uses given host' do + expect(helper.provider_project_link('gitea', 'octocat/Hello-World')). + to include('href="https://try.gitea.io/octocat/Hello-World"') end end end From e046e4c14d06a19cc30a679f4943c77b56ee6d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 17:43:34 +0100 Subject: [PATCH 327/386] Namespace access token session key in `Import::GithubController` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/controllers/import/gitea_controller.rb | 14 ++++++---- app/controllers/import/github_controller.rb | 18 +++++++----- .../import/gitea_controller_spec.rb | 2 +- .../import/github_controller_spec.rb | 2 +- ...ubish_import_controller_shared_examples.rb | 28 +++++++++++-------- spec/support/import_spec_helper.rb | 4 --- 6 files changed, 38 insertions(+), 30 deletions(-) diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb index 3bc21e62a1e..fbd851c64a7 100644 --- a/app/controllers/import/gitea_controller.rb +++ b/app/controllers/import/gitea_controller.rb @@ -1,22 +1,26 @@ class Import::GiteaController < Import::GithubController def new - if session[:access_token].present? && session[:host_url].present? + if session[access_token_key].present? && session[host_key].present? redirect_to status_import_url end end def personal_access_token - session[:host_url] = params[:gitea_host_url] + session[host_key] = params[host_key] super end def status - @gitea_host_url = session[:host_url] + @gitea_host_url = session[host_key] super end private + def host_key + :"#{provider}_host_url" + end + # Overriden methods def provider :gitea @@ -29,13 +33,13 @@ class Import::GiteaController < Import::GithubController end def provider_auth - if session[:access_token].blank? || session[:host_url].blank? + if session[access_token_key].blank? || session[host_key].blank? redirect_to new_import_gitea_url, alert: 'You need to specify both an Access Token and a Host URL.' end end def client_options - { host: session[:host_url], api_version: 'v1' } + { host: session[host_key], api_version: 'v1' } end end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 4ae121ec482..53a5981e564 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -7,18 +7,18 @@ class Import::GithubController < Import::BaseController def new if logged_in_with_provider? go_to_provider_for_permissions - elsif session[:access_token] + elsif session[access_token_key] redirect_to status_import_url end end def callback - session[:access_token] = client.get_token(params[:code]) + session[access_token_key] = client.get_token(params[:code]) redirect_to status_import_url end def personal_access_token - session[:access_token] = params[:personal_access_token] + session[access_token_key] = params[:personal_access_token] redirect_to status_import_url end @@ -52,7 +52,7 @@ class Import::GithubController < Import::BaseController private def client - @client ||= Gitlab::GithubImport::Client.new(session[:access_token], client_options) + @client ||= Gitlab::GithubImport::Client.new(session[access_token_key], client_options) end def verify_import_enabled @@ -80,13 +80,17 @@ class Import::GithubController < Import::BaseController end def provider_unauthorized - session[:access_token] = nil + session[access_token_key] = nil redirect_to new_import_url, alert: "Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account." end + def access_token_key + :"#{provider}_access_token" + end + def access_params - { github_access_token: session[:access_token] } + { github_access_token: session[access_token_key] } end # The following methods are overriden in subclasses @@ -99,7 +103,7 @@ class Import::GithubController < Import::BaseController end def provider_auth - if session[:access_token].blank? + if session[access_token_key].blank? go_to_provider_for_permissions end end diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb index 3643386ffbc..5ba64ab3eed 100644 --- a/spec/controllers/import/gitea_controller_spec.rb +++ b/spec/controllers/import/gitea_controller_spec.rb @@ -9,7 +9,7 @@ describe Import::GiteaController do include_context 'a GitHub-ish import controller' def assign_host_url - session[:host_url] = host_url + session[:gitea_host_url] = host_url end describe "GET new" do diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 55820a7cc65..95696e14b6c 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -29,7 +29,7 @@ describe Import::GithubController do get :callback - expect(session[:access_token]).to eq(token) + expect(session[:github_access_token]).to eq(token) expect(controller).to redirect_to(status_import_github_url) end end diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index e11ab802095..d0fd2d52004 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -4,6 +4,10 @@ # Note: You have access to `email_value` which is the email address value # being currently tested). +def assign_session_token(provider) + session[:"#{provider}_access_token"] = 'asdasd12345' +end + shared_examples 'a GitHub-ish import controller: POST personal_access_token' do let(:status_import_url) { public_send("status_import_#{provider}_url") } @@ -15,7 +19,7 @@ shared_examples 'a GitHub-ish import controller: POST personal_access_token' do post :personal_access_token, personal_access_token: token - expect(session[:access_token]).to eq(token) + expect(session[:"#{provider}_access_token"]).to eq(token) expect(controller).to redirect_to(status_import_url) end end @@ -24,7 +28,7 @@ shared_examples 'a GitHub-ish import controller: GET new' do let(:status_import_url) { public_send("status_import_#{provider}_url") } it "redirects to status if we already have a token" do - assign_session_token + assign_session_token(provider) allow(controller).to receive(:logged_in_with_provider?).and_return(false) get :new @@ -48,7 +52,7 @@ shared_examples 'a GitHub-ish import controller: GET status' do let(:extra_assign_expectations) { {} } before do - assign_session_token + assign_session_token(provider) end it "assigns variables" do @@ -80,7 +84,7 @@ shared_examples 'a GitHub-ish import controller: GET status' do get :status - expect(session[:access_token]).to eq(nil) + expect(session[:"#{provider}_access_token"]).to be_nil expect(controller).to redirect_to(new_import_url) expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.") end @@ -100,11 +104,11 @@ shared_examples 'a GitHub-ish import controller: POST create' do before do stub_client(user: provider_user, repo: provider_repo) - assign_session_token + assign_session_token(provider) end - context "when the repository owner is the Gitea user" do - context "when the Gitea user and GitLab user's usernames match" do + context "when the repository owner is the provider user" do + context "when the provider user and GitLab user's usernames match" do it "takes the current user's namespace" do expect(Gitlab::GithubImport::ProjectCreator). to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). @@ -114,7 +118,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context "when the Gitea user and GitLab user's usernames don't match" do + context "when the provider user and GitLab user's usernames don't match" do let(:provider_username) { "someone_else" } it "takes the current user's namespace" do @@ -127,15 +131,15 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context "when the repository owner is not the Gitea user" do + context "when the repository owner is not the provider user" do let(:other_username) { "someone_else" } before do provider_repo.owner = OpenStruct.new(login: other_username) - assign_session_token + assign_session_token(provider) end - context "when a namespace with the Gitea user's username already exists" do + context "when a namespace with the provider user's username already exists" do let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) } context "when the namespace is owned by the GitLab user" do @@ -164,7 +168,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context "when a namespace with the Gitea user's username doesn't exist" do + context "when a namespace with the provider user's username doesn't exist" do context "when current user can create namespaces" do it "creates the namespace" do expect(Gitlab::GithubImport::ProjectCreator). diff --git a/spec/support/import_spec_helper.rb b/spec/support/import_spec_helper.rb index cd25e05ac4b..6710962f082 100644 --- a/spec/support/import_spec_helper.rb +++ b/spec/support/import_spec_helper.rb @@ -30,8 +30,4 @@ module ImportSpecHelper ) allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider]) end - - def assign_session_token - session[:access_token] = 'asdasd12345' - end end From ab06313c36fc5856b2472d3dfcb966a8c6341d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 17:44:22 +0100 Subject: [PATCH 328/386] Add Project#gitea_import? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/project.rb | 4 ++++ lib/gitlab/github_import/importer.rb | 6 +++--- lib/gitlab/github_import/milestone_formatter.rb | 2 +- spec/lib/gitlab/github_import/importer_spec.rb | 2 +- spec/models/project_spec.rb | 12 ++++++++++++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 5d5d6737dad..0ba8fca24d5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -533,6 +533,10 @@ class Project < ActiveRecord::Base import_type == 'gitlab_project' end + def gitea_import? + import_type == 'gitea' + end + def check_limit unless creator.can_create_project? or namespace.kind == 'group' projects_limit = creator.projects_limit diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index c53cd1a928d..ec1318ab33c 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -22,7 +22,7 @@ module Gitlab opts = {} # Gitea plan to be GitHub compliant - if project.import_type == 'gitea' + if project.gitea_import? uri = URI.parse(project.import_url) host = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}".sub(%r{/?[\w-]+/[\w-]+\.git\z}, '') opts = { @@ -53,7 +53,7 @@ module Gitlab # Gitea doesn't have a Release API yet # See https://github.com/go-gitea/gitea/issues/330 - unless project.import_type == 'gitea' + unless project.gitea_import? import_releases end @@ -141,7 +141,7 @@ module Gitlab merge_request = gh_pull_request.create! # Gitea doesn't return PR in the Issue API endpoint, so labels must be assigned at this stage - if project.import_type == 'gitea' + if project.gitea_import? apply_labels(merge_request, raw) end rescue => e diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb index fe172b854d5..dd782eff059 100644 --- a/lib/gitlab/github_import/milestone_formatter.rb +++ b/lib/gitlab/github_import/milestone_formatter.rb @@ -23,7 +23,7 @@ module Gitlab end def number - if project.import_type == 'gitea' + if project.gitea_import? raw_data.id else raw_data.number diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index 0a03b7353f6..72421832ffc 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -192,7 +192,7 @@ describe Gitlab::GithubImport::Importer, lib: true do ] } - unless project.import_type == 'gitea' + unless project.gitea_import? error[:errors] << { type: :release, url: "#{api_root}/repos/octocat/Hello-World/releases/2", errors: "Validation failed: Description can't be blank" } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ed6b2c6a22b..8779b399344 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1458,6 +1458,18 @@ describe Project, models: true do end end + describe '#gitlab_project_import?' do + subject(:project) { build(:project, import_type: 'gitlab_project') } + + it { expect(project.gitlab_project_import?).to be true } + end + + describe '#gitea_import?' do + subject(:project) { build(:project, import_type: 'gitea') } + + it { expect(project.gitea_import?).to be true } + end + describe '#lfs_enabled?' do let(:project) { create(:project) } From a0c48ec62b64f3b836d8af04c46938d6951911ef Mon Sep 17 00:00:00 2001 From: jurre Date: Tue, 13 Dec 2016 16:22:18 +0100 Subject: [PATCH 329/386] Move Admin Appearance spinach feature to rspec --- features/admin/appearance.feature | 37 ---------- features/steps/admin/appearance.rb | 72 ------------------- features/steps/shared/paths.rb | 4 -- spec/features/admin/admin_appearance_spec.rb | 76 ++++++++++++++++++++ 4 files changed, 76 insertions(+), 113 deletions(-) delete mode 100644 features/admin/appearance.feature delete mode 100644 features/steps/admin/appearance.rb create mode 100644 spec/features/admin/admin_appearance_spec.rb diff --git a/features/admin/appearance.feature b/features/admin/appearance.feature deleted file mode 100644 index 5c1dd7531c1..00000000000 --- a/features/admin/appearance.feature +++ /dev/null @@ -1,37 +0,0 @@ -Feature: Admin Appearance - Scenario: Create new appearance - Given I sign in as an admin - And I visit admin appearance page - When submit form with new appearance - Then I should be redirected to admin appearance page - And I should see newly created appearance - - Scenario: Preview appearance - Given application has custom appearance - And I sign in as an admin - When I visit admin appearance page - And I click preview button - Then I should see a customized appearance - - Scenario: Custom sign-in page - Given application has custom appearance - When I visit login page - Then I should see a customized appearance - - Scenario: Appearance logo - Given application has custom appearance - And I sign in as an admin - And I visit admin appearance page - When I attach a logo - Then I should see a logo - And I remove the logo - Then I should see logo removed - - Scenario: Header logos - Given application has custom appearance - And I sign in as an admin - And I visit admin appearance page - When I attach header logos - Then I should see header logos - And I remove the header logos - Then I should see header logos removed diff --git a/features/steps/admin/appearance.rb b/features/steps/admin/appearance.rb deleted file mode 100644 index 0d1be46d11d..00000000000 --- a/features/steps/admin/appearance.rb +++ /dev/null @@ -1,72 +0,0 @@ -class Spinach::Features::AdminAppearance < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - - step 'submit form with new appearance' do - fill_in 'appearance_title', with: 'MyCompany' - fill_in 'appearance_description', with: 'dev server' - click_button 'Save' - end - - step 'I should be redirected to admin appearance page' do - expect(current_path).to eq admin_appearances_path - expect(page).to have_content 'Appearance settings' - end - - step 'I should see newly created appearance' do - expect(page).to have_field('appearance_title', with: 'MyCompany') - expect(page).to have_field('appearance_description', with: 'dev server') - expect(page).to have_content 'Last edit' - end - - step 'I click preview button' do - click_link "Preview" - end - - step 'application has custom appearance' do - create(:appearance) - end - - step 'I should see a customized appearance' do - expect(page).to have_content appearance.title - expect(page).to have_content appearance.description - end - - step 'I attach a logo' do - attach_file(:appearance_logo, Rails.root.join('spec', 'fixtures', 'dk.png')) - click_button 'Save' - end - - step 'I attach header logos' do - attach_file(:appearance_header_logo, Rails.root.join('spec', 'fixtures', 'dk.png')) - click_button 'Save' - end - - step 'I should see a logo' do - expect(page).to have_xpath('//img[@src="/uploads/appearance/logo/1/dk.png"]') - end - - step 'I should see header logos' do - expect(page).to have_xpath('//img[@src="/uploads/appearance/header_logo/1/dk.png"]') - end - - step 'I remove the logo' do - click_link 'Remove logo' - end - - step 'I remove the header logos' do - click_link 'Remove header logo' - end - - step 'I should see logo removed' do - expect(page).not_to have_xpath('//img[@src="/uploads/appearance/logo/1/gitlab_logo.png"]') - end - - step 'I should see header logos removed' do - expect(page).not_to have_xpath('//img[@src="/uploads/appearance/header_logo/1/header_logo_light.png"]') - end - - def appearance - Appearance.last - end -end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index a78d0a775ba..15b81fa529b 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -195,10 +195,6 @@ module SharedPaths visit admin_groups_path end - step 'I visit admin appearance page' do - visit admin_appearances_path - end - step 'I visit admin teams page' do visit admin_teams_path end diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb new file mode 100644 index 00000000000..96d715ef383 --- /dev/null +++ b/spec/features/admin/admin_appearance_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +feature 'Admin Appearance', feature: true do + let!(:appearance) { create(:appearance) } + + scenario 'Create new appearance' do + login_as :admin + visit admin_appearances_path + + fill_in 'appearance_title', with: 'MyCompany' + fill_in 'appearance_description', with: 'dev server' + click_button 'Save' + + expect(current_path).to eq admin_appearances_path + expect(page).to have_content 'Appearance settings' + + expect(page).to have_field('appearance_title', with: 'MyCompany') + expect(page).to have_field('appearance_description', with: 'dev server') + expect(page).to have_content 'Last edit' + end + + scenario 'Preview appearance' do + login_as :admin + + visit admin_appearances_path + click_link "Preview" + + expect_page_has_custom_appearance(appearance) + end + + scenario 'Custom sign-in page' do + visit new_user_session_path + expect_page_has_custom_appearance(appearance) + end + + scenario 'Appearance logo' do + login_as :admin + visit admin_appearances_path + + attach_file(:appearance_logo, logo_fixture) + click_button 'Save' + expect(page).to have_css(logo_selector) + + click_link 'Remove logo' + expect(page).not_to have_css(logo_selector) + end + + scenario 'Header logos' do + login_as :admin + visit admin_appearances_path + + attach_file(:appearance_header_logo, logo_fixture) + click_button 'Save' + expect(page).to have_css(header_logo_selector) + + click_link 'Remove header logo' + expect(page).not_to have_css(header_logo_selector) + end + + def expect_page_has_custom_appearance(appearance) + expect(page).to have_content appearance.title + expect(page).to have_content appearance.description + end + + def logo_selector + '//img[@src^="/uploads/appearance/logo"]' + end + + def header_logo_selector + '//img[@src^="/uploads/appearance/header_logo"]' + end + + def logo_fixture + Rails.root.join('spec', 'fixtures', 'dk.png') + end +end From bf6b1078521b6461caeffb3a0ba003c5c5ac2a9e Mon Sep 17 00:00:00 2001 From: jurre Date: Tue, 13 Dec 2016 17:52:58 +0100 Subject: [PATCH 330/386] Move admin broadcast messages spinach feature to rspec --- features/admin/broadcast_messages.feature | 33 ---------- features/steps/admin/broadcast_messages.rb | 66 ------------------- .../admin/admin_broadcast_messages_spec.rb | 51 ++++++++++++++ 3 files changed, 51 insertions(+), 99 deletions(-) delete mode 100644 features/admin/broadcast_messages.feature delete mode 100644 features/steps/admin/broadcast_messages.rb create mode 100644 spec/features/admin/admin_broadcast_messages_spec.rb diff --git a/features/admin/broadcast_messages.feature b/features/admin/broadcast_messages.feature deleted file mode 100644 index 4f9c651561e..00000000000 --- a/features/admin/broadcast_messages.feature +++ /dev/null @@ -1,33 +0,0 @@ -@admin -Feature: Admin Broadcast Messages - Background: - Given I sign in as an admin - And application already has a broadcast message - And I visit admin messages page - - Scenario: See broadcast messages list - Then I should see all broadcast messages - - Scenario: Create a customized broadcast message - When submit form with new customized broadcast message - Then I should be redirected to admin messages page - And I should see newly created broadcast message - Then I visit dashboard page - And I should see a customized broadcast message - - Scenario: Edit an existing broadcast message - When I edit an existing broadcast message - And I change the broadcast message text - Then I should be redirected to admin messages page - And I should see the updated broadcast message - - Scenario: Remove an existing broadcast message - When I remove an existing broadcast message - Then I should be redirected to admin messages page - And I should not see the removed broadcast message - - @javascript - Scenario: Live preview a customized broadcast message - When I visit admin messages page - And I enter a broadcast message with Markdown - Then I should see a live preview of the rendered broadcast message diff --git a/features/steps/admin/broadcast_messages.rb b/features/steps/admin/broadcast_messages.rb deleted file mode 100644 index af2b4a29313..00000000000 --- a/features/steps/admin/broadcast_messages.rb +++ /dev/null @@ -1,66 +0,0 @@ -class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - - step 'application already has a broadcast message' do - FactoryGirl.create(:broadcast_message, :expired, message: "Migration to new server") - end - - step 'I should see all broadcast messages' do - expect(page).to have_content "Migration to new server" - end - - step 'I should be redirected to admin messages page' do - expect(current_path).to eq admin_broadcast_messages_path - end - - step 'I should see newly created broadcast message' do - expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' - end - - step 'submit form with new customized broadcast message' do - fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' - fill_in 'broadcast_message_color', with: '#f2dede' - fill_in 'broadcast_message_font', with: '#b94a48' - select Date.today.next_year.year, from: "broadcast_message_ends_at_1i" - click_button "Add broadcast message" - end - - step 'I should see a customized broadcast message' do - expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' - expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' - expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) - end - - step 'I edit an existing broadcast message' do - click_link 'Edit' - end - - step 'I change the broadcast message text' do - fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW' - click_button 'Update broadcast message' - end - - step 'I should see the updated broadcast message' do - expect(page).to have_content "Application update RIGHT NOW" - end - - step 'I remove an existing broadcast message' do - click_link 'Remove' - end - - step 'I should not see the removed broadcast message' do - expect(page).not_to have_content 'Migration to new server' - end - - step 'I enter a broadcast message with Markdown' do - fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" - end - - step 'I should see a live preview of the rendered broadcast message' do - page.within('.broadcast-message-preview') do - expect(page).to have_selector('strong', text: 'Markdown') - expect(page).to have_selector('img.emoji') - end - end -end diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb new file mode 100644 index 00000000000..bc957ec72e1 --- /dev/null +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +feature 'Admin Broadcast Messages', feature: true do + before do + login_as :admin + create(:broadcast_message, :expired, message: 'Migration to new server') + visit admin_broadcast_messages_path + end + + scenario 'See broadcast messages list' do + expect(page).to have_content 'Migration to new server' + end + + scenario 'Create a customized broadcast message' do + fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' + fill_in 'broadcast_message_color', with: '#f2dede' + fill_in 'broadcast_message_font', with: '#b94a48' + select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' + click_button 'Add broadcast message' + + expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' + expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' + expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) + end + + scenario 'Edit an existing broadcast message' do + click_link 'Edit' + fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW' + click_button 'Update broadcast message' + + expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_content 'Application update RIGHT NOW' + end + + scenario 'Remove an existing broadcast message' do + click_link 'Remove' + + expect(current_path).to eq admin_broadcast_messages_path + expect(page).not_to have_content 'Migration to new server' + end + + scenario 'Live preview a customized broadcast message', js: true do + fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" + + page.within('.broadcast-message-preview') do + expect(page).to have_selector('strong', text: 'Markdown') + expect(page).to have_selector('img.emoji') + end + end +end From b1613e54894fdf9176df338f0c1e162075dc80ae Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Dec 2016 17:47:45 +0000 Subject: [PATCH 331/386] Makes API call when stage is clicked --- app/assets/javascripts/dispatcher.js.es6 | 15 ++++ .../mini_pipeline_graph_dropdown.js.es6 | 69 +++++++++++++++++++ .../projects/ci/pipelines/_pipeline.html.haml | 10 +-- .../projects/commit/_pipelines_list.haml | 2 +- app/views/projects/pipelines/_stage.html.haml | 19 ++--- app/views/projects/pipelines/index.html.haml | 2 +- 6 files changed, 95 insertions(+), 22 deletions(-) create mode 100644 app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 1e259a16f06..752f35e6356 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -141,6 +141,11 @@ case 'projects:merge_requests:builds': new MergedButtons(); break; + case 'projects:merge_requests:pipelines': + new gl.MiniPipelineGraph({ + container: '.js-pipeline-table', + }); + break; case "projects:merge_requests:diffs": new gl.Diff(); new ZenMode(); @@ -158,6 +163,11 @@ new ZenMode(); shortcut_handler = new ShortcutsNavigation(); break; + case 'projects:commit:pipelines': + new gl.MiniPipelineGraph({ + container: '.js-pipeline-table', + }); + break; case 'projects:commit:builds': new gl.Pipelines(); break; @@ -172,6 +182,11 @@ new TreeView(); } break; + case 'projects:pipelines:index': + new gl.MiniPipelineGraph({ + container: '.js-pipeline-table', + }); + break; case 'projects:pipelines:builds': case 'projects:pipelines:show': const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 new file mode 100644 index 00000000000..ce24cbdb705 --- /dev/null +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 @@ -0,0 +1,69 @@ +/* global Flash */ + +/** + * In each pipelines table we have a mini pipeline graph for each pipeline. + * + * When we click in a pipeline stage, we need to make an API call to get the + * builds list to render in a dropdown. + * + * The container should be the table element. + * + * The stage icon clicked needs to have the following HTML structure: + *

    + */ +(() => { + class MiniPipelineGraph { + constructor({ container }) { + this.container = container; + this.getBuildsList = this.getBuildsList.bind(this); + + this.bindEvents(); + } + + /** + * Adds an removes the event listener. + * TODO: Remove jQuery when we have a way to handle events properly. + */ + bindEvents() { + $(this.container).off('click', 'button.js-builds-dropdown-button', this.getBuildsList); + $(this.container).on('click', 'button.js-builds-dropdown-button', this.getBuildsList); + } + + /** + * For the clicked stage, renders the received html in the sibiling + * element with the `js-builds-dropdown-container` clas + * + * @param {Element} stageContainer + * @param {Object} data + */ + renderBuildsList(stageContainer, data) { + const dropdownContainer = stageContainer.parentElement.querySelector('.js-builds-dropdown-container'); + + dropdownContainer.innerHTML = data.html; + } + + /** + * For the clicked stage, gets the list of builds. + * + * @param {Object} e + * @return {Promise} + */ + getBuildsList(e) { + const endpoint = e.currentTarget.dataset.stageEndpoint; + + return $.ajax({ + dataType: 'json', + type: 'GET', + url: endpoint, + success: data => this.renderBuildsList(e.currentTarget, data), + error: () => new Flash('An error occurred while fetching the builds.', 'alert'), + }); + } + } + + window.gl = window.gl || {}; + window.gl.MiniPipelineGraph = MiniPipelineGraph; +})(); diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 74ad9557130..d488eeda2fe 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -53,17 +53,13 @@ .stage-container.mini-pipeline-graph - if hasMultipleBuilds .dropdown.inline.build-content - %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } + %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} %span{ class: klass } %span.mini-pipeline-graph-icon-container %span{ class: icon_status_klass }= custom_icon(icon_status) = icon('caret-down', class: 'dropdown-caret') - .dropdown-menu.grouped-pipeline-dropdown - .arrow-up - %ul - - stage.statuses.each do |status| - %li.dropdown-build - = render 'ci/status/graph_badge', subject: status + + %div.js-builds-dropdown-container - else - if detailed_status.has_details? = link_to detailed_status.details_path, class: klass, title: tooltip do diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index 0c2f45c6035..5a9f7295135 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -4,7 +4,7 @@ .nothing-here-block No pipelines to show - else .table-holder - %table.table.ci-table + %table.table.ci-table.js-pipeline-table %thead %th.pipeline-status Status %th.pipeline-info Pipeline diff --git a/app/views/projects/pipelines/_stage.html.haml b/app/views/projects/pipelines/_stage.html.haml index 44533b77eba..83fd518726d 100644 --- a/app/views/projects/pipelines/_stage.html.haml +++ b/app/views/projects/pipelines/_stage.html.haml @@ -1,13 +1,6 @@ -- detailed_status = @stage.detailed_status(current_user) -- klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status}" -- hasMultipleBuilds = @stage.statuses.count > 1 -- icon_status = "#{detailed_status.icon}_borderless" -- icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status}" -- tooltip = "#{@stage.name}: #{detailed_status.label || 'not found'}" - -.dropdown.inline.build-content - %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } - %span{ class: klass } - %span.mini-pipeline-graph-icon-container - %span{ class: icon_status_klass }= custom_icon(icon_status) - = icon('caret-down', class: 'dropdown-caret') +.dropdown-menu.grouped-pipeline-dropdown + .arrow-up + %ul + - @stage.statuses.each do |status| + %li.dropdown-build + = render 'ci/status/graph_badge', subject: status diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 4d009871f0d..28026ccf861 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -42,7 +42,7 @@ .nothing-here-block No pipelines to show - else .table-holder - %table.table.ci-table + %table.table.ci-table.js-pipeline-table %thead %th.pipeline-status Status %th.pipeline-info Pipeline From 2e6c1720ead0f2843abb0d03f0c01b92fa063980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 18:21:58 +0100 Subject: [PATCH 332/386] Allow Repositories API GET endpoints to be requested anonymously MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../4269-public-repositories-api.yml | 4 + doc/api/repositories.md | 18 +- lib/api/repositories.rb | 6 - spec/requests/api/repositories_spec.rb | 282 ++++++++++++------ 4 files changed, 214 insertions(+), 96 deletions(-) create mode 100644 changelogs/unreleased/4269-public-repositories-api.yml diff --git a/changelogs/unreleased/4269-public-repositories-api.yml b/changelogs/unreleased/4269-public-repositories-api.yml new file mode 100644 index 00000000000..b88ce63845d --- /dev/null +++ b/changelogs/unreleased/4269-public-repositories-api.yml @@ -0,0 +1,4 @@ +--- +title: Allow Repositories API GET endpoints to be requested anonymously +merge_request: +author: diff --git a/doc/api/repositories.md b/doc/api/repositories.md index bcf8b955044..727617f1ecc 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -2,7 +2,8 @@ ## List repository tree -Get a list of repository files and directories in a project. +Get a list of repository files and directories in a project. This endpoint can +be accessed without authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/tree @@ -71,7 +72,8 @@ Parameters: ## Raw file content -Get the raw file contents for a file by commit SHA and path. +Get the raw file contents for a file by commit SHA and path. This endpoint can +be accessed without authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/blobs/:sha @@ -85,7 +87,8 @@ Parameters: ## Raw blob content -Get the raw file contents for a blob by blob SHA. +Get the raw file contents for a blob by blob SHA. This endpoint can be accessed +without authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/raw_blobs/:sha @@ -98,7 +101,8 @@ Parameters: ## Get file archive -Get an archive of the repository +Get an archive of the repository. This endpoint can be accessed without +authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/archive @@ -111,6 +115,9 @@ Parameters: ## Compare branches, tags or commits +This endpoint can be accessed without authentication if the repository is +publicly accessible. + ``` GET /projects/:id/repository/compare ``` @@ -163,7 +170,8 @@ Response: ## Contributors -Get repository contributors list +Get repository contributors list. This endpoint can be accessed without +authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/contributors diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index c287ee34a68..4ca6646a6f1 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -2,7 +2,6 @@ require 'mime/types' module API class Repositories < Grape::API - before { authenticate! } before { authorize! :download_code, user_project } params do @@ -79,8 +78,6 @@ module API optional :format, type: String, desc: 'The archive format' end get ':id/repository/archive', requirements: { format: Gitlab::Regex.archive_formats_regex } do - authorize! :download_code, user_project - begin send_git_archive user_project.repository, ref: params[:sha], format: params[:format] rescue @@ -96,7 +93,6 @@ module API requires :to, type: String, desc: 'The commit, branch name, or tag name to stop comparison' end get ':id/repository/compare' do - authorize! :download_code, user_project compare = Gitlab::Git::Compare.new(user_project.repository.raw_repository, params[:from], params[:to]) present compare, with: Entities::Compare end @@ -105,8 +101,6 @@ module API success Entities::Contributor end get ':id/repository/contributors' do - authorize! :download_code, user_project - begin present user_project.repository.contributors, with: Entities::Contributor diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index c90b69e8ebb..67f0bc537fe 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -16,15 +16,32 @@ describe API::Repositories, api: true do context "authorized user" do before { project.team << [user2, :reporter] } - it "returns project commits" do - get api("/projects/#{project.id}/repository/tree", user) + shared_examples_for 'repository tree' do + it 'returns the repository tree' do + get api("/projects/#{project.id}/repository/tree", current_user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first['name']).to eq('bar') - expect(json_response.first['type']).to eq('tree') - expect(json_response.first['mode']).to eq('040000') + first_commit = json_response.first + + expect(json_response).to be_an Array + expect(first_commit['name']).to eq('bar') + expect(first_commit['type']).to eq('tree') + expect(first_commit['mode']).to eq('040000') + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository tree' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository tree' do + let(:current_user) { user } + end end it 'returns a 404 for unknown ref' do @@ -39,7 +56,8 @@ describe API::Repositories, api: true do context "unauthorized user" do it "does not return project commits" do get api("/projects/#{project.id}/repository/tree") - expect(response).to have_http_status(401) + + expect(response).to have_http_status(404) end end end @@ -72,15 +90,38 @@ describe API::Repositories, api: true do context "unauthorized user" do it "does not return project commits" do get api("/projects/#{project.id}/repository/tree?recursive=1") - expect(response).to have_http_status(401) + + expect(response).to have_http_status(404) end end end - describe "GET /projects/:id/repository/blobs/:sha" do - it "gets the raw file contents" do - get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user) - expect(response).to have_http_status(200) + describe "GET /projects/:id/repository/blobs/:sha & /projects/:id/repository/commits/:sha" do + shared_examples_for 'repository blob' do + it 'returns the repository blob for /repository/blobs/master' do + get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", current_user) + + expect(response).to have_http_status(200) + end + + it 'returns the repository blob for /repository/commits/master' do + get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", current_user) + + expect(response).to have_http_status(200) + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository blob' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository blob' do + let(:current_user) { user } + end end it "returns 404 for invalid branch_name" do @@ -99,17 +140,26 @@ describe API::Repositories, api: true do end end - describe "GET /projects/:id/repository/commits/:sha/blob" do - it "gets the raw file contents" do - get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) - expect(response).to have_http_status(200) - end - end - describe "GET /projects/:id/repository/raw_blobs/:sha" do - it "gets the raw file contents" do - get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user) - expect(response).to have_http_status(200) + shared_examples_for 'repository raw blob' do + it 'returns the repository raw blob' do + get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", current_user) + + expect(response).to have_http_status(200) + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository raw blob' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository raw blob' do + let(:current_user) { user } + end end it 'returns a 404 for unknown blob' do @@ -122,31 +172,55 @@ describe API::Repositories, api: true do end describe "GET /projects/:id/repository/archive(.:format)?:sha" do - it "gets the archive" do - get api("/projects/#{project.id}/repository/archive", user) - repo_name = project.repository.name.gsub("\.git", "") - expect(response).to have_http_status(200) - type, params = workhorse_send_data - expect(type).to eq('git-archive') - expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/) + shared_examples_for 'repository archive' do + it 'returns the repository archive' do + get api("/projects/#{project.id}/repository/archive", current_user) + + expect(response).to have_http_status(200) + + repo_name = project.repository.name.gsub("\.git", "") + type, params = workhorse_send_data + + expect(type).to eq('git-archive') + expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/) + end + + it 'returns the repository archive archive.zip' do + get api("/projects/#{project.id}/repository/archive.zip", user) + + expect(response).to have_http_status(200) + + repo_name = project.repository.name.gsub("\.git", "") + type, params = workhorse_send_data + + expect(type).to eq('git-archive') + expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/) + end + + it 'returns the repository archive archive.tar.bz2' do + get api("/projects/#{project.id}/repository/archive.tar.bz2", user) + + expect(response).to have_http_status(200) + + repo_name = project.repository.name.gsub("\.git", "") + type, params = workhorse_send_data + + expect(type).to eq('git-archive') + expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) + end end - it "gets the archive.zip" do - get api("/projects/#{project.id}/repository/archive.zip", user) - repo_name = project.repository.name.gsub("\.git", "") - expect(response).to have_http_status(200) - type, params = workhorse_send_data - expect(type).to eq('git-archive') - expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/) + context 'when unauthenticated' do + it_behaves_like 'repository archive' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end end - it "gets the archive.tar.bz2" do - get api("/projects/#{project.id}/repository/archive.tar.bz2", user) - repo_name = project.repository.name.gsub("\.git", "") - expect(response).to have_http_status(200) - type, params = workhorse_send_data - expect(type).to eq('git-archive') - expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) + context 'when authenticated' do + it_behaves_like 'repository archive' do + let(:current_user) { user } + end end it "returns 404 for invalid sha" do @@ -156,55 +230,93 @@ describe API::Repositories, api: true do end describe 'GET /projects/:id/repository/compare' do - it "compares branches" do - get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'feature' - expect(response).to have_http_status(200) - expect(json_response['commits']).to be_present - expect(json_response['diffs']).to be_present + shared_examples_for 'repository compare' do + it "compares branches" do + get api("/projects/#{project.id}/repository/compare", current_user), from: 'master', to: 'feature' + + expect(response).to have_http_status(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present + end + + it "compares tags" do + get api("/projects/#{project.id}/repository/compare", current_user), from: 'v1.0.0', to: 'v1.1.0' + + expect(response).to have_http_status(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present + end + + it "compares commits" do + get api("/projects/#{project.id}/repository/compare", current_user), from: sample_commit.id, to: sample_commit.parent_id + + expect(response).to have_http_status(200) + expect(json_response['commits']).to be_empty + expect(json_response['diffs']).to be_empty + expect(json_response['compare_same_ref']).to be_falsey + end + + it "compares commits in reverse order" do + get api("/projects/#{project.id}/repository/compare", current_user), from: sample_commit.parent_id, to: sample_commit.id + + expect(response).to have_http_status(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present + end + + it "compares same refs" do + get api("/projects/#{project.id}/repository/compare", current_user), from: 'master', to: 'master' + + expect(response).to have_http_status(200) + expect(json_response['commits']).to be_empty + expect(json_response['diffs']).to be_empty + expect(json_response['compare_same_ref']).to be_truthy + end end - it "compares tags" do - get api("/projects/#{project.id}/repository/compare", user), from: 'v1.0.0', to: 'v1.1.0' - expect(response).to have_http_status(200) - expect(json_response['commits']).to be_present - expect(json_response['diffs']).to be_present + context 'when unauthenticated' do + it_behaves_like 'repository compare' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end end - it "compares commits" do - get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.id, to: sample_commit.parent_id - expect(response).to have_http_status(200) - expect(json_response['commits']).to be_empty - expect(json_response['diffs']).to be_empty - expect(json_response['compare_same_ref']).to be_falsey - end - - it "compares commits in reverse order" do - get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.parent_id, to: sample_commit.id - expect(response).to have_http_status(200) - expect(json_response['commits']).to be_present - expect(json_response['diffs']).to be_present - end - - it "compares same refs" do - get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'master' - expect(response).to have_http_status(200) - expect(json_response['commits']).to be_empty - expect(json_response['diffs']).to be_empty - expect(json_response['compare_same_ref']).to be_truthy + context 'when authenticated' do + it_behaves_like 'repository compare' do + let(:current_user) { user } + end end end describe 'GET /projects/:id/repository/contributors' do - it 'returns valid data' do - get api("/projects/#{project.id}/repository/contributors", user) - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - contributor = json_response.first - expect(contributor['email']).to eq('tiagonbotelho@hotmail.com') - expect(contributor['name']).to eq('tiagonbotelho') - expect(contributor['commits']).to eq(1) - expect(contributor['additions']).to eq(0) - expect(contributor['deletions']).to eq(0) + shared_examples_for 'repository contributors' do + it 'returns valid data' do + get api("/projects/#{project.id}/repository/contributors", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + + first_contributor = json_response.first + + expect(first_contributor['email']).to eq('tiagonbotelho@hotmail.com') + expect(first_contributor['name']).to eq('tiagonbotelho') + expect(first_contributor['commits']).to eq(1) + expect(first_contributor['additions']).to eq(0) + expect(first_contributor['deletions']).to eq(0) + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository contributors' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository contributors' do + let(:current_user) { user } + end end end end From 66ff2dede173c9918c72d381468f2d9f5b39ab86 Mon Sep 17 00:00:00 2001 From: Mitchell Hentges Date: Mon, 19 Dec 2016 01:34:45 -0800 Subject: [PATCH 333/386] Make CI badge hitboxes better match container --- app/assets/stylesheets/pages/pipelines.scss | 74 +++++++++---------- app/views/ci/status/_graph_badge.html.haml | 9 ++- app/views/projects/stage/_graph.html.haml | 11 ++- .../projects/stage/_in_stage_group.html.haml | 4 +- .../unreleased/pipeline-build-hitbox.yml | 4 + .../projects/pipelines/pipeline_spec.rb | 33 ++++----- .../fixtures/pipeline_graph.html.haml | 9 +-- 7 files changed, 68 insertions(+), 76 deletions(-) create mode 100644 changelogs/unreleased/pipeline-build-hitbox.yml diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index be22e7bdc79..243c9153ded 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -428,7 +428,7 @@ width: 21px; height: 25px; position: absolute; - top: -32px; + top: -31px; border-top: 2px solid $border-color; } @@ -456,32 +456,31 @@ } .build { - border: 1px solid $border-color; - border-radius: 30px; - background-color: $white-light; position: relative; - padding: 8px 4px 9px 10px; width: 186px; margin-bottom: 10px; white-space: normal; + color: $gl-text-color-light; - &:hover { - background-color: $stage-hover-bg; - border: 1px solid $stage-hover-border; + > .build-content { + display: inline-block; + padding: 8px 10px 9px; + width: 100%; + border: 1px solid $border-color; + border-radius: 30px; + background-color: $white-light; - a, - .dropdown-counter-badge, - .dropdown-menu-toggle { + &:hover { + background-color: $stage-hover-bg; + border: 1px solid $stage-hover-border; color: $gl-text-color; } + } - .grouped-pipeline-dropdown a { - color: $gl-text-color-light; - - &:hover { - color: $gl-text-color; - } - } + > .ci-action-icon-container { + position: absolute; + right: 4px; + top: 5px; } .ci-status-icon { @@ -621,8 +620,8 @@ padding: 0; width: 191px; left: auto; - right: -206px; - top: -11px; + right: -195px; + top: -4px; box-shadow: 0 1px 5px $black-transparent; a { @@ -650,17 +649,25 @@ .dropdown-build { color: $gl-text-color-light; - a.ci-action-icon-container { - padding: 0; + .build-content { + width: 100%; + } + + .ci-action-icon-container { font-size: 11px; - float: right; - margin-top: 4px; - display: inline-block; - position: relative; + position: absolute; + right: 4px; i { + width: 25px; + height: 25px; font-size: 11px; margin-top: 0; + + &::before { + top: 1px; + left: 1px; + } } } @@ -670,18 +677,6 @@ color: $gl-text-color; } - .ci-action-icon-container { - i { - width: 25px; - height: 25px; - - &::before { - top: 1px; - left: 1px; - } - } - } - .stage { max-width: 100px; width: 100px; @@ -704,9 +699,6 @@ // Action Icons .ci-action-icon-container .ci-action-icon-wrapper { - float: right; - margin-top: -4px; - i { color: $border-color; border-radius: 100%; diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml index 9f3a9c0c6b2..52b4d77d074 100644 --- a/app/views/ci/status/_graph_badge.html.haml +++ b/app/views/ci/status/_graph_badge.html.haml @@ -5,12 +5,13 @@ - klass = "ci-status-icon ci-status-icon-#{status.group}" - if status.has_details? - = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status.label}" } do + = link_to status.details_path, class: 'build-content' do %span{ class: klass }= custom_icon(status.icon) - .ci-status-text= subject.name + .ci-status-text{ 'data-toggle' => 'tooltip', 'data-title' => "#{subject.name} - #{status.label}" }= subject.name - else - %span{ class: klass }= custom_icon(status.icon) - .ci-status-text= subject.name + .build-content + %span{ class: klass }= custom_icon(status.icon) + .ci-status-text{ 'data-toggle' => 'tooltip', 'data-title' => "#{subject.name} - #{status.label}" }= subject.name - if status.has_action? = link_to status.action_path, method: status.action_method, diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index b70b574e687..6919b210a00 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -10,12 +10,11 @@ - status_groups.each do |group_name, grouped_statuses| - if grouped_statuses.one? - status = grouped_statuses.first - %li.build + %li.build{ 'id' => "ci-badge-#{group_name}" } .curve - .build-content - = render 'ci/status/graph_badge', subject: status + = render 'ci/status/graph_badge', subject: status - else - %li.build + %li.build{ 'id' => "ci-badge-#{group_name}" } .curve - .dropdown.inline.build-content - = render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses + = render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses + diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index b03837d1211..b15f7eaeab2 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -1,8 +1,8 @@ - group_status = CommitStatus.where(id: subject).status -%button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } } +%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown'} } %span{class: "ci-status-icon ci-status-icon-#{group_status}"} = ci_icon_for_status(group_status) - %span.ci-status-text + %span.ci-status-text{ 'data-toggle' => 'tooltip', 'data-title' => "#{name} - #{group_status}" } = name %span.dropdown-counter-badge= subject.size .dropdown-menu.grouped-pipeline-dropdown diff --git a/changelogs/unreleased/pipeline-build-hitbox.yml b/changelogs/unreleased/pipeline-build-hitbox.yml new file mode 100644 index 00000000000..051b538a9a3 --- /dev/null +++ b/changelogs/unreleased/pipeline-build-hitbox.yml @@ -0,0 +1,4 @@ +--- +title: Make CI badge hitboxes match parent +merge_request: +author: diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 57f1e75ea2c..1210e2745db 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -19,7 +19,7 @@ describe "Pipelines", feature: true, js: true do @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build') @failed = create(:ci_build, :failed, pipeline: pipeline, stage: 'test', name: 'test', commands: 'test') @running = create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', name: 'deploy') - @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual build') + @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual-build') @external = create(:generic_commit_status, status: 'success', pipeline: pipeline, name: 'jenkins', stage: 'external') end @@ -41,37 +41,34 @@ describe "Pipelines", feature: true, js: true do describe 'pipeline graph' do context 'when pipeline has running builds' do it 'shows a running icon and a cancel action for the running build' do - page.within('a[data-title="deploy - running"]') do + page.within('#ci-badge-deploy') do expect(page).to have_selector('.ci-status-icon-running') - expect(page).to have_content('deploy') - end - - page.within('a[data-title="deploy - running"] + .ci-action-icon-container') do expect(page).to have_selector('.ci-action-icon-container .fa-ban') + expect(page).to have_content('deploy') end end it 'should be possible to cancel the running build' do - find('a[data-title="deploy - running"] + .ci-action-icon-container').trigger('click') + find('#ci-badge-deploy .ci-action-icon-container').trigger('click') expect(page).not_to have_content('Cancel running') end end context 'when pipeline has successful builds' do - it 'shows the success icon and a retry action for the successfull build' do - page.within('a[data-title="build - passed"]') do + it 'shows the success icon and a retry action for the successful build' do + page.within('#ci-badge-build') do expect(page).to have_selector('.ci-status-icon-success') expect(page).to have_content('build') end - page.within('a[data-title="build - passed"] + .ci-action-icon-container') do + page.within('#ci-badge-build .ci-action-icon-container') do expect(page).to have_selector('.ci-action-icon-container .fa-refresh') end end it 'should be possible to retry the success build' do - find('a[data-title="build - passed"] + .ci-action-icon-container').trigger('click') + find('#ci-badge-build .ci-action-icon-container').trigger('click') expect(page).not_to have_content('Retry build') end @@ -79,18 +76,18 @@ describe "Pipelines", feature: true, js: true do context 'when pipeline has failed builds' do it 'shows the failed icon and a retry action for the failed build' do - page.within('a[data-title="test - failed"]') do + page.within('#ci-badge-test') do expect(page).to have_selector('.ci-status-icon-failed') expect(page).to have_content('test') end - page.within('a[data-title="test - failed"] + .ci-action-icon-container') do + page.within('#ci-badge-test .ci-action-icon-container') do expect(page).to have_selector('.ci-action-icon-container .fa-refresh') end end it 'should be possible to retry the failed build' do - find('a[data-title="test - failed"] + .ci-action-icon-container').trigger('click') + find('#ci-badge-test .ci-action-icon-container').trigger('click') expect(page).not_to have_content('Retry build') end @@ -98,18 +95,18 @@ describe "Pipelines", feature: true, js: true do context 'when pipeline has manual builds' do it 'shows the skipped icon and a play action for the manual build' do - page.within('a[data-title="manual build - manual play action"]') do + page.within('#ci-badge-manual-build') do expect(page).to have_selector('.ci-status-icon-manual') expect(page).to have_content('manual') end - page.within('a[data-title="manual build - manual play action"] + .ci-action-icon-container') do + page.within('#ci-badge-manual-build .ci-action-icon-container') do expect(page).to have_selector('.ci-action-icon-container .fa-play') end end it 'should be possible to play the manual build' do - find('a[data-title="manual build - manual play action"] + .ci-action-icon-container').trigger('click') + find('#ci-badge-manual-build .ci-action-icon-container').trigger('click') expect(page).not_to have_content('Play build') end @@ -167,7 +164,7 @@ describe "Pipelines", feature: true, js: true do @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build') @failed = create(:ci_build, :failed, pipeline: pipeline, stage: 'test', name: 'test', commands: 'test') @running = create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', name: 'deploy') - @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual build') + @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual-build') @external = create(:generic_commit_status, status: 'success', pipeline: pipeline, name: 'jenkins', stage: 'external') end diff --git a/spec/javascripts/fixtures/pipeline_graph.html.haml b/spec/javascripts/fixtures/pipeline_graph.html.haml index deca50ceaa7..c0b5ab4411e 100644 --- a/spec/javascripts/fixtures/pipeline_graph.html.haml +++ b/spec/javascripts/fixtures/pipeline_graph.html.haml @@ -8,8 +8,7 @@ %ul %li.build .curve - .build-content - %a - %svg - .ci-status-text - stop_review + %a + %svg + .ci-status-text + stop_review From 5b0ebbe5b4c18c136aad2a53898c1a6441d39a9d Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 19 Dec 2016 20:44:57 +0200 Subject: [PATCH 334/386] Add Wiki import to BB importer --- doc/integration/bitbucket.md | 3 +- .../img/bitbucket_oauth_settings_page.png | Bin 5607 -> 28719 bytes .../import_projects_from_bitbucket.md | 1 + lib/bitbucket/representation/repo.rb | 4 ++ lib/gitlab/bitbucket_import/importer.rb | 13 +++++ .../bitbucket_import/project_creator.rb | 9 +++- .../lib/bitbucket/representation/repo_spec.rb | 49 ++++++++++++++++++ .../gitlab/bitbucket_import/importer_spec.rb | 12 +++-- .../bitbucket_import/project_creator_spec.rb | 3 +- 9 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 spec/lib/bitbucket/representation/repo_spec.rb diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 5df6e103f42..1dfc985eaea 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -5,7 +5,7 @@ Bitbucket.org account. ## Overview -You can set up Bitbucket.org as an OAuth provider so that you can use your +You can set up Bitbucket.org as an OAuth2 provider so that you can use your credentials to authenticate into GitLab or import your projects from Bitbucket.org. @@ -50,6 +50,7 @@ you to use. Repositories: Read Pull Requests: Read Issues: Read + Wiki: Read and Write ``` ![Bitbucket OAuth settings page](img/bitbucket_oauth_settings_page.png) diff --git a/doc/integration/img/bitbucket_oauth_settings_page.png b/doc/integration/img/bitbucket_oauth_settings_page.png index 21ce82a6074e2a5b1d52331a5567634a8c5c7914..3e6dea6cfe9abfe51777318f07f916e6d2806996 100644 GIT binary patch literal 28719 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 literal 5607 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 diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md index b6d47e5afa2..97380bce172 100644 --- a/doc/workflow/importing/import_projects_from_bitbucket.md +++ b/doc/workflow/importing/import_projects_from_bitbucket.md @@ -17,6 +17,7 @@ to enable this if not already. - the pull requests (GitLab 8.4+) - the pull request comments (GitLab 8.15+) - the milestones (GitLab 8.15+) + - the wiki (GitLab 8.15+) - References to pull requests and issues are preserved (GitLab 8.7+) - Repository public access is retained. If a repository is private in Bitbucket it will be created as private in GitLab as well. diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb index 8969ecd1c19..423eff8f2a5 100644 --- a/lib/bitbucket/representation/repo.rb +++ b/lib/bitbucket/representation/repo.rb @@ -51,6 +51,10 @@ module Bitbucket raw['scm'] == 'git' end + def has_wiki? + raw['has_wiki'] + end + def visibility_level if raw['is_private'] Gitlab::VisibilityLevel::PRIVATE diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 7d2f92d577a..44323b47dca 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -1,6 +1,8 @@ module Gitlab module BitbucketImport class Importer + include Gitlab::ShellAdapter + LABELS = [{ title: 'bug', color: '#FF0000' }, { title: 'enhancement', color: '#428BCA' }, { title: 'proposal', color: '#69D100' }, @@ -18,6 +20,7 @@ module Gitlab end def execute + import_wiki import_issues import_pull_requests handle_errors @@ -55,6 +58,16 @@ module Gitlab @repo ||= client.repo(project.import_source) end + def import_wiki + return if project.wiki.repository_exists? + + path_with_namespace = "#{project.path_with_namespace}.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 + errors << { type: :wiki, errors: e.message } + end + def import_issues return unless repo.issues_enabled? diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index eb03882ab26..d94f70fd1fb 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -22,9 +22,16 @@ module Gitlab import_type: 'bitbucket', import_source: repo.full_name, import_url: repo.clone_url(session_data[:token]), - import_data: { credentials: session_data } + import_data: { credentials: session_data }, + skip_wiki: skip_wiki ).execute end + + private + + def skip_wiki + repo.has_wiki? + end end end end diff --git a/spec/lib/bitbucket/representation/repo_spec.rb b/spec/lib/bitbucket/representation/repo_spec.rb new file mode 100644 index 00000000000..adcd978e1b3 --- /dev/null +++ b/spec/lib/bitbucket/representation/repo_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe Bitbucket::Representation::Repo do + describe '#has_wiki?' do + it { expect(described_class.new({ 'has_wiki' => false }).has_wiki?).to be_falsey } + it { expect(described_class.new({ 'has_wiki' => true }).has_wiki?).to be_truthy } + end + + describe '#name' do + it { expect(described_class.new({ 'name' => 'test' }).name).to eq('test') } + end + + describe '#valid?' do + it { expect(described_class.new({ 'scm' => 'hg' }).valid?).to be_falsey } + it { expect(described_class.new({ 'scm' => 'git' }).valid?).to be_truthy } + end + + describe '#full_name' do + it { expect(described_class.new({ 'full_name' => 'test_full' }).full_name).to eq('test_full') } + end + + describe '#description' do + it { expect(described_class.new({ 'description' => 'desc' }).description).to eq('desc') } + end + + describe '#issues_enabled?' do + it { expect(described_class.new({ 'has_issues' => false }).issues_enabled?).to be_falsey } + it { expect(described_class.new({ 'has_issues' => true }).issues_enabled?).to be_truthy } + end + + describe '#owner_and_slug' do + it { expect(described_class.new({ 'full_name' => 'ben/test' }).owner_and_slug).to eq(['ben', 'test']) } + end + + describe '#owner' do + it { expect(described_class.new({ 'full_name' => 'ben/test' }).owner).to eq('ben') } + end + + describe '#slug' do + it { expect(described_class.new({ 'full_name' => 'ben/test' }).slug).to eq('test') } + end + + describe '#clone_url' do + it 'builds url' do + data = { 'links' => { 'clone' => [ { 'name' => 'https', 'href' => 'https://bibucket.org/test/test.git' }] } } + expect(described_class.new(data).clone_url('abc')).to eq('https://x-token-auth:abc@bibucket.org/test/test.git') + end + end +end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 53f3c73ade4..72b1ba36b58 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -69,6 +69,9 @@ describe Gitlab::BitbucketImport::Importer, lib: true do context 'issues statuses' do before do + # HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this + Bitbucket::Representation::Issue.new({}) + stub_request( :get, "https://api.bitbucket.org/2.0/repositories/#{project_identifier}" @@ -108,13 +111,16 @@ describe Gitlab::BitbucketImport::Importer, lib: true do body: {}.to_json) end - it 'map statuses to open or closed' do - # HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this - Bitbucket::Representation::Issue.new({}) + it 'maps statuses to open or closed' do importer.execute expect(project.issues.where(state: "closed").size).to eq(5) expect(project.issues.where(state: "opened").size).to eq(2) end + + it 'calls import_wiki' do + expect(importer).to receive(:import_wiki) + importer.execute + end end end diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb index b6d052a4612..773d0d4d288 100644 --- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::BitbucketImport::ProjectCreator, lib: true do owner: "asd", full_name: 'Vim repo', visibility_level: Gitlab::VisibilityLevel::PRIVATE, - clone_url: 'ssh://git@bitbucket.org/asd/vim.git') + clone_url: 'ssh://git@bitbucket.org/asd/vim.git', + has_wiki?: false) end let(:namespace){ create(:group, owner: user) } From e7c56dd1f63a0065b5930accb3ff24314430f82c Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 19 Dec 2016 10:25:46 -0500 Subject: [PATCH 335/386] Align milestone column header with count number --- app/assets/stylesheets/framework/panels.scss | 14 ++++++++++++++ app/views/shared/milestones/_issuables.html.haml | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss index 5ba0486177f..9d8d08dff88 100644 --- a/app/assets/stylesheets/framework/panels.scss +++ b/app/assets/stylesheets/framework/panels.scss @@ -18,6 +18,20 @@ margin-top: -2px; margin-left: 5px; } + + &.split { + display: flex; + align-items: center; + } + + .left { + flex: 1 1 auto; + } + + .right { + flex: 0 0 auto; + text-align: right; + } } .panel-body { diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml index 8619939dde7..15ff5b8a27e 100644 --- a/app/views/shared/milestones/_issuables.html.haml +++ b/app/views/shared/milestones/_issuables.html.haml @@ -3,10 +3,12 @@ - panel_class = primary ? 'panel-primary' : 'panel-default' .panel{ class: panel_class } - .panel-heading - = title + .panel-heading.split + .left + = title - if show_counter - .pull-right= issuables.size + .right + = issuables.size - class_prefix = dom_class(issuables).pluralize %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id } From 18c9fc42249a08ff28cf9d5b9159b7bada168bcf Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 20 Dec 2016 03:24:38 +0800 Subject: [PATCH 336/386] Use a block to insert extra check for authenticate_build! Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8091#note_20253762 --- lib/ci/api/helpers.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 62c10c3b753..31fbd1da108 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -14,13 +14,16 @@ module Ci end def authenticate_build!(build) - not_found! unless build - forbidden! unless build_token_valid?(build) - validate_build!(build) + validate_build!(build) do + forbidden! unless build_token_valid?(build) + end end def validate_build!(build) not_found! unless build + + yield if block_given? + forbidden!('Project has been deleted!') unless build.project forbidden!('Build has been erased!') if build.erased? end From c5741e57c2726660732ef2e4114645035025f8c3 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Wed, 14 Dec 2016 15:48:46 +0300 Subject: [PATCH 337/386] Fix consistent typo in environment.js environmnets => environments --- .../environments/components/environment.js.es6 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 88c3d257cea..d04adecd207 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -18,7 +18,7 @@ * The environments array is a recursive tree structure and we need to filter * both root level environments and children environments. * - * In order to acomplish that, both `filterState` and `filterEnvironmnetsByState` + * In order to acomplish that, both `filterState` and `filterEnvironmentsByState` * functions work together. * The first one works as the filter that verifies if the given environment matches * the given state. @@ -34,9 +34,9 @@ * @param {Array} array * @return {Array} */ - const filterEnvironmnetsByState = (fn, arr) => arr.map((item) => { + const filterEnvironmentsByState = (fn, arr) => arr.map((item) => { if (item.children) { - const filteredChildren = filterEnvironmnetsByState(fn, item.children).filter(Boolean); + const filteredChildren = filterEnvironmentsByState(fn, item.children).filter(Boolean); if (filteredChildren.length) { item.children = filteredChildren; return item; @@ -81,7 +81,7 @@ computed: { filteredEnvironments() { - return filterEnvironmnetsByState(filterState(this.visibility), this.state.environments); + return filterEnvironmentsByState(filterState(this.visibility), this.state.environments); }, scope() { @@ -102,7 +102,7 @@ }, /** - * Fetches all the environmnets and stores them. + * Fetches all the environments and stores them. * Toggles loading property. */ created() { From dcddd0f374f9daa321bb8bfa354dc7032d02f5a0 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Wed, 14 Dec 2016 15:42:39 +0300 Subject: [PATCH 338/386] Remove unnecessary hidden svg elements for icons. --- app/views/projects/environments/index.html.haml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index a65a630f2d0..6aae035b3e0 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -4,10 +4,6 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag("environments/environments_bundle.js") -.commit-icon-svg.hidden - = custom_icon("icon_commit") -.play-icon-svg.hidden - = custom_icon("icon_play") #environments-list-view{ data: { environments_data: environments_list_data, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, From 7c2e16d05319fa79d0b84472130c4a9300e08808 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 24 Nov 2016 13:01:25 +0000 Subject: [PATCH 339/386] Add xterm.js 2.1.0 and a wrapper class to the asset pipeline --- .../javascripts/terminal/terminal.js.es6 | 62 + .../terminal/terminal_bundle.js.es6 | 5 + config/application.rb | 2 + vendor/assets/javascripts/xterm/fit.js | 86 + vendor/assets/javascripts/xterm/xterm.js | 2235 +++++++++++++++++ vendor/assets/stylesheets/xterm/xterm.css | 2206 ++++++++++++++++ 6 files changed, 4596 insertions(+) create mode 100644 app/assets/javascripts/terminal/terminal.js.es6 create mode 100644 app/assets/javascripts/terminal/terminal_bundle.js.es6 create mode 100644 vendor/assets/javascripts/xterm/fit.js create mode 100644 vendor/assets/javascripts/xterm/xterm.js create mode 100644 vendor/assets/stylesheets/xterm/xterm.css diff --git a/app/assets/javascripts/terminal/terminal.js.es6 b/app/assets/javascripts/terminal/terminal.js.es6 new file mode 100644 index 00000000000..6b9422b1816 --- /dev/null +++ b/app/assets/javascripts/terminal/terminal.js.es6 @@ -0,0 +1,62 @@ +/* global Terminal */ + +(() => { + class GLTerminal { + + constructor(options) { + this.options = options || {}; + + this.options.cursorBlink = options.cursorBlink || true; + this.options.screenKeys = options.screenKeys || true; + this.container = document.querySelector(options.selector); + + this.setSocketUrl(); + this.createTerminal(); + $(window).off('resize.terminal').on('resize.terminal', () => { + this.terminal.fit(); + }); + } + + setSocketUrl() { + const { protocol, hostname, port } = window.location; + const wsProtocol = protocol === 'https:' ? 'wss://' : 'ws://'; + const path = this.container.dataset.projectPath; + + this.socketUrl = `${wsProtocol}${hostname}:${port}${path}`; + } + + createTerminal() { + this.terminal = new Terminal(this.options); + this.socket = new WebSocket(this.socketUrl, ['terminal.gitlab.com']); + this.socket.binaryType = 'arraybuffer'; + + this.terminal.open(this.container); + this.socket.onopen = () => { this.runTerminal(); }; + this.socket.onerror = () => { this.handleSocketFailure(); }; + } + + runTerminal() { + const decoder = new TextDecoder('utf-8'); + const encoder = new TextEncoder('utf-8'); + + this.terminal.on('data', (data) => { + this.socket.send(encoder.encode(data)); + }); + + this.socket.addEventListener('message', (ev) => { + this.terminal.write(decoder.decode(ev.data)); + }); + + this.isTerminalInitialized = true; + this.terminal.fit(); + } + + handleSocketFailure() { + this.terminal.write('\r\nConnection failure'); + } + + } + + window.gl = window.gl || {}; + gl.Terminal = GLTerminal; +})(); diff --git a/app/assets/javascripts/terminal/terminal_bundle.js.es6 b/app/assets/javascripts/terminal/terminal_bundle.js.es6 new file mode 100644 index 00000000000..ded7ee6e9fe --- /dev/null +++ b/app/assets/javascripts/terminal/terminal_bundle.js.es6 @@ -0,0 +1,5 @@ +//= require xterm/xterm.js +//= require xterm/fit.js +//= require ./terminal.js + +$(() => new gl.Terminal({ selector: '#terminal' })); diff --git a/config/application.rb b/config/application.rb index 782a7a36895..057d60ca869 100644 --- a/config/application.rb +++ b/config/application.rb @@ -89,6 +89,7 @@ module Gitlab config.assets.precompile << "mailers/*.css" config.assets.precompile << "katex.css" config.assets.precompile << "katex.js" + config.assets.precompile << "xterm/xterm.css" config.assets.precompile << "graphs/graphs_bundle.js" config.assets.precompile << "users/users_bundle.js" config.assets.precompile << "network/network_bundle.js" @@ -102,6 +103,7 @@ module Gitlab config.assets.precompile << "environments/environments_bundle.js" config.assets.precompile << "blob_edit/blob_edit_bundle.js" config.assets.precompile << "snippet/snippet_bundle.js" + config.assets.precompile << "terminal/terminal_bundle.js" config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/*.js" config.assets.precompile << "u2f.js" diff --git a/vendor/assets/javascripts/xterm/fit.js b/vendor/assets/javascripts/xterm/fit.js new file mode 100644 index 00000000000..7e24fd9b36e --- /dev/null +++ b/vendor/assets/javascripts/xterm/fit.js @@ -0,0 +1,86 @@ +/* + * Fit terminal columns and rows to the dimensions of its + * DOM element. + * + * Approach: + * - Rows: Truncate the division of the terminal parent element height + * by the terminal row height + * + * - Columns: Truncate the division of the terminal parent element width by + * the terminal character width (apply display: inline at the + * terminal row and truncate its width with the current number + * of columns) + */ +(function (fit) { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = fit(require('../../xterm')); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../xterm'], fit); + } else { + /* + * Plain browser environment + */ + fit(window.Terminal); + } +})(function (Xterm) { + /** + * This module provides methods for fitting a terminal's size to a parent container. + * + * @module xterm/addons/fit/fit + */ + var exports = {}; + + exports.proposeGeometry = function (term) { + var parentElementStyle = window.getComputedStyle(term.element.parentElement), + parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), + parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')), + elementStyle = window.getComputedStyle(term.element), + elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), + elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')), + availableHeight = parentElementHeight - elementPaddingVer, + availableWidth = parentElementWidth - elementPaddingHor, + container = term.rowContainer, + subjectRow = term.rowContainer.firstElementChild, + contentBuffer = subjectRow.innerHTML, + characterHeight, + rows, + characterWidth, + cols, + geometry; + + subjectRow.style.display = 'inline'; + subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace + characterWidth = subjectRow.getBoundingClientRect().width; + subjectRow.style.display = ''; // Revert style before calculating height, since they differ. + characterHeight = parseInt(subjectRow.offsetHeight); + subjectRow.innerHTML = contentBuffer; + + rows = parseInt(availableHeight / characterHeight); + cols = parseInt(availableWidth / characterWidth) - 1; + + geometry = {cols: cols, rows: rows}; + return geometry; + }; + + exports.fit = function (term) { + var geometry = exports.proposeGeometry(term); + + term.resize(geometry.cols, geometry.rows); + }; + + Xterm.prototype.proposeGeometry = function () { + return exports.proposeGeometry(this); + }; + + Xterm.prototype.fit = function () { + return exports.fit(this); + }; + + return exports; +}); diff --git a/vendor/assets/javascripts/xterm/xterm.js b/vendor/assets/javascripts/xterm/xterm.js new file mode 100644 index 00000000000..11ce3c73db9 --- /dev/null +++ b/vendor/assets/javascripts/xterm/xterm.js @@ -0,0 +1,2235 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Terminal = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + self.terminal.handler(diff); + } + } + }, 0); +}; + +/** + * Positions the composition view on top of the cursor and the textarea just below it (so the + * IME helper dialog is positioned correctly). + */ +CompositionHelper.prototype.updateCompositionElements = function (dontRecurse) { + if (!this.isComposing) { + return; + } + var cursor = this.terminal.element.querySelector('.terminal-cursor'); + if (cursor) { + // Take .xterm-rows offsetTop into account as well in case it's positioned absolutely within + // the .xterm element. + var xtermRows = this.terminal.element.querySelector('.xterm-rows'); + var cursorTop = xtermRows.offsetTop + cursor.offsetTop; + + this.compositionView.style.left = cursor.offsetLeft + 'px'; + this.compositionView.style.top = cursorTop + 'px'; + this.compositionView.style.height = cursor.offsetHeight + 'px'; + this.compositionView.style.lineHeight = cursor.offsetHeight + 'px'; + // Sync the textarea to the exact position of the composition view so the IME knows where the + // text is. + var compositionViewBounds = this.compositionView.getBoundingClientRect(); + this.textarea.style.left = cursor.offsetLeft + 'px'; + this.textarea.style.top = cursorTop + 'px'; + this.textarea.style.width = compositionViewBounds.width + 'px'; + this.textarea.style.height = compositionViewBounds.height + 'px'; + this.textarea.style.lineHeight = compositionViewBounds.height + 'px'; + } + if (!dontRecurse) { + setTimeout(this.updateCompositionElements.bind(this, true), 0); + } +}; + +/** + * Clears the textarea's position so that the cursor does not blink on IE. + * @private + */ +CompositionHelper.prototype.clearTextareaPosition = function () { + this.textarea.style.left = ''; + this.textarea.style.top = ''; +}; + +exports.CompositionHelper = CompositionHelper; + +},{}],2:[function(_dereq_,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +/** + * xterm.js: xterm, in the browser + * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License) + */ + +function EventEmitter() { + this._events = this._events || {}; +} + +EventEmitter.prototype.addListener = function (type, listener) { + this._events[type] = this._events[type] || []; + this._events[type].push(listener); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.removeListener = function (type, listener) { + if (!this._events[type]) return; + + var obj = this._events[type], + i = obj.length; + + while (i--) { + if (obj[i] === listener || obj[i].listener === listener) { + obj.splice(i, 1); + return; + } + } +}; + +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + +EventEmitter.prototype.removeAllListeners = function (type) { + if (this._events[type]) delete this._events[type]; +}; + +EventEmitter.prototype.once = function (type, listener) { + var self = this; + function on() { + var args = Array.prototype.slice.call(arguments); + this.removeListener(type, on); + return listener.apply(this, args); + } + on.listener = listener; + return this.on(type, on); +}; + +EventEmitter.prototype.emit = function (type) { + if (!this._events[type]) return; + + var args = Array.prototype.slice.call(arguments, 1), + obj = this._events[type], + l = obj.length, + i = 0; + + for (; i < l; i++) { + obj[i].apply(this, args); + } +}; + +EventEmitter.prototype.listeners = function (type) { + return this._events[type] = this._events[type] || []; +}; + +exports.EventEmitter = EventEmitter; + +},{}],3:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +/** + * xterm.js: xterm, in the browser + * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License) + */ + +/** + * Represents the viewport of a terminal, the visible area within the larger buffer of output. + * Logic for the virtual scroll bar is included in this object. + * @param {Terminal} terminal The Terminal object. + * @param {HTMLElement} viewportElement The DOM element acting as the viewport + * @param {HTMLElement} charMeasureElement A DOM element used to measure the character size of + * the terminal. + */ +function Viewport(terminal, viewportElement, scrollArea, charMeasureElement) { + this.terminal = terminal; + this.viewportElement = viewportElement; + this.scrollArea = scrollArea; + this.charMeasureElement = charMeasureElement; + this.currentRowHeight = 0; + this.lastRecordedBufferLength = 0; + this.lastRecordedViewportHeight = 0; + + this.terminal.on('scroll', this.syncScrollArea.bind(this)); + this.terminal.on('resize', this.syncScrollArea.bind(this)); + this.viewportElement.addEventListener('scroll', this.onScroll.bind(this)); + + this.syncScrollArea(); +} + +/** + * Refreshes row height, setting line-height, viewport height and scroll area height if + * necessary. + * @param {number|undefined} charSize A character size measurement bounding rect object, if it + * doesn't exist it will be created. + */ +Viewport.prototype.refresh = function (charSize) { + var size = charSize || this.charMeasureElement.getBoundingClientRect(); + if (size.height > 0) { + var rowHeightChanged = size.height !== this.currentRowHeight; + if (rowHeightChanged) { + this.currentRowHeight = size.height; + this.viewportElement.style.lineHeight = size.height + 'px'; + this.terminal.rowContainer.style.lineHeight = size.height + 'px'; + } + var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows; + if (rowHeightChanged || viewportHeightChanged) { + this.lastRecordedViewportHeight = this.terminal.rows; + this.viewportElement.style.height = size.height * this.terminal.rows + 'px'; + } + this.scrollArea.style.height = size.height * this.lastRecordedBufferLength + 'px'; + } +}; + +/** + * Updates dimensions and synchronizes the scroll area if necessary. + */ +Viewport.prototype.syncScrollArea = function () { + if (this.lastRecordedBufferLength !== this.terminal.lines.length) { + // If buffer height changed + this.lastRecordedBufferLength = this.terminal.lines.length; + this.refresh(); + } else if (this.lastRecordedViewportHeight !== this.terminal.rows) { + // If viewport height changed + this.refresh(); + } else { + // If size has changed, refresh viewport + var size = this.charMeasureElement.getBoundingClientRect(); + if (size.height !== this.currentRowHeight) { + this.refresh(size); + } + } + + // Sync scrollTop + var scrollTop = this.terminal.ydisp * this.currentRowHeight; + if (this.viewportElement.scrollTop !== scrollTop) { + this.viewportElement.scrollTop = scrollTop; + } +}; + +/** + * Handles scroll events on the viewport, calculating the new viewport and requesting the + * terminal to scroll to it. + * @param {Event} ev The scroll event. + */ +Viewport.prototype.onScroll = function (ev) { + var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight); + var diff = newRow - this.terminal.ydisp; + this.terminal.scrollDisp(diff, true); +}; + +/** + * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual + * scrolling to `onScroll`, this event needs to be attached manually by the consumer of + * `Viewport`. + * @param {WheelEvent} ev The mouse wheel event. + */ +Viewport.prototype.onWheel = function (ev) { + if (ev.deltaY === 0) { + // Do nothing if it's not a vertical scroll event + return; + } + // Fallback to WheelEvent.DOM_DELTA_PIXEL + var multiplier = 1; + if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { + multiplier = this.currentRowHeight; + } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { + multiplier = this.currentRowHeight * this.terminal.rows; + } + this.viewportElement.scrollTop += ev.deltaY * multiplier; + // Prevent the page from scrolling when the terminal scrolls + ev.preventDefault(); +}; + +exports.Viewport = Viewport; + +},{}],4:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +/** + * xterm.js: xterm, in the browser + * Copyright (c) 2016, SourceLair Private Company (MIT License) + */ + +/** + * Clipboard handler module. This module contains methods for handling all + * clipboard-related events appropriately in the terminal. + * @module xterm/handlers/Clipboard + */ + +/** + * Prepares text copied from terminal selection, to be saved in the clipboard by: + * 1. stripping all trailing white spaces + * 2. converting all non-breaking spaces to regular spaces + * @param {string} text The copied text that needs processing for storing in clipboard + * @returns {string} + */ +function prepareTextForClipboard(text) { + var space = String.fromCharCode(32), + nonBreakingSpace = String.fromCharCode(160), + allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'), + processedText = text.split('\n').map(function (line) { + // Strip all trailing white spaces and convert all non-breaking spaces + // to regular spaces. + var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space); + + return processedLine; + }).join('\n'); + + return processedText; +} + +/** + * Binds copy functionality to the given terminal. + * @param {ClipboardEvent} ev The original copy event to be handled + */ +function copyHandler(ev, term) { + var copiedText = window.getSelection().toString(), + text = prepareTextForClipboard(copiedText); + + if (term.browser.isMSIE) { + window.clipboardData.setData('Text', text); + } else { + ev.clipboardData.setData('text/plain', text); + } + + ev.preventDefault(); // Prevent or the original text will be copied. +} + +/** + * Redirect the clipboard's data to the terminal's input handler. + * @param {ClipboardEvent} ev The original paste event to be handled + * @param {Terminal} term The terminal on which to apply the handled paste event + */ +function pasteHandler(ev, term) { + ev.stopPropagation(); + + var dispatchPaste = function dispatchPaste(text) { + term.handler(text); + term.textarea.value = ''; + return term.cancel(ev); + }; + + if (term.browser.isMSIE) { + if (window.clipboardData) { + var text = window.clipboardData.getData('Text'); + dispatchPaste(text); + } + } else { + if (ev.clipboardData) { + var text = ev.clipboardData.getData('text/plain'); + dispatchPaste(text); + } + } +} + +/** + * Bind to right-click event and allow right-click copy and paste. + * + * **Logic** + * If text is selected and right-click happens on selected text, then + * do nothing to allow seamless copying. + * If no text is selected or right-click is outside of the selection + * area, then bring the terminal's input below the cursor, in order to + * trigger the event on the textarea and allow-right click paste, without + * caring about disappearing selection. + * @param {ClipboardEvent} ev The original paste event to be handled + * @param {Terminal} term The terminal on which to apply the handled paste event + */ +function rightClickHandler(ev, term) { + var s = document.getSelection(), + selectedText = prepareTextForClipboard(s.toString()), + clickIsOnSelection = false; + + if (s.rangeCount) { + var r = s.getRangeAt(0), + cr = r.getClientRects(), + x = ev.clientX, + y = ev.clientY, + i, + rect; + + for (i = 0; i < cr.length; i++) { + rect = cr[i]; + clickIsOnSelection = x > rect.left && x < rect.right && y > rect.top && y < rect.bottom; + + if (clickIsOnSelection) { + break; + } + } + // If we clicked on selection and selection is not a single space, + // then mark the right click as copy-only. We check for the single + // space selection, as this can happen when clicking on an   + // and there is not much pointing in copying a single space. + if (selectedText.match(/^\s$/) || !selectedText.length) { + clickIsOnSelection = false; + } + } + + // Bring textarea at the cursor position + if (!clickIsOnSelection) { + term.textarea.style.position = 'fixed'; + term.textarea.style.width = '20px'; + term.textarea.style.height = '20px'; + term.textarea.style.left = x - 10 + 'px'; + term.textarea.style.top = y - 10 + 'px'; + term.textarea.style.zIndex = 1000; + term.textarea.focus(); + + // Reset the terminal textarea's styling + setTimeout(function () { + term.textarea.style.position = null; + term.textarea.style.width = null; + term.textarea.style.height = null; + term.textarea.style.left = null; + term.textarea.style.top = null; + term.textarea.style.zIndex = null; + }, 4); + } +} + +exports.prepareTextForClipboard = prepareTextForClipboard; +exports.copyHandler = copyHandler; +exports.pasteHandler = pasteHandler; +exports.rightClickHandler = rightClickHandler; + +},{}],5:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isMSWindows = exports.isIphone = exports.isIpad = exports.isMac = exports.isMSIE = exports.isFirefox = undefined; + +var _Generic = _dereq_('./Generic.js'); + +var isNode = typeof navigator == 'undefined' ? true : false; /** + * xterm.js: xterm, in the browser + * Copyright (c) 2016, SourceLair Private Company (MIT License) + */ + +/** + * Browser utilities module. This module contains attributes and methods to help with + * identifying the current browser and platform. + * @module xterm/utils/Browser + */ + +var userAgent = isNode ? 'node' : navigator.userAgent; +var platform = isNode ? 'node' : navigator.platform; + +var isFirefox = exports.isFirefox = !!~userAgent.indexOf('Firefox'); +var isMSIE = exports.isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident'); + +// Find the users platform. We use this to interpret the meta key +// and ISO third level shifts. +// http://stackoverflow.com/q/19877924/577598 +var isMac = exports.isMac = (0, _Generic.contains)(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform); +var isIpad = exports.isIpad = platform === 'iPad'; +var isIphone = exports.isIphone = platform === 'iPhone'; +var isMSWindows = exports.isMSWindows = (0, _Generic.contains)(['Windows', 'Win16', 'Win32', 'WinCE'], platform); + +},{"./Generic.js":6}],6:[function(_dereq_,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +/** + * xterm.js: xterm, in the browser + * Copyright (c) 2016, SourceLair Private Company (MIT License) + */ + +/** + * Generic utilities module. This module contains generic methods that can be helpful at + * different parts of the code base. + * @module xterm/utils/Generic + */ + +/** + * Return if the given array contains the given element + * @param {Array} array The array to search for the given element. + * @param {Object} el The element to look for into the array + */ +var contains = exports.contains = function contains(arr, el) { + return arr.indexOf(el) >= 0; +}; + +},{}],7:[function(_dereq_,module,exports){ +'use strict';var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj;}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj;};/** + * xterm.js: xterm, in the browser + * Copyright (c) 2014-2014, SourceLair Private Company (MIT License) + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */var _CompositionHelper=_dereq_('./CompositionHelper.js');var _EventEmitter=_dereq_('./EventEmitter.js');var _Viewport=_dereq_('./Viewport.js');var _Clipboard=_dereq_('./handlers/Clipboard.js');var _Browser=_dereq_('./utils/Browser');var Browser=_interopRequireWildcard(_Browser);function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj;}else{var newObj={};if(obj!=null){for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key]=obj[key];}}newObj.default=obj;return newObj;}}/** + * Terminal Emulation References: + * http://vt100.net/ + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * http://invisible-island.net/vttest/ + * http://www.inwap.com/pdp10/ansicode.txt + * http://linux.die.net/man/4/console_codes + * http://linux.die.net/man/7/urxvt + */// Let it work inside Node.js for automated testing purposes. +var document=typeof window!='undefined'?window.document:null;/** + * States + */var normal=0,escaped=1,csi=2,osc=3,charset=4,dcs=5,ignore=6;/** + * Terminal + *//** + * Creates a new `Terminal` object. + * + * @param {object} options An object containing a set of options, the available options are: + * - `cursorBlink` (boolean): Whether the terminal cursor blinks + * - `cols` (number): The number of columns of the terminal (horizontal size) + * - `rows` (number): The number of rows of the terminal (vertical size) + * + * @public + * @class Xterm Xterm + * @alias module:xterm/src/xterm + */function Terminal(options){var self=this;if(!(this instanceof Terminal)){return new Terminal(arguments[0],arguments[1],arguments[2]);}self.browser=Browser;self.cancel=Terminal.cancel;_EventEmitter.EventEmitter.call(this);if(typeof options==='number'){options={cols:arguments[0],rows:arguments[1],handler:arguments[2]};}options=options||{};Object.keys(Terminal.defaults).forEach(function(key){if(options[key]==null){options[key]=Terminal.options[key];if(Terminal[key]!==Terminal.defaults[key]){options[key]=Terminal[key];}}self[key]=options[key];});if(options.colors.length===8){options.colors=options.colors.concat(Terminal._colors.slice(8));}else if(options.colors.length===16){options.colors=options.colors.concat(Terminal._colors.slice(16));}else if(options.colors.length===10){options.colors=options.colors.slice(0,-2).concat(Terminal._colors.slice(8,-2),options.colors.slice(-2));}else if(options.colors.length===18){options.colors=options.colors.concat(Terminal._colors.slice(16,-2),options.colors.slice(-2));}this.colors=options.colors;this.options=options;// this.context = options.context || window; +// this.document = options.document || document; +this.parent=options.body||options.parent||(document?document.getElementsByTagName('body')[0]:null);this.cols=options.cols||options.geometry[0];this.rows=options.rows||options.geometry[1];this.geometry=[this.cols,this.rows];if(options.handler){this.on('data',options.handler);}/** + * The scroll position of the y cursor, ie. ybase + y = the y position within the entire + * buffer + */this.ybase=0;/** + * The scroll position of the viewport + */this.ydisp=0;/** + * The cursor's x position after ybase + */this.x=0;/** + * The cursor's y position after ybase + */this.y=0;/** + * Used to debounce the refresh function + */this.isRefreshing=false;/** + * Whether there is a full terminal refresh queued + */this.cursorState=0;this.cursorHidden=false;this.convertEol;this.state=0;this.queue='';this.scrollTop=0;this.scrollBottom=this.rows-1;this.customKeydownHandler=null;// modes +this.applicationKeypad=false;this.applicationCursor=false;this.originMode=false;this.insertMode=false;this.wraparoundMode=true;// defaults: xterm - true, vt100 - false +this.normal=null;// charset +this.charset=null;this.gcharset=null;this.glevel=0;this.charsets=[null];// mouse properties +this.decLocator;this.x10Mouse;this.vt200Mouse;this.vt300Mouse;this.normalMouse;this.mouseEvents;this.sendFocus;this.utfMouse;this.sgrMouse;this.urxvtMouse;// misc +this.element;this.children;this.refreshStart;this.refreshEnd;this.savedX;this.savedY;this.savedCols;// stream +this.readable=true;this.writable=true;this.defAttr=0<<18|257<<9|256<<0;this.curAttr=this.defAttr;this.params=[];this.currentParam=0;this.prefix='';this.postfix='';// leftover surrogate high from previous write invocation +this.surrogate_high='';/** + * An array of all lines in the entire buffer, including the prompt. The lines are array of + * characters which are 2-length arrays where [0] is an attribute and [1] is the character. + */this.lines=[];var i=this.rows;while(i--){this.lines.push(this.blankLine());}this.tabs;this.setupStops();// Store if user went browsing history in scrollback +this.userScrolling=false;}inherits(Terminal,_EventEmitter.EventEmitter);/** + * back_color_erase feature for xterm. + */Terminal.prototype.eraseAttr=function(){// if (this.is('screen')) return this.defAttr; +return this.defAttr&~0x1ff|this.curAttr&0x1ff;};/** + * Colors + */// Colors 0-15 +Terminal.tangoColors=[// dark: +'#2e3436','#cc0000','#4e9a06','#c4a000','#3465a4','#75507b','#06989a','#d3d7cf',// bright: +'#555753','#ef2929','#8ae234','#fce94f','#729fcf','#ad7fa8','#34e2e2','#eeeeec'];// Colors 0-15 + 16-255 +// Much thanks to TooTallNate for writing this. +Terminal.colors=function(){var colors=Terminal.tangoColors.slice(),r=[0x00,0x5f,0x87,0xaf,0xd7,0xff],i;// 16-231 +i=0;for(;i<216;i++){out(r[i/36%6|0],r[i/6%6|0],r[i%6]);}// 232-255 (grey) +i=0;for(;i<24;i++){r=8+i*10;out(r,r,r);}function out(r,g,b){colors.push('#'+hex(r)+hex(g)+hex(b));}function hex(c){c=c.toString(16);return c.length<2?'0'+c:c;}return colors;}();Terminal._colors=Terminal.colors.slice();Terminal.vcolors=function(){var out=[],colors=Terminal.colors,i=0,color;for(;i<256;i++){color=parseInt(colors[i].substring(1),16);out.push([color>>16&0xff,color>>8&0xff,color&0xff]);}return out;}();/** + * Options + */Terminal.defaults={colors:Terminal.colors,theme:'default',convertEol:false,termName:'xterm',geometry:[80,24],cursorBlink:false,visualBell:false,popOnBell:false,scrollback:1000,screenKeys:false,debug:false,cancelEvents:false// programFeatures: false, +// focusKeys: false, +};Terminal.options={};Terminal.focus=null;each(keys(Terminal.defaults),function(key){Terminal[key]=Terminal.defaults[key];Terminal.options[key]=Terminal.defaults[key];});/** + * Focus the terminal. Delegates focus handling to the terminal's DOM element. + */Terminal.prototype.focus=function(){return this.textarea.focus();};/** + * Retrieves an option's value from the terminal. + * @param {string} key The option key. + */Terminal.prototype.getOption=function(key,value){if(!(key in Terminal.defaults)){throw new Error('No option with key "'+key+'"');}if(typeof this.options[key]!=='undefined'){return this.options[key];}return this[key];};/** + * Sets an option on the terminal. + * @param {string} key The option key. + * @param {string} value The option value. + */Terminal.prototype.setOption=function(key,value){if(!(key in Terminal.defaults)){throw new Error('No option with key "'+key+'"');}this[key]=value;this.options[key]=value;};/** + * Binds the desired focus behavior on a given terminal object. + * + * @static + */Terminal.bindFocus=function(term){on(term.textarea,'focus',function(ev){if(term.sendFocus){term.send('\x1b[I');}term.element.classList.add('focus');term.showCursor();Terminal.focus=term;term.emit('focus',{terminal:term});});};/** + * Blur the terminal. Delegates blur handling to the terminal's DOM element. + */Terminal.prototype.blur=function(){return this.textarea.blur();};/** + * Binds the desired blur behavior on a given terminal object. + * + * @static + */Terminal.bindBlur=function(term){on(term.textarea,'blur',function(ev){term.refresh(term.y,term.y);if(term.sendFocus){term.send('\x1b[O');}term.element.classList.remove('focus');Terminal.focus=null;term.emit('blur',{terminal:term});});};/** + * Initialize default behavior + */Terminal.prototype.initGlobal=function(){var term=this;Terminal.bindKeys(this);Terminal.bindFocus(this);Terminal.bindBlur(this);// Bind clipboard functionality +on(this.element,'copy',function(ev){_Clipboard.copyHandler.call(this,ev,term);});on(this.textarea,'paste',function(ev){_Clipboard.pasteHandler.call(this,ev,term);});function rightClickHandlerWrapper(ev){_Clipboard.rightClickHandler.call(this,ev,term);}if(term.browser.isFirefox){on(this.element,'mousedown',function(ev){if(ev.button==2){rightClickHandlerWrapper(ev);}});}else{on(this.element,'contextmenu',rightClickHandlerWrapper);}};/** + * Apply key handling to the terminal + */Terminal.bindKeys=function(term){on(term.element,'keydown',function(ev){if(document.activeElement!=this){return;}term.keyDown(ev);},true);on(term.element,'keypress',function(ev){if(document.activeElement!=this){return;}term.keyPress(ev);},true);on(term.element,'keyup',term.focus.bind(term));on(term.textarea,'keydown',function(ev){term.keyDown(ev);},true);on(term.textarea,'keypress',function(ev){term.keyPress(ev);// Truncate the textarea's value, since it is not needed +this.value='';},true);on(term.textarea,'compositionstart',term.compositionHelper.compositionstart.bind(term.compositionHelper));on(term.textarea,'compositionupdate',term.compositionHelper.compositionupdate.bind(term.compositionHelper));on(term.textarea,'compositionend',term.compositionHelper.compositionend.bind(term.compositionHelper));term.on('refresh',term.compositionHelper.updateCompositionElements.bind(term.compositionHelper));};/** + * Insert the given row to the terminal or produce a new one + * if no row argument is passed. Return the inserted row. + * @param {HTMLElement} row (optional) The row to append to the terminal. + */Terminal.prototype.insertRow=function(row){if((typeof row==='undefined'?'undefined':_typeof(row))!='object'){row=document.createElement('div');}this.rowContainer.appendChild(row);this.children.push(row);return row;};/** + * Opens the terminal within an element. + * + * @param {HTMLElement} parent The element to create the terminal within. + */Terminal.prototype.open=function(parent){var self=this,i=0,div;this.parent=parent||this.parent;if(!this.parent){throw new Error('Terminal requires a parent element.');}// Grab global elements +this.context=this.parent.ownerDocument.defaultView;this.document=this.parent.ownerDocument;this.body=this.document.getElementsByTagName('body')[0];//Create main element container +this.element=this.document.createElement('div');this.element.classList.add('terminal');this.element.classList.add('xterm');this.element.classList.add('xterm-theme-'+this.theme);this.element.style.height;this.element.setAttribute('tabindex',0);this.viewportElement=document.createElement('div');this.viewportElement.classList.add('xterm-viewport');this.element.appendChild(this.viewportElement);this.viewportScrollArea=document.createElement('div');this.viewportScrollArea.classList.add('xterm-scroll-area');this.viewportElement.appendChild(this.viewportScrollArea);// Create the container that will hold the lines of the terminal and then +// produce the lines the lines. +this.rowContainer=document.createElement('div');this.rowContainer.classList.add('xterm-rows');this.element.appendChild(this.rowContainer);this.children=[];// Create the container that will hold helpers like the textarea for +// capturing DOM Events. Then produce the helpers. +this.helperContainer=document.createElement('div');this.helperContainer.classList.add('xterm-helpers');// TODO: This should probably be inserted once it's filled to prevent an additional layout +this.element.appendChild(this.helperContainer);this.textarea=document.createElement('textarea');this.textarea.classList.add('xterm-helper-textarea');this.textarea.setAttribute('autocorrect','off');this.textarea.setAttribute('autocapitalize','off');this.textarea.setAttribute('spellcheck','false');this.textarea.tabIndex=0;this.textarea.addEventListener('focus',function(){self.emit('focus',{terminal:self});});this.textarea.addEventListener('blur',function(){self.emit('blur',{terminal:self});});this.helperContainer.appendChild(this.textarea);this.compositionView=document.createElement('div');this.compositionView.classList.add('composition-view');this.compositionHelper=new _CompositionHelper.CompositionHelper(this.textarea,this.compositionView,this);this.helperContainer.appendChild(this.compositionView);this.charMeasureElement=document.createElement('div');this.charMeasureElement.classList.add('xterm-char-measure-element');this.charMeasureElement.innerHTML='W';this.helperContainer.appendChild(this.charMeasureElement);for(;i +function sendButton(ev){var button,pos;// get the xterm-style button +button=getButton(ev);// get mouse coordinates +pos=getCoords(ev);if(!pos)return;sendEvent(button,pos);switch(ev.overrideType||ev.type){case'mousedown':pressed=button;break;case'mouseup':// keep it at the left +// button, just in case. +pressed=32;break;case'wheel':// nothing. don't +// interfere with +// `pressed`. +break;}}// motion example of a left click: +// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< +function sendMove(ev){var button=pressed,pos;pos=getCoords(ev);if(!pos)return;// buttons marked as motions +// are incremented by 32 +button+=32;sendEvent(button,pos);}// encode button and +// position to characters +function encode(data,ch){if(!self.utfMouse){if(ch===255)return data.push(0);if(ch>127)ch=127;data.push(ch);}else{if(ch===2047)return data.push(0);if(ch<127){data.push(ch);}else{if(ch>2047)ch=2047;data.push(0xC0|ch>>6);data.push(0x80|ch&0x3F);}}}// send a mouse event: +// regular/utf8: ^[[M Cb Cx Cy +// urxvt: ^[[ Cb ; Cx ; Cy M +// sgr: ^[[ Cb ; Cx ; Cy M/m +// vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r +// locator: CSI P e ; P b ; P r ; P c ; P p & w +function sendEvent(button,pos){// self.emit('mouse', { +// x: pos.x - 32, +// y: pos.x - 32, +// button: button +// }); +if(self.vt300Mouse){// NOTE: Unstable. +// http://www.vt100.net/docs/vt3xx-gp/chapter15.html +button&=3;pos.x-=32;pos.y-=32;var data='\x1b[24';if(button===0)data+='1';else if(button===1)data+='3';else if(button===2)data+='5';else if(button===3)return;else data+='0';data+='~['+pos.x+','+pos.y+']\r';self.send(data);return;}if(self.decLocator){// NOTE: Unstable. +button&=3;pos.x-=32;pos.y-=32;if(button===0)button=2;else if(button===1)button=4;else if(button===2)button=6;else if(button===3)button=3;self.send('\x1b['+button+';'+(button===3?4:0)+';'+pos.y+';'+pos.x+';'+(pos.page||0)+'&w');return;}if(self.urxvtMouse){pos.x-=32;pos.y-=32;pos.x++;pos.y++;self.send('\x1b['+button+';'+pos.x+';'+pos.y+'M');return;}if(self.sgrMouse){pos.x-=32;pos.y-=32;self.send('\x1b[<'+((button&3)===3?button&~3:button)+';'+pos.x+';'+pos.y+((button&3)===3?'m':'M'));return;}var data=[];encode(data,button);encode(data,pos.x);encode(data,pos.y);self.send('\x1b[M'+String.fromCharCode.apply(String,data));}function getButton(ev){var button,shift,meta,ctrl,mod;// two low bits: +// 0 = left +// 1 = middle +// 2 = right +// 3 = release +// wheel up/down: +// 1, and 2 - with 64 added +switch(ev.overrideType||ev.type){case'mousedown':button=ev.button!=null?+ev.button:ev.which!=null?ev.which-1:null;if(self.browser.isMSIE){button=button===1?0:button===4?1:button;}break;case'mouseup':button=3;break;case'DOMMouseScroll':button=ev.detail<0?64:65;break;case'wheel':button=ev.wheelDeltaY>0?64:65;break;}// next three bits are the modifiers: +// 4 = shift, 8 = meta, 16 = control +shift=ev.shiftKey?4:0;meta=ev.metaKey?8:0;ctrl=ev.ctrlKey?16:0;mod=shift|meta|ctrl;// no mods +if(self.vt200Mouse){// ctrl only +mod&=ctrl;}else if(!self.normalMouse){mod=0;}// increment to SP +button=32+(mod<<2)+button;return button;}// mouse coordinates measured in cols/rows +function getCoords(ev){var x,y,w,h,el;// ignore browsers without pageX for now +if(ev.pageX==null)return;x=ev.pageX;y=ev.pageY;el=self.element;// should probably check offsetParent +// but this is more portable +while(el&&el!==self.document.documentElement){x-=el.offsetLeft;y-=el.offsetTop;el='offsetParent'in el?el.offsetParent:el.parentNode;}// convert to cols/rows +w=self.element.clientWidth;h=self.element.clientHeight;x=Math.ceil(x/w*self.cols);y=Math.ceil(y/h*self.rows);// be sure to avoid sending +// bad positions to the program +if(x<0)x=0;if(x>self.cols)x=self.cols;if(y<0)y=0;if(y>self.rows)y=self.rows;// xterm sends raw bytes and +// starts at 32 (SP) for each. +x+=32;y+=32;return{x:x,y:y,type:'wheel'};}on(el,'mousedown',function(ev){if(!self.mouseEvents)return;// send the button +sendButton(ev);// ensure focus +self.focus();// fix for odd bug +//if (self.vt200Mouse && !self.normalMouse) { +if(self.vt200Mouse){ev.overrideType='mouseup';sendButton(ev);return self.cancel(ev);}// bind events +if(self.normalMouse)on(self.document,'mousemove',sendMove);// x10 compatibility mode can't send button releases +if(!self.x10Mouse){on(self.document,'mouseup',function up(ev){sendButton(ev);if(self.normalMouse)off(self.document,'mousemove',sendMove);off(self.document,'mouseup',up);return self.cancel(ev);});}return self.cancel(ev);});//if (self.normalMouse) { +// on(self.document, 'mousemove', sendMove); +//} +on(el,'wheel',function(ev){if(!self.mouseEvents)return;if(self.x10Mouse||self.vt300Mouse||self.decLocator)return;sendButton(ev);return self.cancel(ev);});// allow wheel scrolling in +// the shell for example +on(el,'wheel',function(ev){if(self.mouseEvents)return;self.viewport.onWheel(ev);return self.cancel(ev);});};/** + * Destroys the terminal. + */Terminal.prototype.destroy=function(){this.readable=false;this.writable=false;this._events={};this.handler=function(){};this.write=function(){};if(this.element.parentNode){this.element.parentNode.removeChild(this.element);}//this.emit('close'); +};/** + * Flags used to render terminal text properly + */Terminal.flags={BOLD:1,UNDERLINE:2,BLINK:4,INVERSE:8,INVISIBLE:16};/** + * Refreshes (re-renders) terminal content within two rows (inclusive) + * + * Rendering Engine: + * + * In the screen buffer, each character is stored as a an array with a character + * and a 32-bit integer: + * - First value: a utf-16 character. + * - Second value: + * - Next 9 bits: background color (0-511). + * - Next 9 bits: foreground color (0-511). + * - Next 14 bits: a mask for misc. flags: + * - 1=bold + * - 2=underline + * - 4=blink + * - 8=inverse + * - 16=invisible + * + * @param {number} start The row to start from (between 0 and terminal's height terminal - 1) + * @param {number} end The row to end at (between fromRow and terminal's height terminal - 1) + * @param {boolean} queue Whether the refresh should ran right now or be queued + */Terminal.prototype.refresh=function(start,end,queue){var self=this;// queue defaults to true +queue=typeof queue=='undefined'?true:queue;/** + * The refresh queue allows refresh to execute only approximately 30 times a second. For + * commands that pass a significant amount of output to the write function, this prevents the + * terminal from maxing out the CPU and making the UI unresponsive. While commands can still + * run beyond what they do on the terminal, it is far better with a debounce in place as + * every single terminal manipulation does not need to be constructed in the DOM. + * + * A side-effect of this is that it makes ^C to interrupt a process seem more responsive. + */if(queue){// If refresh should be queued, order the refresh and return. +if(this._refreshIsQueued){// If a refresh has already been queued, just order a full refresh next +this._fullRefreshNext=true;}else{setTimeout(function(){self.refresh(start,end,false);},34);this._refreshIsQueued=true;}return;}// If refresh should be run right now (not be queued), release the lock +this._refreshIsQueued=false;// If multiple refreshes were requested, make a full refresh. +if(this._fullRefreshNext){start=0;end=this.rows-1;this._fullRefreshNext=false;// reset lock +}var x,y,i,line,out,ch,ch_width,width,data,attr,bg,fg,flags,row,parent,focused=document.activeElement;// If this is a big refresh, remove the terminal rows from the DOM for faster calculations +if(end-start>=this.rows/2){parent=this.element.parentNode;if(parent){this.element.removeChild(this.rowContainer);}}width=this.cols;y=start;if(end>=this.rows.length){this.log('`end` is too large. Most likely a bad CSR.');end=this.rows.length-1;}for(;y<=end;y++){row=y+this.ydisp;line=this.lines[row];out='';if(this.y===y-(this.ybase-this.ydisp)&&this.cursorState&&!this.cursorHidden){x=this.x;}else{x=-1;}attr=this.defAttr;i=0;for(;i';}if(data!==this.defAttr){if(data===-1){out+='';}else{var classNames=[];bg=data&0x1ff;fg=data>>9&0x1ff;flags=data>>18;if(flags&Terminal.flags.BOLD){if(!Terminal.brokenBold){classNames.push('xterm-bold');}// See: XTerm*boldColors +if(fg<8)fg+=8;}if(flags&Terminal.flags.UNDERLINE){classNames.push('xterm-underline');}if(flags&Terminal.flags.BLINK){classNames.push('xterm-blink');}// If inverse flag is on, then swap the foreground and background variables. +if(flags&Terminal.flags.INVERSE){/* One-line variable swap in JavaScript: http://stackoverflow.com/a/16201730 */bg=[fg,fg=bg][0];// Should inverse just be before the +// above boldColors effect instead? +if(flags&1&&fg<8)fg+=8;}if(flags&Terminal.flags.INVISIBLE){classNames.push('xterm-hidden');}/** + * Weird situation: Invert flag used black foreground and white background results + * in invalid background color, positioned at the 256 index of the 256 terminal + * color map. Pin the colors manually in such a case. + * + * Source: https://github.com/sourcelair/xterm.js/issues/57 + */if(flags&Terminal.flags.INVERSE){if(bg==257){bg=15;}if(fg==256){fg=0;}}if(bg<256){classNames.push('xterm-bg-color-'+bg);}if(fg<256){classNames.push('xterm-color-'+fg);}out+='':out+='>';break;default:if(ch<=' '){out+=' ';}else{out+=ch;}break;}attr=data;}if(attr!==this.defAttr){out+='';}this.children[y].innerHTML=out;}if(parent){this.element.appendChild(this.rowContainer);}this.emit('refresh',{element:this.element,start:start,end:end});};/** + * Display the cursor element + */Terminal.prototype.showCursor=function(){if(!this.cursorState){this.cursorState=1;this.refresh(this.y,this.y);}};/** + * Scroll the terminal + */Terminal.prototype.scroll=function(){var row;if(++this.ybase===this.scrollback){this.ybase=this.ybase/2|0;this.lines=this.lines.slice(-(this.ybase+this.rows)+1);}if(!this.userScrolling){this.ydisp=this.ybase;}// last line +row=this.ybase+this.rows-1;// subtract the bottom scroll region +row-=this.rows-1-this.scrollBottom;if(row===this.lines.length){// potential optimization: +// pushing is faster than splicing +// when they amount to the same +// behavior. +this.lines.push(this.blankLine());}else{// add our new line +this.lines.splice(row,0,this.blankLine());}if(this.scrollTop!==0){if(this.ybase!==0){this.ybase--;if(!this.userScrolling){this.ydisp=this.ybase;}}this.lines.splice(this.ybase+this.scrollTop,1);}// this.maxRange(); +this.updateRange(this.scrollTop);this.updateRange(this.scrollBottom);this.emit('scroll',this.ydisp);};/** + * Scroll the display of the terminal + * @param {number} disp The number of lines to scroll down (negatives scroll up). + * @param {boolean} suppressScrollEvent Don't emit the scroll event as scrollDisp. This is used + * to avoid unwanted events being handled by the veiwport when the event was triggered from the + * viewport originally. + */Terminal.prototype.scrollDisp=function(disp,suppressScrollEvent){if(disp<0){this.userScrolling=true;}else if(disp+this.ydisp>=this.ybase){this.userScrolling=false;}this.ydisp+=disp;if(this.ydisp>this.ybase){this.ydisp=this.ybase;}else if(this.ydisp<0){this.ydisp=0;}if(!suppressScrollEvent){this.emit('scroll',this.ydisp);}this.refresh(0,this.rows-1);};/** + * Scroll the display of the terminal by a number of pages. + * @param {number} pageCount The number of pages to scroll (negative scrolls up). + */Terminal.prototype.scrollPages=function(pageCount){this.scrollDisp(pageCount*(this.rows-1));};/** + * Scrolls the display of the terminal to the top. + */Terminal.prototype.scrollToTop=function(){this.scrollDisp(-this.ydisp);};/** + * Scrolls the display of the terminal to the bottom. + */Terminal.prototype.scrollToBottom=function(){this.scrollDisp(this.ybase-this.ydisp);};/** + * Writes text to the terminal. + * @param {string} text The text to write to the terminal. + */Terminal.prototype.write=function(data){var l=data.length,i=0,j,cs,ch,code,low,ch_width,row;this.refreshStart=this.y;this.refreshEnd=this.y;// apply leftover surrogate high from last write +if(this.surrogate_high){data=this.surrogate_high+data;this.surrogate_high='';}for(;i maybe move to default +code=data.charCodeAt(i);if(0xD800<=code&&code<=0xDBFF){// we got a surrogate high +// get surrogate low (next 2 bytes) +low=data.charCodeAt(i+1);if(isNaN(low)){// end of data stream, save surrogate high +this.surrogate_high=ch;continue;}code=(code-0xD800)*0x400+(low-0xDC00)+0x10000;ch+=data.charAt(i+1);}// surrogate low - already handled above +if(0xDC00<=code&&code<=0xDFFF)continue;switch(this.state){case normal:switch(ch){case'\x07':this.bell();break;// '\n', '\v', '\f' +case'\n':case'\x0b':case'\x0c':if(this.convertEol){this.x=0;}this.y++;if(this.y>this.scrollBottom){this.y--;this.scroll();}break;// '\r' +case'\r':this.x=0;break;// '\b' +case'\x08':if(this.x>0){this.x--;}break;// '\t' +case'\t':this.x=this.nextStop();break;// shift out +case'\x0e':this.setgLevel(1);break;// shift in +case'\x0f':this.setgLevel(0);break;// '\e' +case'\x1b':this.state=escaped;break;default:// ' ' +// calculate print space +// expensive call, therefore we save width in line buffer +ch_width=wcwidth(code);if(ch>=' '){if(this.charset&&this.charset[ch]){ch=this.charset[ch];}row=this.y+this.ybase;// insert combining char in last cell +// FIXME: needs handling after cursor jumps +if(!ch_width&&this.x){// dont overflow left +if(this.lines[row][this.x-1]){if(!this.lines[row][this.x-1][2]){// found empty cell after fullwidth, need to go 2 cells back +if(this.lines[row][this.x-2])this.lines[row][this.x-2][1]+=ch;}else{this.lines[row][this.x-1][1]+=ch;}this.updateRange(this.y);}break;}// goto next line if ch would overflow +// TODO: needs a global min terminal width of 2 +if(this.x+ch_width-1>=this.cols){// autowrap - DECAWM +if(this.wraparoundMode){this.x=0;this.y++;if(this.y>this.scrollBottom){this.y--;this.scroll();}}else{this.x=this.cols-1;if(ch_width===2)// FIXME: check for xterm behavior +continue;}}row=this.y+this.ybase;// insert mode: move characters to right +if(this.insertMode){// do this twice for a fullwidth char +for(var moves=0;moves Normal Keypad (DECKPNM). +case'>':this.log('Switching back to normal keypad.');this.applicationKeypad=false;this.viewport.syncScrollArea();this.state=normal;break;default:this.state=normal;this.error('Unknown ESC control: %s.',ch);break;}break;case charset:switch(ch){case'0':// DEC Special Character and Line Drawing Set. +cs=Terminal.charsets.SCLD;break;case'A':// UK +cs=Terminal.charsets.UK;break;case'B':// United States (USASCII). +cs=Terminal.charsets.US;break;case'4':// Dutch +cs=Terminal.charsets.Dutch;break;case'C':// Finnish +case'5':cs=Terminal.charsets.Finnish;break;case'R':// French +cs=Terminal.charsets.French;break;case'Q':// FrenchCanadian +cs=Terminal.charsets.FrenchCanadian;break;case'K':// German +cs=Terminal.charsets.German;break;case'Y':// Italian +cs=Terminal.charsets.Italian;break;case'E':// NorwegianDanish +case'6':cs=Terminal.charsets.NorwegianDanish;break;case'Z':// Spanish +cs=Terminal.charsets.Spanish;break;case'H':// Swedish +case'7':cs=Terminal.charsets.Swedish;break;case'=':// Swiss +cs=Terminal.charsets.Swiss;break;case'/':// ISOLatin (actually /A) +cs=Terminal.charsets.ISOLatin;i++;break;default:// Default +cs=Terminal.charsets.US;break;}this.setgCharset(this.gcharset,cs);this.gcharset=null;this.state=normal;break;case osc:// OSC Ps ; Pt ST +// OSC Ps ; Pt BEL +// Set Text Parameters. +if(ch==='\x1b'||ch==='\x07'){if(ch==='\x1b')i++;this.params.push(this.currentParam);switch(this.params[0]){case 0:case 1:case 2:if(this.params[1]){this.title=this.params[1];this.handleTitle(this.title);}break;case 3:// set X property +break;case 4:case 5:// change dynamic colors +break;case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:// change dynamic ui colors +break;case 46:// change log file +break;case 50:// dynamic font +break;case 51:// emacs shell +break;case 52:// manipulate selection data +break;case 104:case 105:case 110:case 111:case 112:case 113:case 114:case 115:case 116:case 117:case 118:// reset colors +break;}this.params=[];this.currentParam=0;this.state=normal;}else{if(!this.params.length){if(ch>='0'&&ch<='9'){this.currentParam=this.currentParam*10+ch.charCodeAt(0)-48;}else if(ch===';'){this.params.push(this.currentParam);this.currentParam='';}}else{this.currentParam+=ch;}}break;case csi:// '?', '>', '!' +if(ch==='?'||ch==='>'||ch==='!'){this.prefix=ch;break;}// 0 - 9 +if(ch>='0'&&ch<='9'){this.currentParam=this.currentParam*10+ch.charCodeAt(0)-48;break;}// '$', '"', ' ', '\'' +if(ch==='$'||ch==='"'||ch===' '||ch==='\''){this.postfix=ch;break;}this.params.push(this.currentParam);this.currentParam=0;// ';' +if(ch===';')break;this.state=normal;switch(ch){// CSI Ps A +// Cursor Up Ps Times (default = 1) (CUU). +case'A':this.cursorUp(this.params);break;// CSI Ps B +// Cursor Down Ps Times (default = 1) (CUD). +case'B':this.cursorDown(this.params);break;// CSI Ps C +// Cursor Forward Ps Times (default = 1) (CUF). +case'C':this.cursorForward(this.params);break;// CSI Ps D +// Cursor Backward Ps Times (default = 1) (CUB). +case'D':this.cursorBackward(this.params);break;// CSI Ps ; Ps H +// Cursor Position [row;column] (default = [1,1]) (CUP). +case'H':this.cursorPos(this.params);break;// CSI Ps J Erase in Display (ED). +case'J':this.eraseInDisplay(this.params);break;// CSI Ps K Erase in Line (EL). +case'K':this.eraseInLine(this.params);break;// CSI Pm m Character Attributes (SGR). +case'm':if(!this.prefix){this.charAttributes(this.params);}break;// CSI Ps n Device Status Report (DSR). +case'n':if(!this.prefix){this.deviceStatus(this.params);}break;/** + * Additions + */// CSI Ps @ +// Insert Ps (Blank) Character(s) (default = 1) (ICH). +case'@':this.insertChars(this.params);break;// CSI Ps E +// Cursor Next Line Ps Times (default = 1) (CNL). +case'E':this.cursorNextLine(this.params);break;// CSI Ps F +// Cursor Preceding Line Ps Times (default = 1) (CNL). +case'F':this.cursorPrecedingLine(this.params);break;// CSI Ps G +// Cursor Character Absolute [column] (default = [row,1]) (CHA). +case'G':this.cursorCharAbsolute(this.params);break;// CSI Ps L +// Insert Ps Line(s) (default = 1) (IL). +case'L':this.insertLines(this.params);break;// CSI Ps M +// Delete Ps Line(s) (default = 1) (DL). +case'M':this.deleteLines(this.params);break;// CSI Ps P +// Delete Ps Character(s) (default = 1) (DCH). +case'P':this.deleteChars(this.params);break;// CSI Ps X +// Erase Ps Character(s) (default = 1) (ECH). +case'X':this.eraseChars(this.params);break;// CSI Pm ` Character Position Absolute +// [column] (default = [row,1]) (HPA). +case'`':this.charPosAbsolute(this.params);break;// 141 61 a * HPR - +// Horizontal Position Relative +case'a':this.HPositionRelative(this.params);break;// CSI P s c +// Send Device Attributes (Primary DA). +// CSI > P s c +// Send Device Attributes (Secondary DA) +case'c':this.sendDeviceAttributes(this.params);break;// CSI Pm d +// Line Position Absolute [row] (default = [1,column]) (VPA). +case'd':this.linePosAbsolute(this.params);break;// 145 65 e * VPR - Vertical Position Relative +case'e':this.VPositionRelative(this.params);break;// CSI Ps ; Ps f +// Horizontal and Vertical Position [row;column] (default = +// [1,1]) (HVP). +case'f':this.HVPosition(this.params);break;// CSI Pm h Set Mode (SM). +// CSI ? Pm h - mouse escape codes, cursor escape codes +case'h':this.setMode(this.params);break;// CSI Pm l Reset Mode (RM). +// CSI ? Pm l +case'l':this.resetMode(this.params);break;// CSI Ps ; Ps r +// Set Scrolling Region [top;bottom] (default = full size of win- +// dow) (DECSTBM). +// CSI ? Pm r +case'r':this.setScrollRegion(this.params);break;// CSI s +// Save cursor (ANSI.SYS). +case's':this.saveCursor(this.params);break;// CSI u +// Restore cursor (ANSI.SYS). +case'u':this.restoreCursor(this.params);break;/** + * Lesser Used + */// CSI Ps I +// Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). +case'I':this.cursorForwardTab(this.params);break;// CSI Ps S Scroll up Ps lines (default = 1) (SU). +case'S':this.scrollUp(this.params);break;// CSI Ps T Scroll down Ps lines (default = 1) (SD). +// CSI Ps ; Ps ; Ps ; Ps ; Ps T +// CSI > Ps; Ps T +case'T':// if (this.prefix === '>') { +// this.resetTitleModes(this.params); +// break; +// } +// if (this.params.length > 2) { +// this.initMouseTracking(this.params); +// break; +// } +if(this.params.length<2&&!this.prefix){this.scrollDown(this.params);}break;// CSI Ps Z +// Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). +case'Z':this.cursorBackwardTab(this.params);break;// CSI Ps b Repeat the preceding graphic character Ps times (REP). +case'b':this.repeatPrecedingCharacter(this.params);break;// CSI Ps g Tab Clear (TBC). +case'g':this.tabClear(this.params);break;// CSI Pm i Media Copy (MC). +// CSI ? Pm i +// case 'i': +// this.mediaCopy(this.params); +// break; +// CSI Pm m Character Attributes (SGR). +// CSI > Ps; Ps m +// case 'm': // duplicate +// if (this.prefix === '>') { +// this.setResources(this.params); +// } else { +// this.charAttributes(this.params); +// } +// break; +// CSI Ps n Device Status Report (DSR). +// CSI > Ps n +// case 'n': // duplicate +// if (this.prefix === '>') { +// this.disableModifiers(this.params); +// } else { +// this.deviceStatus(this.params); +// } +// break; +// CSI > Ps p Set pointer mode. +// CSI ! p Soft terminal reset (DECSTR). +// CSI Ps$ p +// Request ANSI mode (DECRQM). +// CSI ? Ps$ p +// Request DEC private mode (DECRQM). +// CSI Ps ; Ps " p +case'p':switch(this.prefix){// case '>': +// this.setPointerMode(this.params); +// break; +case'!':this.softReset(this.params);break;// case '?': +// if (this.postfix === '$') { +// this.requestPrivateMode(this.params); +// } +// break; +// default: +// if (this.postfix === '"') { +// this.setConformanceLevel(this.params); +// } else if (this.postfix === '$') { +// this.requestAnsiMode(this.params); +// } +// break; +}break;// CSI Ps q Load LEDs (DECLL). +// CSI Ps SP q +// CSI Ps " q +// case 'q': +// if (this.postfix === ' ') { +// this.setCursorStyle(this.params); +// break; +// } +// if (this.postfix === '"') { +// this.setCharProtectionAttr(this.params); +// break; +// } +// this.loadLEDs(this.params); +// break; +// CSI Ps ; Ps r +// Set Scrolling Region [top;bottom] (default = full size of win- +// dow) (DECSTBM). +// CSI ? Pm r +// CSI Pt; Pl; Pb; Pr; Ps$ r +// case 'r': // duplicate +// if (this.prefix === '?') { +// this.restorePrivateValues(this.params); +// } else if (this.postfix === '$') { +// this.setAttrInRectangle(this.params); +// } else { +// this.setScrollRegion(this.params); +// } +// break; +// CSI s Save cursor (ANSI.SYS). +// CSI ? Pm s +// case 's': // duplicate +// if (this.prefix === '?') { +// this.savePrivateValues(this.params); +// } else { +// this.saveCursor(this.params); +// } +// break; +// CSI Ps ; Ps ; Ps t +// CSI Pt; Pl; Pb; Pr; Ps$ t +// CSI > Ps; Ps t +// CSI Ps SP t +// case 't': +// if (this.postfix === '$') { +// this.reverseAttrInRectangle(this.params); +// } else if (this.postfix === ' ') { +// this.setWarningBellVolume(this.params); +// } else { +// if (this.prefix === '>') { +// this.setTitleModeFeature(this.params); +// } else { +// this.manipulateWindow(this.params); +// } +// } +// break; +// CSI u Restore cursor (ANSI.SYS). +// CSI Ps SP u +// case 'u': // duplicate +// if (this.postfix === ' ') { +// this.setMarginBellVolume(this.params); +// } else { +// this.restoreCursor(this.params); +// } +// break; +// CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v +// case 'v': +// if (this.postfix === '$') { +// this.copyRectagle(this.params); +// } +// break; +// CSI Pt ; Pl ; Pb ; Pr ' w +// case 'w': +// if (this.postfix === '\'') { +// this.enableFilterRectangle(this.params); +// } +// break; +// CSI Ps x Request Terminal Parameters (DECREQTPARM). +// CSI Ps x Select Attribute Change Extent (DECSACE). +// CSI Pc; Pt; Pl; Pb; Pr$ x +// case 'x': +// if (this.postfix === '$') { +// this.fillRectangle(this.params); +// } else { +// this.requestParameters(this.params); +// //this.__(this.params); +// } +// break; +// CSI Ps ; Pu ' z +// CSI Pt; Pl; Pb; Pr$ z +// case 'z': +// if (this.postfix === '\'') { +// this.enableLocatorReporting(this.params); +// } else if (this.postfix === '$') { +// this.eraseRectangle(this.params); +// } +// break; +// CSI Pm ' { +// CSI Pt; Pl; Pb; Pr$ { +// case '{': +// if (this.postfix === '\'') { +// this.setLocatorEvents(this.params); +// } else if (this.postfix === '$') { +// this.selectiveEraseRectangle(this.params); +// } +// break; +// CSI Ps ' | +// case '|': +// if (this.postfix === '\'') { +// this.requestLocatorPosition(this.params); +// } +// break; +// CSI P m SP } +// Insert P s Column(s) (default = 1) (DECIC), VT420 and up. +// case '}': +// if (this.postfix === ' ') { +// this.insertColumns(this.params); +// } +// break; +// CSI P m SP ~ +// Delete P s Column(s) (default = 1) (DECDC), VT420 and up +// case '~': +// if (this.postfix === ' ') { +// this.deleteColumns(this.params); +// } +// break; +default:this.error('Unknown CSI code: %s.',ch);break;}this.prefix='';this.postfix='';break;case dcs:if(ch==='\x1b'||ch==='\x07'){if(ch==='\x1b')i++;switch(this.prefix){// User-Defined Keys (DECUDK). +case'':break;// Request Status String (DECRQSS). +// test: echo -e '\eP$q"p\e\\' +case'$q':var pt=this.currentParam,valid=false;switch(pt){// DECSCA +case'"q':pt='0"q';break;// DECSCL +case'"p':pt='61"p';break;// DECSTBM +case'r':pt=''+(this.scrollTop+1)+';'+(this.scrollBottom+1)+'r';break;// SGR +case'm':pt='0m';break;default:this.error('Unknown DCS Pt: %s.',pt);pt='';break;}this.send('\x1bP'+ +valid+'$r'+pt+'\x1b\\');break;// Set Termcap/Terminfo Data (xterm, experimental). +case'+p':break;// Request Termcap/Terminfo String (xterm, experimental) +// Regular xterm does not even respond to this sequence. +// This can cause a small glitch in vim. +// test: echo -ne '\eP+q6b64\e\\' +case'+q':var pt=this.currentParam,valid=false;this.send('\x1bP'+ +valid+'+r'+pt+'\x1b\\');break;default:this.error('Unknown DCS prefix: %s.',this.prefix);break;}this.currentParam=0;this.prefix='';this.state=normal;}else if(!this.currentParam){if(!this.prefix&&ch!=='$'&&ch!=='+'){this.currentParam=ch;}else if(this.prefix.length===2){this.currentParam=ch;}else{this.prefix+=ch;}}else{this.currentParam+=ch;}break;case ignore:// For PM and APC. +if(ch==='\x1b'||ch==='\x07'){if(ch==='\x1b')i++;this.state=normal;}break;}}this.updateRange(this.y);this.refresh(this.refreshStart,this.refreshEnd);};/** + * Writes text to the terminal, followed by a break line character (\n). + * @param {string} text The text to write to the terminal. + */Terminal.prototype.writeln=function(data){this.write(data+'\r\n');};/** + * Attaches a custom keydown handler which is run before keys are processed, giving consumers of + * xterm.js ultimate control as to what keys should be processed by the terminal and what keys + * should not. + * @param {function} customKeydownHandler The custom KeyboardEvent handler to attach. This is a + * function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent + * the default action. The function returns whether the event should be processed by xterm.js. + */Terminal.prototype.attachCustomKeydownHandler=function(customKeydownHandler){this.customKeydownHandler=customKeydownHandler;};/** + * Handle a keydown event + * Key Resources: + * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent + * @param {KeyboardEvent} ev The keydown event to be handled. + */Terminal.prototype.keyDown=function(ev){// Scroll down to prompt, whenever the user presses a key. +if(this.ybase!==this.ydisp){this.scrollToBottom();}if(this.customKeydownHandler&&this.customKeydownHandler(ev)===false){return false;}if(!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)){return false;}var self=this;var result=this.evaluateKeyEscapeSequence(ev);if(result.scrollDisp){this.scrollDisp(result.scrollDisp);return this.cancel(ev,true);}if(isThirdLevelShift(this,ev)){return true;}if(result.cancel){// The event is canceled at the end already, is this necessary? +this.cancel(ev,true);}if(!result.key){return true;}this.emit('keydown',ev);this.emit('key',result.key,ev);this.showCursor();this.handler(result.key);return this.cancel(ev,true);};/** + * Returns an object that determines how a KeyboardEvent should be handled. The key of the + * returned value is the new key code to pass to the PTY. + * + * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * @param {KeyboardEvent} ev The keyboard event to be translated to key escape sequence. + */Terminal.prototype.evaluateKeyEscapeSequence=function(ev){var result={// Whether to cancel event propogation (NOTE: this may not be needed since the event is +// canceled at the end of keyDown +cancel:false,// The new key even to emit +key:undefined,// The number of characters to scroll, if this is defined it will cancel the event +scrollDisp:undefined};var modifiers=ev.shiftKey<<0|ev.altKey<<1|ev.ctrlKey<<2|ev.metaKey<<3;switch(ev.keyCode){case 8:// backspace +if(ev.shiftKey){result.key='\x08';// ^H +break;}result.key='\x7f';// ^? +break;case 9:// tab +if(ev.shiftKey){result.key='\x1b[Z';break;}result.key='\t';result.cancel=true;break;case 13:// return/enter +result.key='\r';result.cancel=true;break;case 27:// escape +result.key='\x1b';result.cancel=true;break;case 37:// left-arrow +if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'D';// HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards +// http://unix.stackexchange.com/a/108106 +if(result.key=='\x1b[1;3D'){result.key='\x1b[1;5D';}}else if(this.applicationCursor){result.key='\x1bOD';}else{result.key='\x1b[D';}break;case 39:// right-arrow +if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'C';// HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward +// http://unix.stackexchange.com/a/108106 +if(result.key=='\x1b[1;3C'){result.key='\x1b[1;5C';}}else if(this.applicationCursor){result.key='\x1bOC';}else{result.key='\x1b[C';}break;case 38:// up-arrow +if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'A';// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow +// http://unix.stackexchange.com/a/108106 +if(result.key=='\x1b[1;3A'){result.key='\x1b[1;5A';}}else if(this.applicationCursor){result.key='\x1bOA';}else{result.key='\x1b[A';}break;case 40:// down-arrow +if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'B';// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow +// http://unix.stackexchange.com/a/108106 +if(result.key=='\x1b[1;3B'){result.key='\x1b[1;5B';}}else if(this.applicationCursor){result.key='\x1bOB';}else{result.key='\x1b[B';}break;case 45:// insert +if(!ev.shiftKey&&!ev.ctrlKey){// or + are used to +// copy-paste on some systems. +result.key='\x1b[2~';}break;case 46:// delete +if(modifiers){result.key='\x1b[3;'+(modifiers+1)+'~';}else{result.key='\x1b[3~';}break;case 36:// home +if(modifiers)result.key='\x1b[1;'+(modifiers+1)+'H';else if(this.applicationCursor)result.key='\x1bOH';else result.key='\x1b[H';break;case 35:// end +if(modifiers)result.key='\x1b[1;'+(modifiers+1)+'F';else if(this.applicationCursor)result.key='\x1bOF';else result.key='\x1b[F';break;case 33:// page up +if(ev.shiftKey){result.scrollDisp=-(this.rows-1);}else{result.key='\x1b[5~';}break;case 34:// page down +if(ev.shiftKey){result.scrollDisp=this.rows-1;}else{result.key='\x1b[6~';}break;case 112:// F1-F12 +if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'P';}else{result.key='\x1bOP';}break;case 113:if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'Q';}else{result.key='\x1bOQ';}break;case 114:if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'R';}else{result.key='\x1bOR';}break;case 115:if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'S';}else{result.key='\x1bOS';}break;case 116:if(modifiers){result.key='\x1b[15;'+(modifiers+1)+'~';}else{result.key='\x1b[15~';}break;case 117:if(modifiers){result.key='\x1b[17;'+(modifiers+1)+'~';}else{result.key='\x1b[17~';}break;case 118:if(modifiers){result.key='\x1b[18;'+(modifiers+1)+'~';}else{result.key='\x1b[18~';}break;case 119:if(modifiers){result.key='\x1b[19;'+(modifiers+1)+'~';}else{result.key='\x1b[19~';}break;case 120:if(modifiers){result.key='\x1b[20;'+(modifiers+1)+'~';}else{result.key='\x1b[20~';}break;case 121:if(modifiers){result.key='\x1b[21;'+(modifiers+1)+'~';}else{result.key='\x1b[21~';}break;case 122:if(modifiers){result.key='\x1b[23;'+(modifiers+1)+'~';}else{result.key='\x1b[23~';}break;case 123:if(modifiers){result.key='\x1b[24;'+(modifiers+1)+'~';}else{result.key='\x1b[24~';}break;default:// a-z and space +if(ev.ctrlKey&&!ev.shiftKey&&!ev.altKey&&!ev.metaKey){if(ev.keyCode>=65&&ev.keyCode<=90){result.key=String.fromCharCode(ev.keyCode-64);}else if(ev.keyCode===32){// NUL +result.key=String.fromCharCode(0);}else if(ev.keyCode>=51&&ev.keyCode<=55){// escape, file sep, group sep, record sep, unit sep +result.key=String.fromCharCode(ev.keyCode-51+27);}else if(ev.keyCode===56){// delete +result.key=String.fromCharCode(127);}else if(ev.keyCode===219){// ^[ - escape +result.key=String.fromCharCode(27);}else if(ev.keyCode===221){// ^] - group sep +result.key=String.fromCharCode(29);}}else if(!this.browser.isMac&&ev.altKey&&!ev.ctrlKey&&!ev.metaKey){// On Mac this is a third level shift. Use instead. +if(ev.keyCode>=65&&ev.keyCode<=90){result.key='\x1b'+String.fromCharCode(ev.keyCode+32);}else if(ev.keyCode===192){result.key='\x1b`';}else if(ev.keyCode>=48&&ev.keyCode<=57){result.key='\x1b'+(ev.keyCode-48);}}break;}return result;};/** + * Set the G level of the terminal + * @param g + */Terminal.prototype.setgLevel=function(g){this.glevel=g;this.charset=this.charsets[g];};/** + * Set the charset for the given G level of the terminal + * @param g + * @param charset + */Terminal.prototype.setgCharset=function(g,charset){this.charsets[g]=charset;if(this.glevel===g){this.charset=charset;}};/** + * Handle a keypress event. + * Key Resources: + * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent + * @param {KeyboardEvent} ev The keypress event to be handled. + */Terminal.prototype.keyPress=function(ev){var key;this.cancel(ev);if(ev.charCode){key=ev.charCode;}else if(ev.which==null){key=ev.keyCode;}else if(ev.which!==0&&ev.charCode!==0){key=ev.which;}else{return false;}if(!key||(ev.altKey||ev.ctrlKey||ev.metaKey)&&!isThirdLevelShift(this,ev)){return false;}key=String.fromCharCode(key);this.emit('keypress',key,ev);this.emit('key',key,ev);this.showCursor();this.handler(key);return false;};/** + * Send data for handling to the terminal + * @param {string} data + */Terminal.prototype.send=function(data){var self=this;if(!this.queue){setTimeout(function(){self.handler(self.queue);self.queue='';},1);}this.queue+=data;};/** + * Ring the bell. + * Note: We could do sweet things with webaudio here + */Terminal.prototype.bell=function(){if(!this.visualBell)return;var self=this;this.element.style.borderColor='white';setTimeout(function(){self.element.style.borderColor='';},10);if(this.popOnBell)this.focus();};/** + * Log the current state to the console. + */Terminal.prototype.log=function(){if(!this.debug)return;if(!this.context.console||!this.context.console.log)return;var args=Array.prototype.slice.call(arguments);this.context.console.log.apply(this.context.console,args);};/** + * Log the current state as error to the console. + */Terminal.prototype.error=function(){if(!this.debug)return;if(!this.context.console||!this.context.console.error)return;var args=Array.prototype.slice.call(arguments);this.context.console.error.apply(this.context.console,args);};/** + * Resizes the terminal. + * + * @param {number} x The number of columns to resize to. + * @param {number} y The number of rows to resize to. + */Terminal.prototype.resize=function(x,y){var line,el,i,j,ch,addToY;if(x===this.cols&&y===this.rows){return;}if(x<1)x=1;if(y<1)y=1;// resize cols +j=this.cols;if(j x) +i=this.lines.length;while(i--){while(this.lines[i].length>x){this.lines[i].pop();}}}this.setupStops(j);this.cols=x;// resize rows +j=this.rows;addToY=0;if(j0&&this.lines.length<=this.ybase+this.y+addToY+1){// There is room above the buffer and there are no empty elements below the line, +// scroll up +this.ybase--;addToY++;if(this.ydisp>0){// Viewport is at the top of the buffer, must increase downwards +this.ydisp--;}}else{// Add a blank line if there is no buffer left at the top to scroll to, or if there +// are blank lines after the cursor +this.lines.push(this.blankLine());}}if(this.children.length y) +while(j-->y){if(this.lines.length>y+this.ybase){if(this.lines.length>this.ybase+this.y+1){// The line is a blank line below the cursor, remove it +this.lines.pop();}else{// The line is the cursor, scroll down +this.ybase++;this.ydisp++;}}if(this.children.length>y){el=this.children.shift();if(!el)continue;el.parentNode.removeChild(el);}}}this.rows=y;// Make sure that the cursor stays on screen +if(this.y>=y){this.y=y-1;}if(addToY){this.y+=addToY;}if(this.x>=x){this.x=x-1;}this.scrollTop=0;this.scrollBottom=y-1;this.refresh(0,this.rows-1);this.normal=null;this.geometry=[this.cols,this.rows];this.emit('resize',{terminal:this,cols:x,rows:y});};/** + * Updates the range of rows to refresh + * @param {number} y The number of rows to refresh next. + */Terminal.prototype.updateRange=function(y){if(ythis.refreshEnd)this.refreshEnd=y;// if (y > this.refreshEnd) { +// this.refreshEnd = y; +// if (y > this.rows - 1) { +// this.refreshEnd = this.rows - 1; +// } +// } +};/** + * Set the range of refreshing to the maximum value + */Terminal.prototype.maxRange=function(){this.refreshStart=0;this.refreshEnd=this.rows-1;};/** + * Setup the tab stops. + * @param {number} i + */Terminal.prototype.setupStops=function(i){if(i!=null){if(!this.tabs[i]){i=this.prevStop(i);}}else{this.tabs={};i=0;}for(;i0){}return x>=this.cols?this.cols-1:x<0?0:x;};/** + * Move the cursor one tab stop forward from the given position (default is current). + * @param {number} x The position to move the cursor one tab stop forward. + */Terminal.prototype.nextStop=function(x){if(x==null)x=this.x;while(!this.tabs[++x]&&x=this.cols?this.cols-1:x<0?0:x;};/** + * Erase in the identified line everything from "x" to the end of the line (right). + * @param {number} x The column from which to start erasing to the end of the line. + * @param {number} y The line in which to operate. + */Terminal.prototype.eraseRight=function(x,y){var line=this.lines[this.ybase+y],ch=[this.eraseAttr(),' ',1];// xterm +for(;xthis.scrollBottom){this.y--;this.scroll();}this.state=normal;};/** + * ESC M Reverse Index (RI is 0x8d). + */Terminal.prototype.reverseIndex=function(){var j;this.y--;if(this.y=this.rows){this.y=this.rows-1;}};/** + * CSI Ps C + * Cursor Forward Ps Times (default = 1) (CUF). + */Terminal.prototype.cursorForward=function(params){var param=params[0];if(param<1)param=1;this.x+=param;if(this.x>=this.cols){this.x=this.cols-1;}};/** + * CSI Ps D + * Cursor Backward Ps Times (default = 1) (CUB). + */Terminal.prototype.cursorBackward=function(params){var param=params[0];if(param<1)param=1;this.x-=param;if(this.x<0)this.x=0;};/** + * CSI Ps ; Ps H + * Cursor Position [row;column] (default = [1,1]) (CUP). + */Terminal.prototype.cursorPos=function(params){var row,col;row=params[0]-1;if(params.length>=2){col=params[1]-1;}else{col=0;}if(row<0){row=0;}else if(row>=this.rows){row=this.rows-1;}if(col<0){col=0;}else if(col>=this.cols){col=this.cols-1;}this.x=col;this.y=row;};/** + * CSI Ps J Erase in Display (ED). + * Ps = 0 -> Erase Below (default). + * Ps = 1 -> Erase Above. + * Ps = 2 -> Erase All. + * Ps = 3 -> Erase Saved Lines (xterm). + * CSI ? Ps J + * Erase in Display (DECSED). + * Ps = 0 -> Selective Erase Below (default). + * Ps = 1 -> Selective Erase Above. + * Ps = 2 -> Selective Erase All. + */Terminal.prototype.eraseInDisplay=function(params){var j;switch(params[0]){case 0:this.eraseRight(this.x,this.y);j=this.y+1;for(;j Erase to Right (default). + * Ps = 1 -> Erase to Left. + * Ps = 2 -> Erase All. + * CSI ? Ps K + * Erase in Line (DECSEL). + * Ps = 0 -> Selective Erase to Right (default). + * Ps = 1 -> Selective Erase to Left. + * Ps = 2 -> Selective Erase All. + */Terminal.prototype.eraseInLine=function(params){switch(params[0]){case 0:this.eraseRight(this.x,this.y);break;case 1:this.eraseLeft(this.x,this.y);break;case 2:this.eraseLine(this.y);break;}};/** + * CSI Pm m Character Attributes (SGR). + * Ps = 0 -> Normal (default). + * Ps = 1 -> Bold. + * Ps = 4 -> Underlined. + * Ps = 5 -> Blink (appears as Bold). + * Ps = 7 -> Inverse. + * Ps = 8 -> Invisible, i.e., hidden (VT300). + * Ps = 2 2 -> Normal (neither bold nor faint). + * Ps = 2 4 -> Not underlined. + * Ps = 2 5 -> Steady (not blinking). + * Ps = 2 7 -> Positive (not inverse). + * Ps = 2 8 -> Visible, i.e., not hidden (VT300). + * Ps = 3 0 -> Set foreground color to Black. + * Ps = 3 1 -> Set foreground color to Red. + * Ps = 3 2 -> Set foreground color to Green. + * Ps = 3 3 -> Set foreground color to Yellow. + * Ps = 3 4 -> Set foreground color to Blue. + * Ps = 3 5 -> Set foreground color to Magenta. + * Ps = 3 6 -> Set foreground color to Cyan. + * Ps = 3 7 -> Set foreground color to White. + * Ps = 3 9 -> Set foreground color to default (original). + * Ps = 4 0 -> Set background color to Black. + * Ps = 4 1 -> Set background color to Red. + * Ps = 4 2 -> Set background color to Green. + * Ps = 4 3 -> Set background color to Yellow. + * Ps = 4 4 -> Set background color to Blue. + * Ps = 4 5 -> Set background color to Magenta. + * Ps = 4 6 -> Set background color to Cyan. + * Ps = 4 7 -> Set background color to White. + * Ps = 4 9 -> Set background color to default (original). + * + * If 16-color support is compiled, the following apply. Assume + * that xterm's resources are set so that the ISO color codes are + * the first 8 of a set of 16. Then the aixterm colors are the + * bright versions of the ISO colors: + * Ps = 9 0 -> Set foreground color to Black. + * Ps = 9 1 -> Set foreground color to Red. + * Ps = 9 2 -> Set foreground color to Green. + * Ps = 9 3 -> Set foreground color to Yellow. + * Ps = 9 4 -> Set foreground color to Blue. + * Ps = 9 5 -> Set foreground color to Magenta. + * Ps = 9 6 -> Set foreground color to Cyan. + * Ps = 9 7 -> Set foreground color to White. + * Ps = 1 0 0 -> Set background color to Black. + * Ps = 1 0 1 -> Set background color to Red. + * Ps = 1 0 2 -> Set background color to Green. + * Ps = 1 0 3 -> Set background color to Yellow. + * Ps = 1 0 4 -> Set background color to Blue. + * Ps = 1 0 5 -> Set background color to Magenta. + * Ps = 1 0 6 -> Set background color to Cyan. + * Ps = 1 0 7 -> Set background color to White. + * + * If xterm is compiled with the 16-color support disabled, it + * supports the following, from rxvt: + * Ps = 1 0 0 -> Set foreground and background color to + * default. + * + * If 88- or 256-color support is compiled, the following apply. + * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second + * Ps. + * Ps = 4 8 ; 5 ; Ps -> Set background color to the second + * Ps. + */Terminal.prototype.charAttributes=function(params){// Optimize a single SGR0. +if(params.length===1&¶ms[0]===0){this.curAttr=this.defAttr;return;}var l=params.length,i=0,flags=this.curAttr>>18,fg=this.curAttr>>9&0x1ff,bg=this.curAttr&0x1ff,p;for(;i=30&&p<=37){// fg color 8 +fg=p-30;}else if(p>=40&&p<=47){// bg color 8 +bg=p-40;}else if(p>=90&&p<=97){// fg color 16 +p+=8;fg=p-90;}else if(p>=100&&p<=107){// bg color 16 +p+=8;bg=p-100;}else if(p===0){// default +flags=this.defAttr>>18;fg=this.defAttr>>9&0x1ff;bg=this.defAttr&0x1ff;// flags = 0; +// fg = 0x1ff; +// bg = 0x1ff; +}else if(p===1){// bold text +flags|=1;}else if(p===4){// underlined text +flags|=2;}else if(p===5){// blink +flags|=4;}else if(p===7){// inverse and positive +// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' +flags|=8;}else if(p===8){// invisible +flags|=16;}else if(p===22){// not bold +flags&=~1;}else if(p===24){// not underlined +flags&=~2;}else if(p===25){// not blink +flags&=~4;}else if(p===27){// not inverse +flags&=~8;}else if(p===28){// not invisible +flags&=~16;}else if(p===39){// reset fg +fg=this.defAttr>>9&0x1ff;}else if(p===49){// reset bg +bg=this.defAttr&0x1ff;}else if(p===38){// fg color 256 +if(params[i+1]===2){i+=2;fg=matchColor(params[i]&0xff,params[i+1]&0xff,params[i+2]&0xff);if(fg===-1)fg=0x1ff;i+=2;}else if(params[i+1]===5){i+=2;p=params[i]&0xff;fg=p;}}else if(p===48){// bg color 256 +if(params[i+1]===2){i+=2;bg=matchColor(params[i]&0xff,params[i+1]&0xff,params[i+2]&0xff);if(bg===-1)bg=0x1ff;i+=2;}else if(params[i+1]===5){i+=2;p=params[i]&0xff;bg=p;}}else if(p===100){// reset fg/bg +fg=this.defAttr>>9&0x1ff;bg=this.defAttr&0x1ff;}else{this.error('Unknown SGR attribute: %d.',p);}}this.curAttr=flags<<18|fg<<9|bg;};/** + * CSI Ps n Device Status Report (DSR). + * Ps = 5 -> Status Report. Result (``OK'') is + * CSI 0 n + * Ps = 6 -> Report Cursor Position (CPR) [row;column]. + * Result is + * CSI r ; c R + * CSI ? Ps n + * Device Status Report (DSR, DEC-specific). + * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI + * ? r ; c R (assumes page is zero). + * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). + * or CSI ? 1 1 n (not ready). + * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) + * or CSI ? 2 1 n (locked). + * Ps = 2 6 -> Report Keyboard status as + * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). + * The last two parameters apply to VT400 & up, and denote key- + * board ready and LK01 respectively. + * Ps = 5 3 -> Report Locator status as + * CSI ? 5 3 n Locator available, if compiled-in, or + * CSI ? 5 0 n No Locator, if not. + */Terminal.prototype.deviceStatus=function(params){if(!this.prefix){switch(params[0]){case 5:// status report +this.send('\x1b[0n');break;case 6:// cursor position +this.send('\x1b['+(this.y+1)+';'+(this.x+1)+'R');break;}}else if(this.prefix==='?'){// modern xterm doesnt seem to +// respond to any of these except ?6, 6, and 5 +switch(params[0]){case 6:// cursor position +this.send('\x1b[?'+(this.y+1)+';'+(this.x+1)+'R');break;case 15:// no printer +// this.send('\x1b[?11n'); +break;case 25:// dont support user defined keys +// this.send('\x1b[?21n'); +break;case 26:// north american keyboard +// this.send('\x1b[?27;1;0;0n'); +break;case 53:// no dec locator/mouse +// this.send('\x1b[?50n'); +break;}}};/** + * Additions + *//** + * CSI Ps @ + * Insert Ps (Blank) Character(s) (default = 1) (ICH). + */Terminal.prototype.insertChars=function(params){var param,row,j,ch;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.x;ch=[this.eraseAttr(),' ',1];// xterm +while(param--&&j=this.rows){this.y=this.rows-1;}this.x=0;};/** + * CSI Ps F + * Cursor Preceding Line Ps Times (default = 1) (CNL). + * reuse CSI Ps A ? + */Terminal.prototype.cursorPrecedingLine=function(params){var param=params[0];if(param<1)param=1;this.y-=param;if(this.y<0)this.y=0;this.x=0;};/** + * CSI Ps G + * Cursor Character Absolute [column] (default = [row,1]) (CHA). + */Terminal.prototype.cursorCharAbsolute=function(params){var param=params[0];if(param<1)param=1;this.x=param-1;};/** + * CSI Ps L + * Insert Ps Line(s) (default = 1) (IL). + */Terminal.prototype.insertLines=function(params){var param,row,j;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.rows-1-this.scrollBottom;j=this.rows-1+this.ybase-j+1;while(param--){// test: echo -e '\e[44m\e[1L\e[0m' +// blankLine(true) - xterm/linux behavior +this.lines.splice(row,0,this.blankLine(true));this.lines.splice(j,1);}// this.maxRange(); +this.updateRange(this.y);this.updateRange(this.scrollBottom);};/** + * CSI Ps M + * Delete Ps Line(s) (default = 1) (DL). + */Terminal.prototype.deleteLines=function(params){var param,row,j;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.rows-1-this.scrollBottom;j=this.rows-1+this.ybase-j;while(param--){// test: echo -e '\e[44m\e[1M\e[0m' +// blankLine(true) - xterm/linux behavior +this.lines.splice(j+1,0,this.blankLine(true));this.lines.splice(row,1);}// this.maxRange(); +this.updateRange(this.y);this.updateRange(this.scrollBottom);};/** + * CSI Ps P + * Delete Ps Character(s) (default = 1) (DCH). + */Terminal.prototype.deleteChars=function(params){var param,row,ch;param=params[0];if(param<1)param=1;row=this.y+this.ybase;ch=[this.eraseAttr(),' ',1];// xterm +while(param--){this.lines[row].splice(this.x,1);this.lines[row].push(ch);}};/** + * CSI Ps X + * Erase Ps Character(s) (default = 1) (ECH). + */Terminal.prototype.eraseChars=function(params){var param,row,j,ch;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.x;ch=[this.eraseAttr(),' ',1];// xterm +while(param--&&j=this.cols){this.x=this.cols-1;}};/** + * 141 61 a * HPR - + * Horizontal Position Relative + * reuse CSI Ps C ? + */Terminal.prototype.HPositionRelative=function(params){var param=params[0];if(param<1)param=1;this.x+=param;if(this.x>=this.cols){this.x=this.cols-1;}};/** + * CSI Ps c Send Device Attributes (Primary DA). + * Ps = 0 or omitted -> request attributes from terminal. The + * response depends on the decTerminalID resource setting. + * -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'') + * -> CSI ? 1 ; 0 c (``VT101 with No Options'') + * -> CSI ? 6 c (``VT102'') + * -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'') + * The VT100-style response parameters do not mean anything by + * themselves. VT220 parameters do, telling the host what fea- + * tures the terminal supports: + * Ps = 1 -> 132-columns. + * Ps = 2 -> Printer. + * Ps = 6 -> Selective erase. + * Ps = 8 -> User-defined keys. + * Ps = 9 -> National replacement character sets. + * Ps = 1 5 -> Technical characters. + * Ps = 2 2 -> ANSI color, e.g., VT525. + * Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). + * CSI > Ps c + * Send Device Attributes (Secondary DA). + * Ps = 0 or omitted -> request the terminal's identification + * code. The response depends on the decTerminalID resource set- + * ting. It should apply only to VT220 and up, but xterm extends + * this to VT100. + * -> CSI > Pp ; Pv ; Pc c + * where Pp denotes the terminal type + * Pp = 0 -> ``VT100''. + * Pp = 1 -> ``VT220''. + * and Pv is the firmware version (for xterm, this was originally + * the XFree86 patch number, starting with 95). In a DEC termi- + * nal, Pc indicates the ROM cartridge registration number and is + * always zero. + * More information: + * xterm/charproc.c - line 2012, for more information. + * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) + */Terminal.prototype.sendDeviceAttributes=function(params){if(params[0]>0)return;if(!this.prefix){if(this.is('xterm')||this.is('rxvt-unicode')||this.is('screen')){this.send('\x1b[?1;2c');}else if(this.is('linux')){this.send('\x1b[?6c');}}else if(this.prefix==='>'){// xterm and urxvt +// seem to spit this +// out around ~370 times (?). +if(this.is('xterm')){this.send('\x1b[>0;276;0c');}else if(this.is('rxvt-unicode')){this.send('\x1b[>85;95;0c');}else if(this.is('linux')){// not supported by linux console. +// linux console echoes parameters. +this.send(params[0]+'c');}else if(this.is('screen')){this.send('\x1b[>83;40003;0c');}}};/** + * CSI Pm d + * Line Position Absolute [row] (default = [1,column]) (VPA). + */Terminal.prototype.linePosAbsolute=function(params){var param=params[0];if(param<1)param=1;this.y=param-1;if(this.y>=this.rows){this.y=this.rows-1;}};/** + * 145 65 e * VPR - Vertical Position Relative + * reuse CSI Ps B ? + */Terminal.prototype.VPositionRelative=function(params){var param=params[0];if(param<1)param=1;this.y+=param;if(this.y>=this.rows){this.y=this.rows-1;}};/** + * CSI Ps ; Ps f + * Horizontal and Vertical Position [row;column] (default = + * [1,1]) (HVP). + */Terminal.prototype.HVPosition=function(params){if(params[0]<1)params[0]=1;if(params[1]<1)params[1]=1;this.y=params[0]-1;if(this.y>=this.rows){this.y=this.rows-1;}this.x=params[1]-1;if(this.x>=this.cols){this.x=this.cols-1;}};/** + * CSI Pm h Set Mode (SM). + * Ps = 2 -> Keyboard Action Mode (AM). + * Ps = 4 -> Insert Mode (IRM). + * Ps = 1 2 -> Send/receive (SRM). + * Ps = 2 0 -> Automatic Newline (LNM). + * CSI ? Pm h + * DEC Private Mode Set (DECSET). + * Ps = 1 -> Application Cursor Keys (DECCKM). + * Ps = 2 -> Designate USASCII for character sets G0-G3 + * (DECANM), and set VT100 mode. + * Ps = 3 -> 132 Column Mode (DECCOLM). + * Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). + * Ps = 5 -> Reverse Video (DECSCNM). + * Ps = 6 -> Origin Mode (DECOM). + * Ps = 7 -> Wraparound Mode (DECAWM). + * Ps = 8 -> Auto-repeat Keys (DECARM). + * Ps = 9 -> Send Mouse X & Y on button press. See the sec- + * tion Mouse Tracking. + * Ps = 1 0 -> Show toolbar (rxvt). + * Ps = 1 2 -> Start Blinking Cursor (att610). + * Ps = 1 8 -> Print form feed (DECPFF). + * Ps = 1 9 -> Set print extent to full screen (DECPEX). + * Ps = 2 5 -> Show Cursor (DECTCEM). + * Ps = 3 0 -> Show scrollbar (rxvt). + * Ps = 3 5 -> Enable font-shifting functions (rxvt). + * Ps = 3 8 -> Enter Tektronix Mode (DECTEK). + * Ps = 4 0 -> Allow 80 -> 132 Mode. + * Ps = 4 1 -> more(1) fix (see curses resource). + * Ps = 4 2 -> Enable Nation Replacement Character sets (DECN- + * RCM). + * Ps = 4 4 -> Turn On Margin Bell. + * Ps = 4 5 -> Reverse-wraparound Mode. + * Ps = 4 6 -> Start Logging. This is normally disabled by a + * compile-time option. + * Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- + * abled by the titeInhibit resource). + * Ps = 6 6 -> Application keypad (DECNKM). + * Ps = 6 7 -> Backarrow key sends backspace (DECBKM). + * Ps = 1 0 0 0 -> Send Mouse X & Y on button press and + * release. See the section Mouse Tracking. + * Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. + * Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. + * Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. + * Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. + * Ps = 1 0 0 5 -> Enable Extended Mouse Mode. + * Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). + * Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). + * Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. + * (enables the eightBitInput resource). + * Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- + * Lock keys. (This enables the numLock resource). + * Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This + * enables the metaSendsEscape resource). + * Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete + * key. + * Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This + * enables the altSendsEscape resource). + * Ps = 1 0 4 0 -> Keep selection even if not highlighted. + * (This enables the keepSelection resource). + * Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables + * the selectToClipboard resource). + * Ps = 1 0 4 2 -> Enable Urgency window manager hint when + * Control-G is received. (This enables the bellIsUrgent + * resource). + * Ps = 1 0 4 3 -> Enable raising of the window when Control-G + * is received. (enables the popOnBell resource). + * Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be + * disabled by the titeInhibit resource). + * Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- + * abled by the titeInhibit resource). + * Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate + * Screen Buffer, clearing it first. (This may be disabled by + * the titeInhibit resource). This combines the effects of the 1 + * 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based + * applications rather than the 4 7 mode. + * Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. + * Ps = 1 0 5 1 -> Set Sun function-key mode. + * Ps = 1 0 5 2 -> Set HP function-key mode. + * Ps = 1 0 5 3 -> Set SCO function-key mode. + * Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). + * Ps = 1 0 6 1 -> Set VT220 keyboard emulation. + * Ps = 2 0 0 4 -> Set bracketed paste mode. + * Modes: + * http: *vt100.net/docs/vt220-rm/chapter4.html + */Terminal.prototype.setMode=function(params){if((typeof params==='undefined'?'undefined':_typeof(params))==='object'){var l=params.length,i=0;for(;i1000;this.mouseEvents=true;this.element.style.cursor='default';this.log('Binding to mouse events.');break;case 1004:// send focusin/focusout events +// focusin: ^[[I +// focusout: ^[[O +this.sendFocus=true;break;case 1005:// utf8 ext mode mouse +this.utfMouse=true;// for wide terminals +// simply encodes large values as utf8 characters +break;case 1006:// sgr ext mode mouse +this.sgrMouse=true;// for wide terminals +// does not add 32 to fields +// press: ^[[ Keyboard Action Mode (AM). + * Ps = 4 -> Replace Mode (IRM). + * Ps = 1 2 -> Send/receive (SRM). + * Ps = 2 0 -> Normal Linefeed (LNM). + * CSI ? Pm l + * DEC Private Mode Reset (DECRST). + * Ps = 1 -> Normal Cursor Keys (DECCKM). + * Ps = 2 -> Designate VT52 mode (DECANM). + * Ps = 3 -> 80 Column Mode (DECCOLM). + * Ps = 4 -> Jump (Fast) Scroll (DECSCLM). + * Ps = 5 -> Normal Video (DECSCNM). + * Ps = 6 -> Normal Cursor Mode (DECOM). + * Ps = 7 -> No Wraparound Mode (DECAWM). + * Ps = 8 -> No Auto-repeat Keys (DECARM). + * Ps = 9 -> Don't send Mouse X & Y on button press. + * Ps = 1 0 -> Hide toolbar (rxvt). + * Ps = 1 2 -> Stop Blinking Cursor (att610). + * Ps = 1 8 -> Don't print form feed (DECPFF). + * Ps = 1 9 -> Limit print to scrolling region (DECPEX). + * Ps = 2 5 -> Hide Cursor (DECTCEM). + * Ps = 3 0 -> Don't show scrollbar (rxvt). + * Ps = 3 5 -> Disable font-shifting functions (rxvt). + * Ps = 4 0 -> Disallow 80 -> 132 Mode. + * Ps = 4 1 -> No more(1) fix (see curses resource). + * Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- + * NRCM). + * Ps = 4 4 -> Turn Off Margin Bell. + * Ps = 4 5 -> No Reverse-wraparound Mode. + * Ps = 4 6 -> Stop Logging. (This is normally disabled by a + * compile-time option). + * Ps = 4 7 -> Use Normal Screen Buffer. + * Ps = 6 6 -> Numeric keypad (DECNKM). + * Ps = 6 7 -> Backarrow key sends delete (DECBKM). + * Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and + * release. See the section Mouse Tracking. + * Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. + * Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. + * Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. + * Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. + * Ps = 1 0 0 5 -> Disable Extended Mouse Mode. + * Ps = 1 0 1 0 -> Don't scroll to bottom on tty output + * (rxvt). + * Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). + * Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables + * the eightBitInput resource). + * Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- + * Lock keys. (This disables the numLock resource). + * Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. + * (This disables the metaSendsEscape resource). + * Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad + * Delete key. + * Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. + * (This disables the altSendsEscape resource). + * Ps = 1 0 4 0 -> Do not keep selection when not highlighted. + * (This disables the keepSelection resource). + * Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables + * the selectToClipboard resource). + * Ps = 1 0 4 2 -> Disable Urgency window manager hint when + * Control-G is received. (This disables the bellIsUrgent + * resource). + * Ps = 1 0 4 3 -> Disable raising of the window when Control- + * G is received. (This disables the popOnBell resource). + * Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen + * first if in the Alternate Screen. (This may be disabled by + * the titeInhibit resource). + * Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be + * disabled by the titeInhibit resource). + * Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor + * as in DECRC. (This may be disabled by the titeInhibit + * resource). This combines the effects of the 1 0 4 7 and 1 0 + * 4 8 modes. Use this with terminfo-based applications rather + * than the 4 7 mode. + * Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. + * Ps = 1 0 5 1 -> Reset Sun function-key mode. + * Ps = 1 0 5 2 -> Reset HP function-key mode. + * Ps = 1 0 5 3 -> Reset SCO function-key mode. + * Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). + * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. + * Ps = 2 0 0 4 -> Reset bracketed paste mode. + */Terminal.prototype.resetMode=function(params){if((typeof params==='undefined'?'undefined':_typeof(params))==='object'){var l=params.length,i=0;for(;i Ps; Ps T + * Reset one or more features of the title modes to the default + * value. Normally, "reset" disables the feature. It is possi- + * ble to disable the ability to reset features by compiling a + * different default for the title modes into xterm. + * Ps = 0 -> Do not set window/icon labels using hexadecimal. + * Ps = 1 -> Do not query window/icon labels using hexadeci- + * mal. + * Ps = 2 -> Do not set window/icon labels using UTF-8. + * Ps = 3 -> Do not query window/icon labels using UTF-8. + * (See discussion of "Title Modes"). + */Terminal.prototype.resetTitleModes=function(params){;};/** + * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + */Terminal.prototype.cursorBackwardTab=function(params){var param=params[0]||1;while(param--){this.x=this.prevStop();}};/** + * CSI Ps b Repeat the preceding graphic character Ps times (REP). + */Terminal.prototype.repeatPrecedingCharacter=function(params){var param=params[0]||1,line=this.lines[this.ybase+this.y],ch=line[this.x-1]||[this.defAttr,' ',1];while(param--){line[this.x++]=ch;}};/** + * CSI Ps g Tab Clear (TBC). + * Ps = 0 -> Clear Current Column (default). + * Ps = 3 -> Clear All. + * Potentially: + * Ps = 2 -> Clear Stops on Line. + * http://vt100.net/annarbor/aaa-ug/section6.html + */Terminal.prototype.tabClear=function(params){var param=params[0];if(param<=0){delete this.tabs[this.x];}else if(param===3){this.tabs={};}};/** + * CSI Pm i Media Copy (MC). + * Ps = 0 -> Print screen (default). + * Ps = 4 -> Turn off printer controller mode. + * Ps = 5 -> Turn on printer controller mode. + * CSI ? Pm i + * Media Copy (MC, DEC-specific). + * Ps = 1 -> Print line containing cursor. + * Ps = 4 -> Turn off autoprint mode. + * Ps = 5 -> Turn on autoprint mode. + * Ps = 1 0 -> Print composed display, ignores DECPEX. + * Ps = 1 1 -> Print all pages. + */Terminal.prototype.mediaCopy=function(params){;};/** + * CSI > Ps; Ps m + * Set or reset resource-values used by xterm to decide whether + * to construct escape sequences holding information about the + * modifiers pressed with a given key. The first parameter iden- + * tifies the resource to set/reset. The second parameter is the + * value to assign to the resource. If the second parameter is + * omitted, the resource is reset to its initial value. + * Ps = 1 -> modifyCursorKeys. + * Ps = 2 -> modifyFunctionKeys. + * Ps = 4 -> modifyOtherKeys. + * If no parameters are given, all resources are reset to their + * initial values. + */Terminal.prototype.setResources=function(params){;};/** + * CSI > Ps n + * Disable modifiers which may be enabled via the CSI > Ps; Ps m + * sequence. This corresponds to a resource value of "-1", which + * cannot be set with the other sequence. The parameter identi- + * fies the resource to be disabled: + * Ps = 1 -> modifyCursorKeys. + * Ps = 2 -> modifyFunctionKeys. + * Ps = 4 -> modifyOtherKeys. + * If the parameter is omitted, modifyFunctionKeys is disabled. + * When modifyFunctionKeys is disabled, xterm uses the modifier + * keys to make an extended sequence of functions rather than + * adding a parameter to each function key to denote the modi- + * fiers. + */Terminal.prototype.disableModifiers=function(params){;};/** + * CSI > Ps p + * Set resource value pointerMode. This is used by xterm to + * decide whether to hide the pointer cursor as the user types. + * Valid values for the parameter: + * Ps = 0 -> never hide the pointer. + * Ps = 1 -> hide if the mouse tracking mode is not enabled. + * Ps = 2 -> always hide the pointer. If no parameter is + * given, xterm uses the default, which is 1 . + */Terminal.prototype.setPointerMode=function(params){;};/** + * CSI ! p Soft terminal reset (DECSTR). + * http://vt100.net/docs/vt220-rm/table4-10.html + */Terminal.prototype.softReset=function(params){this.cursorHidden=false;this.insertMode=false;this.originMode=false;this.wraparoundMode=false;// autowrap +this.applicationKeypad=false;// ? +this.viewport.syncScrollArea();this.applicationCursor=false;this.scrollTop=0;this.scrollBottom=this.rows-1;this.curAttr=this.defAttr;this.x=this.y=0;// ? +this.charset=null;this.glevel=0;// ?? +this.charsets=[null];// ?? +};/** + * CSI Ps$ p + * Request ANSI mode (DECRQM). For VT300 and up, reply is + * CSI Ps; Pm$ y + * where Ps is the mode number as in RM, and Pm is the mode + * value: + * 0 - not recognized + * 1 - set + * 2 - reset + * 3 - permanently set + * 4 - permanently reset + */Terminal.prototype.requestAnsiMode=function(params){;};/** + * CSI ? Ps$ p + * Request DEC private mode (DECRQM). For VT300 and up, reply is + * CSI ? Ps; Pm$ p + * where Ps is the mode number as in DECSET, Pm is the mode value + * as in the ANSI DECRQM. + */Terminal.prototype.requestPrivateMode=function(params){;};/** + * CSI Ps ; Ps " p + * Set conformance level (DECSCL). Valid values for the first + * parameter: + * Ps = 6 1 -> VT100. + * Ps = 6 2 -> VT200. + * Ps = 6 3 -> VT300. + * Valid values for the second parameter: + * Ps = 0 -> 8-bit controls. + * Ps = 1 -> 7-bit controls (always set for VT100). + * Ps = 2 -> 8-bit controls. + */Terminal.prototype.setConformanceLevel=function(params){;};/** + * CSI Ps q Load LEDs (DECLL). + * Ps = 0 -> Clear all LEDS (default). + * Ps = 1 -> Light Num Lock. + * Ps = 2 -> Light Caps Lock. + * Ps = 3 -> Light Scroll Lock. + * Ps = 2 1 -> Extinguish Num Lock. + * Ps = 2 2 -> Extinguish Caps Lock. + * Ps = 2 3 -> Extinguish Scroll Lock. + */Terminal.prototype.loadLEDs=function(params){;};/** + * CSI Ps SP q + * Set cursor style (DECSCUSR, VT520). + * Ps = 0 -> blinking block. + * Ps = 1 -> blinking block (default). + * Ps = 2 -> steady block. + * Ps = 3 -> blinking underline. + * Ps = 4 -> steady underline. + */Terminal.prototype.setCursorStyle=function(params){;};/** + * CSI Ps " q + * Select character protection attribute (DECSCA). Valid values + * for the parameter: + * Ps = 0 -> DECSED and DECSEL can erase (default). + * Ps = 1 -> DECSED and DECSEL cannot erase. + * Ps = 2 -> DECSED and DECSEL can erase. + */Terminal.prototype.setCharProtectionAttr=function(params){;};/** + * CSI ? Pm r + * Restore DEC Private Mode Values. The value of Ps previously + * saved is restored. Ps values are the same as for DECSET. + */Terminal.prototype.restorePrivateValues=function(params){;};/** + * CSI Pt; Pl; Pb; Pr; Ps$ r + * Change Attributes in Rectangular Area (DECCARA), VT400 and up. + * Pt; Pl; Pb; Pr denotes the rectangle. + * Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. + * NOTE: xterm doesn't enable this code by default. + */Terminal.prototype.setAttrInRectangle=function(params){var t=params[0],l=params[1],b=params[2],r=params[3],attr=params[4];var line,i;for(;t Locator disabled (default). + * Ps = 1 -> Locator enabled. + * Ps = 2 -> Locator enabled for one report, then disabled. + * The second parameter specifies the coordinate unit for locator + * reports. + * Valid values for the second parameter: + * Pu = 0 <- or omitted -> default to character cells. + * Pu = 1 <- device physical pixels. + * Pu = 2 <- character cells. + */Terminal.prototype.enableLocatorReporting=function(params){var val=params[0]>0;//this.mouseEvents = val; +//this.decLocator = val; +};/** + * CSI Pt; Pl; Pb; Pr$ z + * Erase Rectangular Area (DECERA), VT400 and up. + * Pt; Pl; Pb; Pr denotes the rectangle. + * NOTE: xterm doesn't enable this code by default. + */Terminal.prototype.eraseRectangle=function(params){var t=params[0],l=params[1],b=params[2],r=params[3];var line,i,ch;ch=[this.eraseAttr(),' ',1];// xterm? +for(;t47);}function matchColor(r1,g1,b1){var hash=r1<<16|g1<<8|b1;if(matchColor._cache[hash]!=null){return matchColor._cache[hash];}var ldiff=Infinity,li=-1,i=0,c,r2,g2,b2,diff;for(;iCOMBINING[max][1])return false;while(max>=min){mid=Math.floor((min+max)/2);if(ucs>COMBINING[mid][1])min=mid+1;else if(ucs=0x7f&&ucs<0xa0)return opts.control;// binary search in table of non-spacing characters +if(bisearch(ucs))return 0;// if we arrive here, ucs is not a combining or C0/C1 control character +return 1+(ucs>=0x1100&&(ucs<=0x115f||// Hangul Jamo init. consonants +ucs==0x2329||ucs==0x232a||ucs>=0x2e80&&ucs<=0xa4cf&&ucs!=0x303f||// CJK..Yi +ucs>=0xac00&&ucs<=0xd7a3||// Hangul Syllables +ucs>=0xf900&&ucs<=0xfaff||// CJK Compat Ideographs +ucs>=0xfe10&&ucs<=0xfe19||// Vertical forms +ucs>=0xfe30&&ucs<=0xfe6f||// CJK Compat Forms +ucs>=0xff00&&ucs<=0xff60||// Fullwidth Forms +ucs>=0xffe0&&ucs<=0xffe6||ucs>=0x20000&&ucs<=0x2fffd||ucs>=0x30000&&ucs<=0x3fffd));}return wcwidth;}({nul:0,control:0});// configurable options +/** + * Expose + */Terminal.EventEmitter=_EventEmitter.EventEmitter;Terminal.CompositionHelper=_CompositionHelper.CompositionHelper;Terminal.Viewport=_Viewport.Viewport;Terminal.inherits=inherits;/** + * Adds an event listener to the terminal. + * + * @param {string} event The name of the event. TODO: Document all event types + * @param {function} callback The function to call when the event is triggered. + */Terminal.on=on;Terminal.off=off;Terminal.cancel=cancel;module.exports=Terminal; + +},{"./CompositionHelper.js":1,"./EventEmitter.js":2,"./Viewport.js":3,"./handlers/Clipboard.js":4,"./utils/Browser":5}]},{},[7])(7) +}); +//# sourceMappingURL=xterm.js.map diff --git a/vendor/assets/stylesheets/xterm/xterm.css b/vendor/assets/stylesheets/xterm/xterm.css new file mode 100644 index 00000000000..b30d7b493f1 --- /dev/null +++ b/vendor/assets/stylesheets/xterm/xterm.css @@ -0,0 +1,2206 @@ +/** + * xterm.js: xterm, in the browser + * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License) + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */ + +/* + * Default style for xterm.js + */ + +.terminal { + background-color: #000; + color: #fff; + font-family: courier-new, courier, monospace; + font-feature-settings: "liga" 0; + position: relative; +} + +.terminal.focus, +.terminal:focus { + outline: none; +} + +.terminal .xterm-helpers { + position: absolute; + top: 0; +} + +.terminal .xterm-helper-textarea { + /* + * HACK: to fix IE's blinking cursor + * Move textarea out of the screen to the far left, so that the cursor is not visible. + */ + position: absolute; + opacity: 0; + left: -9999em; + top: -9999em; + width: 0; + height: 0; + z-index: -10; + /** Prevent wrapping so the IME appears against the textarea at the correct position */ + white-space: nowrap; + overflow: hidden; + resize: none; +} + +.terminal .terminal-cursor { + background-color: #fff; + color: #000; +} + +.terminal:not(.focus) .terminal-cursor { + outline: 1px solid #fff; + outline-offset: -1px; + background-color: transparent; +} + +.terminal.focus .terminal-cursor.blinking { + animation: blink-cursor 1.2s infinite step-end; +} + +@keyframes blink-cursor { + 0% { + background-color: #fff; + color: #000; + } + 50% { + background-color: transparent; + color: #FFF; + } +} + +.terminal .composition-view { + background: #000; + color: #FFF; + display: none; + position: absolute; + white-space: nowrap; + z-index: 1; +} + +.terminal .composition-view.active { + display: block; +} + +.terminal .xterm-viewport { + /* On OS X this is required in order for the scroll bar to appear fully opaque */ + background-color: #000; + overflow-y: scroll; +} + +.terminal .xterm-rows { + position: absolute; + left: 0; + top: 0; +} + +.terminal .xterm-rows > div { + /* Lines containing spans and text nodes ocassionally wrap despite being the same width (#327) */ + white-space: nowrap; +} + +.terminal .xterm-scroll-area { + visibility: hidden; +} + +.terminal .xterm-char-measure-element { + display: inline-block; + visibility: hidden; + position: absolute; + left: -9999em; +} + +/* + * Determine default colors for xterm.js + */ +.terminal .xterm-bold { + font-weight: bold; +} + +.terminal .xterm-underline { + text-decoration: underline; +} + +.terminal .xterm-blink { + text-decoration: blink; +} + +.terminal .xterm-hidden { + visibility: hidden; +} + +.terminal .xterm-color-0 { + color: #2e3436; +} + +.terminal .xterm-bg-color-0 { + background-color: #2e3436; +} + +.terminal .xterm-color-1 { + color: #cc0000; +} + +.terminal .xterm-bg-color-1 { + background-color: #cc0000; +} + +.terminal .xterm-color-2 { + color: #4e9a06; +} + +.terminal .xterm-bg-color-2 { + background-color: #4e9a06; +} + +.terminal .xterm-color-3 { + color: #c4a000; +} + +.terminal .xterm-bg-color-3 { + background-color: #c4a000; +} + +.terminal .xterm-color-4 { + color: #3465a4; +} + +.terminal .xterm-bg-color-4 { + background-color: #3465a4; +} + +.terminal .xterm-color-5 { + color: #75507b; +} + +.terminal .xterm-bg-color-5 { + background-color: #75507b; +} + +.terminal .xterm-color-6 { + color: #06989a; +} + +.terminal .xterm-bg-color-6 { + background-color: #06989a; +} + +.terminal .xterm-color-7 { + color: #d3d7cf; +} + +.terminal .xterm-bg-color-7 { + background-color: #d3d7cf; +} + +.terminal .xterm-color-8 { + color: #555753; +} + +.terminal .xterm-bg-color-8 { + background-color: #555753; +} + +.terminal .xterm-color-9 { + color: #ef2929; +} + +.terminal .xterm-bg-color-9 { + background-color: #ef2929; +} + +.terminal .xterm-color-10 { + color: #8ae234; +} + +.terminal .xterm-bg-color-10 { + background-color: #8ae234; +} + +.terminal .xterm-color-11 { + color: #fce94f; +} + +.terminal .xterm-bg-color-11 { + background-color: #fce94f; +} + +.terminal .xterm-color-12 { + color: #729fcf; +} + +.terminal .xterm-bg-color-12 { + background-color: #729fcf; +} + +.terminal .xterm-color-13 { + color: #ad7fa8; +} + +.terminal .xterm-bg-color-13 { + background-color: #ad7fa8; +} + +.terminal .xterm-color-14 { + color: #34e2e2; +} + +.terminal .xterm-bg-color-14 { + background-color: #34e2e2; +} + +.terminal .xterm-color-15 { + color: #eeeeec; +} + +.terminal .xterm-bg-color-15 { + background-color: #eeeeec; +} + +.terminal .xterm-color-16 { + color: #000000; +} + +.terminal .xterm-bg-color-16 { + background-color: #000000; +} + +.terminal .xterm-color-17 { + color: #00005f; +} + +.terminal .xterm-bg-color-17 { + background-color: #00005f; +} + +.terminal .xterm-color-18 { + color: #000087; +} + +.terminal .xterm-bg-color-18 { + background-color: #000087; +} + +.terminal .xterm-color-19 { + color: #0000af; +} + +.terminal .xterm-bg-color-19 { + background-color: #0000af; +} + +.terminal .xterm-color-20 { + color: #0000d7; +} + +.terminal .xterm-bg-color-20 { + background-color: #0000d7; +} + +.terminal .xterm-color-21 { + color: #0000ff; +} + +.terminal .xterm-bg-color-21 { + background-color: #0000ff; +} + +.terminal .xterm-color-22 { + color: #005f00; +} + +.terminal .xterm-bg-color-22 { + background-color: #005f00; +} + +.terminal .xterm-color-23 { + color: #005f5f; +} + +.terminal .xterm-bg-color-23 { + background-color: #005f5f; +} + +.terminal .xterm-color-24 { + color: #005f87; +} + +.terminal .xterm-bg-color-24 { + background-color: #005f87; +} + +.terminal .xterm-color-25 { + color: #005faf; +} + +.terminal .xterm-bg-color-25 { + background-color: #005faf; +} + +.terminal .xterm-color-26 { + color: #005fd7; +} + +.terminal .xterm-bg-color-26 { + background-color: #005fd7; +} + +.terminal .xterm-color-27 { + color: #005fff; +} + +.terminal .xterm-bg-color-27 { + background-color: #005fff; +} + +.terminal .xterm-color-28 { + color: #008700; +} + +.terminal .xterm-bg-color-28 { + background-color: #008700; +} + +.terminal .xterm-color-29 { + color: #00875f; +} + +.terminal .xterm-bg-color-29 { + background-color: #00875f; +} + +.terminal .xterm-color-30 { + color: #008787; +} + +.terminal .xterm-bg-color-30 { + background-color: #008787; +} + +.terminal .xterm-color-31 { + color: #0087af; +} + +.terminal .xterm-bg-color-31 { + background-color: #0087af; +} + +.terminal .xterm-color-32 { + color: #0087d7; +} + +.terminal .xterm-bg-color-32 { + background-color: #0087d7; +} + +.terminal .xterm-color-33 { + color: #0087ff; +} + +.terminal .xterm-bg-color-33 { + background-color: #0087ff; +} + +.terminal .xterm-color-34 { + color: #00af00; +} + +.terminal .xterm-bg-color-34 { + background-color: #00af00; +} + +.terminal .xterm-color-35 { + color: #00af5f; +} + +.terminal .xterm-bg-color-35 { + background-color: #00af5f; +} + +.terminal .xterm-color-36 { + color: #00af87; +} + +.terminal .xterm-bg-color-36 { + background-color: #00af87; +} + +.terminal .xterm-color-37 { + color: #00afaf; +} + +.terminal .xterm-bg-color-37 { + background-color: #00afaf; +} + +.terminal .xterm-color-38 { + color: #00afd7; +} + +.terminal .xterm-bg-color-38 { + background-color: #00afd7; +} + +.terminal .xterm-color-39 { + color: #00afff; +} + +.terminal .xterm-bg-color-39 { + background-color: #00afff; +} + +.terminal .xterm-color-40 { + color: #00d700; +} + +.terminal .xterm-bg-color-40 { + background-color: #00d700; +} + +.terminal .xterm-color-41 { + color: #00d75f; +} + +.terminal .xterm-bg-color-41 { + background-color: #00d75f; +} + +.terminal .xterm-color-42 { + color: #00d787; +} + +.terminal .xterm-bg-color-42 { + background-color: #00d787; +} + +.terminal .xterm-color-43 { + color: #00d7af; +} + +.terminal .xterm-bg-color-43 { + background-color: #00d7af; +} + +.terminal .xterm-color-44 { + color: #00d7d7; +} + +.terminal .xterm-bg-color-44 { + background-color: #00d7d7; +} + +.terminal .xterm-color-45 { + color: #00d7ff; +} + +.terminal .xterm-bg-color-45 { + background-color: #00d7ff; +} + +.terminal .xterm-color-46 { + color: #00ff00; +} + +.terminal .xterm-bg-color-46 { + background-color: #00ff00; +} + +.terminal .xterm-color-47 { + color: #00ff5f; +} + +.terminal .xterm-bg-color-47 { + background-color: #00ff5f; +} + +.terminal .xterm-color-48 { + color: #00ff87; +} + +.terminal .xterm-bg-color-48 { + background-color: #00ff87; +} + +.terminal .xterm-color-49 { + color: #00ffaf; +} + +.terminal .xterm-bg-color-49 { + background-color: #00ffaf; +} + +.terminal .xterm-color-50 { + color: #00ffd7; +} + +.terminal .xterm-bg-color-50 { + background-color: #00ffd7; +} + +.terminal .xterm-color-51 { + color: #00ffff; +} + +.terminal .xterm-bg-color-51 { + background-color: #00ffff; +} + +.terminal .xterm-color-52 { + color: #5f0000; +} + +.terminal .xterm-bg-color-52 { + background-color: #5f0000; +} + +.terminal .xterm-color-53 { + color: #5f005f; +} + +.terminal .xterm-bg-color-53 { + background-color: #5f005f; +} + +.terminal .xterm-color-54 { + color: #5f0087; +} + +.terminal .xterm-bg-color-54 { + background-color: #5f0087; +} + +.terminal .xterm-color-55 { + color: #5f00af; +} + +.terminal .xterm-bg-color-55 { + background-color: #5f00af; +} + +.terminal .xterm-color-56 { + color: #5f00d7; +} + +.terminal .xterm-bg-color-56 { + background-color: #5f00d7; +} + +.terminal .xterm-color-57 { + color: #5f00ff; +} + +.terminal .xterm-bg-color-57 { + background-color: #5f00ff; +} + +.terminal .xterm-color-58 { + color: #5f5f00; +} + +.terminal .xterm-bg-color-58 { + background-color: #5f5f00; +} + +.terminal .xterm-color-59 { + color: #5f5f5f; +} + +.terminal .xterm-bg-color-59 { + background-color: #5f5f5f; +} + +.terminal .xterm-color-60 { + color: #5f5f87; +} + +.terminal .xterm-bg-color-60 { + background-color: #5f5f87; +} + +.terminal .xterm-color-61 { + color: #5f5faf; +} + +.terminal .xterm-bg-color-61 { + background-color: #5f5faf; +} + +.terminal .xterm-color-62 { + color: #5f5fd7; +} + +.terminal .xterm-bg-color-62 { + background-color: #5f5fd7; +} + +.terminal .xterm-color-63 { + color: #5f5fff; +} + +.terminal .xterm-bg-color-63 { + background-color: #5f5fff; +} + +.terminal .xterm-color-64 { + color: #5f8700; +} + +.terminal .xterm-bg-color-64 { + background-color: #5f8700; +} + +.terminal .xterm-color-65 { + color: #5f875f; +} + +.terminal .xterm-bg-color-65 { + background-color: #5f875f; +} + +.terminal .xterm-color-66 { + color: #5f8787; +} + +.terminal .xterm-bg-color-66 { + background-color: #5f8787; +} + +.terminal .xterm-color-67 { + color: #5f87af; +} + +.terminal .xterm-bg-color-67 { + background-color: #5f87af; +} + +.terminal .xterm-color-68 { + color: #5f87d7; +} + +.terminal .xterm-bg-color-68 { + background-color: #5f87d7; +} + +.terminal .xterm-color-69 { + color: #5f87ff; +} + +.terminal .xterm-bg-color-69 { + background-color: #5f87ff; +} + +.terminal .xterm-color-70 { + color: #5faf00; +} + +.terminal .xterm-bg-color-70 { + background-color: #5faf00; +} + +.terminal .xterm-color-71 { + color: #5faf5f; +} + +.terminal .xterm-bg-color-71 { + background-color: #5faf5f; +} + +.terminal .xterm-color-72 { + color: #5faf87; +} + +.terminal .xterm-bg-color-72 { + background-color: #5faf87; +} + +.terminal .xterm-color-73 { + color: #5fafaf; +} + +.terminal .xterm-bg-color-73 { + background-color: #5fafaf; +} + +.terminal .xterm-color-74 { + color: #5fafd7; +} + +.terminal .xterm-bg-color-74 { + background-color: #5fafd7; +} + +.terminal .xterm-color-75 { + color: #5fafff; +} + +.terminal .xterm-bg-color-75 { + background-color: #5fafff; +} + +.terminal .xterm-color-76 { + color: #5fd700; +} + +.terminal .xterm-bg-color-76 { + background-color: #5fd700; +} + +.terminal .xterm-color-77 { + color: #5fd75f; +} + +.terminal .xterm-bg-color-77 { + background-color: #5fd75f; +} + +.terminal .xterm-color-78 { + color: #5fd787; +} + +.terminal .xterm-bg-color-78 { + background-color: #5fd787; +} + +.terminal .xterm-color-79 { + color: #5fd7af; +} + +.terminal .xterm-bg-color-79 { + background-color: #5fd7af; +} + +.terminal .xterm-color-80 { + color: #5fd7d7; +} + +.terminal .xterm-bg-color-80 { + background-color: #5fd7d7; +} + +.terminal .xterm-color-81 { + color: #5fd7ff; +} + +.terminal .xterm-bg-color-81 { + background-color: #5fd7ff; +} + +.terminal .xterm-color-82 { + color: #5fff00; +} + +.terminal .xterm-bg-color-82 { + background-color: #5fff00; +} + +.terminal .xterm-color-83 { + color: #5fff5f; +} + +.terminal .xterm-bg-color-83 { + background-color: #5fff5f; +} + +.terminal .xterm-color-84 { + color: #5fff87; +} + +.terminal .xterm-bg-color-84 { + background-color: #5fff87; +} + +.terminal .xterm-color-85 { + color: #5fffaf; +} + +.terminal .xterm-bg-color-85 { + background-color: #5fffaf; +} + +.terminal .xterm-color-86 { + color: #5fffd7; +} + +.terminal .xterm-bg-color-86 { + background-color: #5fffd7; +} + +.terminal .xterm-color-87 { + color: #5fffff; +} + +.terminal .xterm-bg-color-87 { + background-color: #5fffff; +} + +.terminal .xterm-color-88 { + color: #870000; +} + +.terminal .xterm-bg-color-88 { + background-color: #870000; +} + +.terminal .xterm-color-89 { + color: #87005f; +} + +.terminal .xterm-bg-color-89 { + background-color: #87005f; +} + +.terminal .xterm-color-90 { + color: #870087; +} + +.terminal .xterm-bg-color-90 { + background-color: #870087; +} + +.terminal .xterm-color-91 { + color: #8700af; +} + +.terminal .xterm-bg-color-91 { + background-color: #8700af; +} + +.terminal .xterm-color-92 { + color: #8700d7; +} + +.terminal .xterm-bg-color-92 { + background-color: #8700d7; +} + +.terminal .xterm-color-93 { + color: #8700ff; +} + +.terminal .xterm-bg-color-93 { + background-color: #8700ff; +} + +.terminal .xterm-color-94 { + color: #875f00; +} + +.terminal .xterm-bg-color-94 { + background-color: #875f00; +} + +.terminal .xterm-color-95 { + color: #875f5f; +} + +.terminal .xterm-bg-color-95 { + background-color: #875f5f; +} + +.terminal .xterm-color-96 { + color: #875f87; +} + +.terminal .xterm-bg-color-96 { + background-color: #875f87; +} + +.terminal .xterm-color-97 { + color: #875faf; +} + +.terminal .xterm-bg-color-97 { + background-color: #875faf; +} + +.terminal .xterm-color-98 { + color: #875fd7; +} + +.terminal .xterm-bg-color-98 { + background-color: #875fd7; +} + +.terminal .xterm-color-99 { + color: #875fff; +} + +.terminal .xterm-bg-color-99 { + background-color: #875fff; +} + +.terminal .xterm-color-100 { + color: #878700; +} + +.terminal .xterm-bg-color-100 { + background-color: #878700; +} + +.terminal .xterm-color-101 { + color: #87875f; +} + +.terminal .xterm-bg-color-101 { + background-color: #87875f; +} + +.terminal .xterm-color-102 { + color: #878787; +} + +.terminal .xterm-bg-color-102 { + background-color: #878787; +} + +.terminal .xterm-color-103 { + color: #8787af; +} + +.terminal .xterm-bg-color-103 { + background-color: #8787af; +} + +.terminal .xterm-color-104 { + color: #8787d7; +} + +.terminal .xterm-bg-color-104 { + background-color: #8787d7; +} + +.terminal .xterm-color-105 { + color: #8787ff; +} + +.terminal .xterm-bg-color-105 { + background-color: #8787ff; +} + +.terminal .xterm-color-106 { + color: #87af00; +} + +.terminal .xterm-bg-color-106 { + background-color: #87af00; +} + +.terminal .xterm-color-107 { + color: #87af5f; +} + +.terminal .xterm-bg-color-107 { + background-color: #87af5f; +} + +.terminal .xterm-color-108 { + color: #87af87; +} + +.terminal .xterm-bg-color-108 { + background-color: #87af87; +} + +.terminal .xterm-color-109 { + color: #87afaf; +} + +.terminal .xterm-bg-color-109 { + background-color: #87afaf; +} + +.terminal .xterm-color-110 { + color: #87afd7; +} + +.terminal .xterm-bg-color-110 { + background-color: #87afd7; +} + +.terminal .xterm-color-111 { + color: #87afff; +} + +.terminal .xterm-bg-color-111 { + background-color: #87afff; +} + +.terminal .xterm-color-112 { + color: #87d700; +} + +.terminal .xterm-bg-color-112 { + background-color: #87d700; +} + +.terminal .xterm-color-113 { + color: #87d75f; +} + +.terminal .xterm-bg-color-113 { + background-color: #87d75f; +} + +.terminal .xterm-color-114 { + color: #87d787; +} + +.terminal .xterm-bg-color-114 { + background-color: #87d787; +} + +.terminal .xterm-color-115 { + color: #87d7af; +} + +.terminal .xterm-bg-color-115 { + background-color: #87d7af; +} + +.terminal .xterm-color-116 { + color: #87d7d7; +} + +.terminal .xterm-bg-color-116 { + background-color: #87d7d7; +} + +.terminal .xterm-color-117 { + color: #87d7ff; +} + +.terminal .xterm-bg-color-117 { + background-color: #87d7ff; +} + +.terminal .xterm-color-118 { + color: #87ff00; +} + +.terminal .xterm-bg-color-118 { + background-color: #87ff00; +} + +.terminal .xterm-color-119 { + color: #87ff5f; +} + +.terminal .xterm-bg-color-119 { + background-color: #87ff5f; +} + +.terminal .xterm-color-120 { + color: #87ff87; +} + +.terminal .xterm-bg-color-120 { + background-color: #87ff87; +} + +.terminal .xterm-color-121 { + color: #87ffaf; +} + +.terminal .xterm-bg-color-121 { + background-color: #87ffaf; +} + +.terminal .xterm-color-122 { + color: #87ffd7; +} + +.terminal .xterm-bg-color-122 { + background-color: #87ffd7; +} + +.terminal .xterm-color-123 { + color: #87ffff; +} + +.terminal .xterm-bg-color-123 { + background-color: #87ffff; +} + +.terminal .xterm-color-124 { + color: #af0000; +} + +.terminal .xterm-bg-color-124 { + background-color: #af0000; +} + +.terminal .xterm-color-125 { + color: #af005f; +} + +.terminal .xterm-bg-color-125 { + background-color: #af005f; +} + +.terminal .xterm-color-126 { + color: #af0087; +} + +.terminal .xterm-bg-color-126 { + background-color: #af0087; +} + +.terminal .xterm-color-127 { + color: #af00af; +} + +.terminal .xterm-bg-color-127 { + background-color: #af00af; +} + +.terminal .xterm-color-128 { + color: #af00d7; +} + +.terminal .xterm-bg-color-128 { + background-color: #af00d7; +} + +.terminal .xterm-color-129 { + color: #af00ff; +} + +.terminal .xterm-bg-color-129 { + background-color: #af00ff; +} + +.terminal .xterm-color-130 { + color: #af5f00; +} + +.terminal .xterm-bg-color-130 { + background-color: #af5f00; +} + +.terminal .xterm-color-131 { + color: #af5f5f; +} + +.terminal .xterm-bg-color-131 { + background-color: #af5f5f; +} + +.terminal .xterm-color-132 { + color: #af5f87; +} + +.terminal .xterm-bg-color-132 { + background-color: #af5f87; +} + +.terminal .xterm-color-133 { + color: #af5faf; +} + +.terminal .xterm-bg-color-133 { + background-color: #af5faf; +} + +.terminal .xterm-color-134 { + color: #af5fd7; +} + +.terminal .xterm-bg-color-134 { + background-color: #af5fd7; +} + +.terminal .xterm-color-135 { + color: #af5fff; +} + +.terminal .xterm-bg-color-135 { + background-color: #af5fff; +} + +.terminal .xterm-color-136 { + color: #af8700; +} + +.terminal .xterm-bg-color-136 { + background-color: #af8700; +} + +.terminal .xterm-color-137 { + color: #af875f; +} + +.terminal .xterm-bg-color-137 { + background-color: #af875f; +} + +.terminal .xterm-color-138 { + color: #af8787; +} + +.terminal .xterm-bg-color-138 { + background-color: #af8787; +} + +.terminal .xterm-color-139 { + color: #af87af; +} + +.terminal .xterm-bg-color-139 { + background-color: #af87af; +} + +.terminal .xterm-color-140 { + color: #af87d7; +} + +.terminal .xterm-bg-color-140 { + background-color: #af87d7; +} + +.terminal .xterm-color-141 { + color: #af87ff; +} + +.terminal .xterm-bg-color-141 { + background-color: #af87ff; +} + +.terminal .xterm-color-142 { + color: #afaf00; +} + +.terminal .xterm-bg-color-142 { + background-color: #afaf00; +} + +.terminal .xterm-color-143 { + color: #afaf5f; +} + +.terminal .xterm-bg-color-143 { + background-color: #afaf5f; +} + +.terminal .xterm-color-144 { + color: #afaf87; +} + +.terminal .xterm-bg-color-144 { + background-color: #afaf87; +} + +.terminal .xterm-color-145 { + color: #afafaf; +} + +.terminal .xterm-bg-color-145 { + background-color: #afafaf; +} + +.terminal .xterm-color-146 { + color: #afafd7; +} + +.terminal .xterm-bg-color-146 { + background-color: #afafd7; +} + +.terminal .xterm-color-147 { + color: #afafff; +} + +.terminal .xterm-bg-color-147 { + background-color: #afafff; +} + +.terminal .xterm-color-148 { + color: #afd700; +} + +.terminal .xterm-bg-color-148 { + background-color: #afd700; +} + +.terminal .xterm-color-149 { + color: #afd75f; +} + +.terminal .xterm-bg-color-149 { + background-color: #afd75f; +} + +.terminal .xterm-color-150 { + color: #afd787; +} + +.terminal .xterm-bg-color-150 { + background-color: #afd787; +} + +.terminal .xterm-color-151 { + color: #afd7af; +} + +.terminal .xterm-bg-color-151 { + background-color: #afd7af; +} + +.terminal .xterm-color-152 { + color: #afd7d7; +} + +.terminal .xterm-bg-color-152 { + background-color: #afd7d7; +} + +.terminal .xterm-color-153 { + color: #afd7ff; +} + +.terminal .xterm-bg-color-153 { + background-color: #afd7ff; +} + +.terminal .xterm-color-154 { + color: #afff00; +} + +.terminal .xterm-bg-color-154 { + background-color: #afff00; +} + +.terminal .xterm-color-155 { + color: #afff5f; +} + +.terminal .xterm-bg-color-155 { + background-color: #afff5f; +} + +.terminal .xterm-color-156 { + color: #afff87; +} + +.terminal .xterm-bg-color-156 { + background-color: #afff87; +} + +.terminal .xterm-color-157 { + color: #afffaf; +} + +.terminal .xterm-bg-color-157 { + background-color: #afffaf; +} + +.terminal .xterm-color-158 { + color: #afffd7; +} + +.terminal .xterm-bg-color-158 { + background-color: #afffd7; +} + +.terminal .xterm-color-159 { + color: #afffff; +} + +.terminal .xterm-bg-color-159 { + background-color: #afffff; +} + +.terminal .xterm-color-160 { + color: #d70000; +} + +.terminal .xterm-bg-color-160 { + background-color: #d70000; +} + +.terminal .xterm-color-161 { + color: #d7005f; +} + +.terminal .xterm-bg-color-161 { + background-color: #d7005f; +} + +.terminal .xterm-color-162 { + color: #d70087; +} + +.terminal .xterm-bg-color-162 { + background-color: #d70087; +} + +.terminal .xterm-color-163 { + color: #d700af; +} + +.terminal .xterm-bg-color-163 { + background-color: #d700af; +} + +.terminal .xterm-color-164 { + color: #d700d7; +} + +.terminal .xterm-bg-color-164 { + background-color: #d700d7; +} + +.terminal .xterm-color-165 { + color: #d700ff; +} + +.terminal .xterm-bg-color-165 { + background-color: #d700ff; +} + +.terminal .xterm-color-166 { + color: #d75f00; +} + +.terminal .xterm-bg-color-166 { + background-color: #d75f00; +} + +.terminal .xterm-color-167 { + color: #d75f5f; +} + +.terminal .xterm-bg-color-167 { + background-color: #d75f5f; +} + +.terminal .xterm-color-168 { + color: #d75f87; +} + +.terminal .xterm-bg-color-168 { + background-color: #d75f87; +} + +.terminal .xterm-color-169 { + color: #d75faf; +} + +.terminal .xterm-bg-color-169 { + background-color: #d75faf; +} + +.terminal .xterm-color-170 { + color: #d75fd7; +} + +.terminal .xterm-bg-color-170 { + background-color: #d75fd7; +} + +.terminal .xterm-color-171 { + color: #d75fff; +} + +.terminal .xterm-bg-color-171 { + background-color: #d75fff; +} + +.terminal .xterm-color-172 { + color: #d78700; +} + +.terminal .xterm-bg-color-172 { + background-color: #d78700; +} + +.terminal .xterm-color-173 { + color: #d7875f; +} + +.terminal .xterm-bg-color-173 { + background-color: #d7875f; +} + +.terminal .xterm-color-174 { + color: #d78787; +} + +.terminal .xterm-bg-color-174 { + background-color: #d78787; +} + +.terminal .xterm-color-175 { + color: #d787af; +} + +.terminal .xterm-bg-color-175 { + background-color: #d787af; +} + +.terminal .xterm-color-176 { + color: #d787d7; +} + +.terminal .xterm-bg-color-176 { + background-color: #d787d7; +} + +.terminal .xterm-color-177 { + color: #d787ff; +} + +.terminal .xterm-bg-color-177 { + background-color: #d787ff; +} + +.terminal .xterm-color-178 { + color: #d7af00; +} + +.terminal .xterm-bg-color-178 { + background-color: #d7af00; +} + +.terminal .xterm-color-179 { + color: #d7af5f; +} + +.terminal .xterm-bg-color-179 { + background-color: #d7af5f; +} + +.terminal .xterm-color-180 { + color: #d7af87; +} + +.terminal .xterm-bg-color-180 { + background-color: #d7af87; +} + +.terminal .xterm-color-181 { + color: #d7afaf; +} + +.terminal .xterm-bg-color-181 { + background-color: #d7afaf; +} + +.terminal .xterm-color-182 { + color: #d7afd7; +} + +.terminal .xterm-bg-color-182 { + background-color: #d7afd7; +} + +.terminal .xterm-color-183 { + color: #d7afff; +} + +.terminal .xterm-bg-color-183 { + background-color: #d7afff; +} + +.terminal .xterm-color-184 { + color: #d7d700; +} + +.terminal .xterm-bg-color-184 { + background-color: #d7d700; +} + +.terminal .xterm-color-185 { + color: #d7d75f; +} + +.terminal .xterm-bg-color-185 { + background-color: #d7d75f; +} + +.terminal .xterm-color-186 { + color: #d7d787; +} + +.terminal .xterm-bg-color-186 { + background-color: #d7d787; +} + +.terminal .xterm-color-187 { + color: #d7d7af; +} + +.terminal .xterm-bg-color-187 { + background-color: #d7d7af; +} + +.terminal .xterm-color-188 { + color: #d7d7d7; +} + +.terminal .xterm-bg-color-188 { + background-color: #d7d7d7; +} + +.terminal .xterm-color-189 { + color: #d7d7ff; +} + +.terminal .xterm-bg-color-189 { + background-color: #d7d7ff; +} + +.terminal .xterm-color-190 { + color: #d7ff00; +} + +.terminal .xterm-bg-color-190 { + background-color: #d7ff00; +} + +.terminal .xterm-color-191 { + color: #d7ff5f; +} + +.terminal .xterm-bg-color-191 { + background-color: #d7ff5f; +} + +.terminal .xterm-color-192 { + color: #d7ff87; +} + +.terminal .xterm-bg-color-192 { + background-color: #d7ff87; +} + +.terminal .xterm-color-193 { + color: #d7ffaf; +} + +.terminal .xterm-bg-color-193 { + background-color: #d7ffaf; +} + +.terminal .xterm-color-194 { + color: #d7ffd7; +} + +.terminal .xterm-bg-color-194 { + background-color: #d7ffd7; +} + +.terminal .xterm-color-195 { + color: #d7ffff; +} + +.terminal .xterm-bg-color-195 { + background-color: #d7ffff; +} + +.terminal .xterm-color-196 { + color: #ff0000; +} + +.terminal .xterm-bg-color-196 { + background-color: #ff0000; +} + +.terminal .xterm-color-197 { + color: #ff005f; +} + +.terminal .xterm-bg-color-197 { + background-color: #ff005f; +} + +.terminal .xterm-color-198 { + color: #ff0087; +} + +.terminal .xterm-bg-color-198 { + background-color: #ff0087; +} + +.terminal .xterm-color-199 { + color: #ff00af; +} + +.terminal .xterm-bg-color-199 { + background-color: #ff00af; +} + +.terminal .xterm-color-200 { + color: #ff00d7; +} + +.terminal .xterm-bg-color-200 { + background-color: #ff00d7; +} + +.terminal .xterm-color-201 { + color: #ff00ff; +} + +.terminal .xterm-bg-color-201 { + background-color: #ff00ff; +} + +.terminal .xterm-color-202 { + color: #ff5f00; +} + +.terminal .xterm-bg-color-202 { + background-color: #ff5f00; +} + +.terminal .xterm-color-203 { + color: #ff5f5f; +} + +.terminal .xterm-bg-color-203 { + background-color: #ff5f5f; +} + +.terminal .xterm-color-204 { + color: #ff5f87; +} + +.terminal .xterm-bg-color-204 { + background-color: #ff5f87; +} + +.terminal .xterm-color-205 { + color: #ff5faf; +} + +.terminal .xterm-bg-color-205 { + background-color: #ff5faf; +} + +.terminal .xterm-color-206 { + color: #ff5fd7; +} + +.terminal .xterm-bg-color-206 { + background-color: #ff5fd7; +} + +.terminal .xterm-color-207 { + color: #ff5fff; +} + +.terminal .xterm-bg-color-207 { + background-color: #ff5fff; +} + +.terminal .xterm-color-208 { + color: #ff8700; +} + +.terminal .xterm-bg-color-208 { + background-color: #ff8700; +} + +.terminal .xterm-color-209 { + color: #ff875f; +} + +.terminal .xterm-bg-color-209 { + background-color: #ff875f; +} + +.terminal .xterm-color-210 { + color: #ff8787; +} + +.terminal .xterm-bg-color-210 { + background-color: #ff8787; +} + +.terminal .xterm-color-211 { + color: #ff87af; +} + +.terminal .xterm-bg-color-211 { + background-color: #ff87af; +} + +.terminal .xterm-color-212 { + color: #ff87d7; +} + +.terminal .xterm-bg-color-212 { + background-color: #ff87d7; +} + +.terminal .xterm-color-213 { + color: #ff87ff; +} + +.terminal .xterm-bg-color-213 { + background-color: #ff87ff; +} + +.terminal .xterm-color-214 { + color: #ffaf00; +} + +.terminal .xterm-bg-color-214 { + background-color: #ffaf00; +} + +.terminal .xterm-color-215 { + color: #ffaf5f; +} + +.terminal .xterm-bg-color-215 { + background-color: #ffaf5f; +} + +.terminal .xterm-color-216 { + color: #ffaf87; +} + +.terminal .xterm-bg-color-216 { + background-color: #ffaf87; +} + +.terminal .xterm-color-217 { + color: #ffafaf; +} + +.terminal .xterm-bg-color-217 { + background-color: #ffafaf; +} + +.terminal .xterm-color-218 { + color: #ffafd7; +} + +.terminal .xterm-bg-color-218 { + background-color: #ffafd7; +} + +.terminal .xterm-color-219 { + color: #ffafff; +} + +.terminal .xterm-bg-color-219 { + background-color: #ffafff; +} + +.terminal .xterm-color-220 { + color: #ffd700; +} + +.terminal .xterm-bg-color-220 { + background-color: #ffd700; +} + +.terminal .xterm-color-221 { + color: #ffd75f; +} + +.terminal .xterm-bg-color-221 { + background-color: #ffd75f; +} + +.terminal .xterm-color-222 { + color: #ffd787; +} + +.terminal .xterm-bg-color-222 { + background-color: #ffd787; +} + +.terminal .xterm-color-223 { + color: #ffd7af; +} + +.terminal .xterm-bg-color-223 { + background-color: #ffd7af; +} + +.terminal .xterm-color-224 { + color: #ffd7d7; +} + +.terminal .xterm-bg-color-224 { + background-color: #ffd7d7; +} + +.terminal .xterm-color-225 { + color: #ffd7ff; +} + +.terminal .xterm-bg-color-225 { + background-color: #ffd7ff; +} + +.terminal .xterm-color-226 { + color: #ffff00; +} + +.terminal .xterm-bg-color-226 { + background-color: #ffff00; +} + +.terminal .xterm-color-227 { + color: #ffff5f; +} + +.terminal .xterm-bg-color-227 { + background-color: #ffff5f; +} + +.terminal .xterm-color-228 { + color: #ffff87; +} + +.terminal .xterm-bg-color-228 { + background-color: #ffff87; +} + +.terminal .xterm-color-229 { + color: #ffffaf; +} + +.terminal .xterm-bg-color-229 { + background-color: #ffffaf; +} + +.terminal .xterm-color-230 { + color: #ffffd7; +} + +.terminal .xterm-bg-color-230 { + background-color: #ffffd7; +} + +.terminal .xterm-color-231 { + color: #ffffff; +} + +.terminal .xterm-bg-color-231 { + background-color: #ffffff; +} + +.terminal .xterm-color-232 { + color: #080808; +} + +.terminal .xterm-bg-color-232 { + background-color: #080808; +} + +.terminal .xterm-color-233 { + color: #121212; +} + +.terminal .xterm-bg-color-233 { + background-color: #121212; +} + +.terminal .xterm-color-234 { + color: #1c1c1c; +} + +.terminal .xterm-bg-color-234 { + background-color: #1c1c1c; +} + +.terminal .xterm-color-235 { + color: #262626; +} + +.terminal .xterm-bg-color-235 { + background-color: #262626; +} + +.terminal .xterm-color-236 { + color: #303030; +} + +.terminal .xterm-bg-color-236 { + background-color: #303030; +} + +.terminal .xterm-color-237 { + color: #3a3a3a; +} + +.terminal .xterm-bg-color-237 { + background-color: #3a3a3a; +} + +.terminal .xterm-color-238 { + color: #444444; +} + +.terminal .xterm-bg-color-238 { + background-color: #444444; +} + +.terminal .xterm-color-239 { + color: #4e4e4e; +} + +.terminal .xterm-bg-color-239 { + background-color: #4e4e4e; +} + +.terminal .xterm-color-240 { + color: #585858; +} + +.terminal .xterm-bg-color-240 { + background-color: #585858; +} + +.terminal .xterm-color-241 { + color: #626262; +} + +.terminal .xterm-bg-color-241 { + background-color: #626262; +} + +.terminal .xterm-color-242 { + color: #6c6c6c; +} + +.terminal .xterm-bg-color-242 { + background-color: #6c6c6c; +} + +.terminal .xterm-color-243 { + color: #767676; +} + +.terminal .xterm-bg-color-243 { + background-color: #767676; +} + +.terminal .xterm-color-244 { + color: #808080; +} + +.terminal .xterm-bg-color-244 { + background-color: #808080; +} + +.terminal .xterm-color-245 { + color: #8a8a8a; +} + +.terminal .xterm-bg-color-245 { + background-color: #8a8a8a; +} + +.terminal .xterm-color-246 { + color: #949494; +} + +.terminal .xterm-bg-color-246 { + background-color: #949494; +} + +.terminal .xterm-color-247 { + color: #9e9e9e; +} + +.terminal .xterm-bg-color-247 { + background-color: #9e9e9e; +} + +.terminal .xterm-color-248 { + color: #a8a8a8; +} + +.terminal .xterm-bg-color-248 { + background-color: #a8a8a8; +} + +.terminal .xterm-color-249 { + color: #b2b2b2; +} + +.terminal .xterm-bg-color-249 { + background-color: #b2b2b2; +} + +.terminal .xterm-color-250 { + color: #bcbcbc; +} + +.terminal .xterm-bg-color-250 { + background-color: #bcbcbc; +} + +.terminal .xterm-color-251 { + color: #c6c6c6; +} + +.terminal .xterm-bg-color-251 { + background-color: #c6c6c6; +} + +.terminal .xterm-color-252 { + color: #d0d0d0; +} + +.terminal .xterm-bg-color-252 { + background-color: #d0d0d0; +} + +.terminal .xterm-color-253 { + color: #dadada; +} + +.terminal .xterm-bg-color-253 { + background-color: #dadada; +} + +.terminal .xterm-color-254 { + color: #e4e4e4; +} + +.terminal .xterm-bg-color-254 { + background-color: #e4e4e4; +} + +.terminal .xterm-color-255 { + color: #eeeeee; +} + +.terminal .xterm-bg-color-255 { + background-color: #eeeeee; +} From 5378302763e1a461bab5213aa379d5b9e6dc322c Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sat, 17 Dec 2016 04:09:50 +0000 Subject: [PATCH 340/386] Add a ReactiveCaching concern for use in the KubernetesService --- app/models/concerns/reactive_caching.rb | 110 +++++++++++++ app/workers/reactive_caching_worker.rb | 15 ++ config/sidekiq_queues.yml | 1 + spec/models/concerns/reactive_caching_spec.rb | 145 ++++++++++++++++++ spec/support/reactive_caching_helpers.rb | 38 +++++ spec/workers/reactive_caching_worker_spec.rb | 15 ++ 6 files changed, 324 insertions(+) create mode 100644 app/models/concerns/reactive_caching.rb create mode 100644 app/workers/reactive_caching_worker.rb create mode 100644 spec/models/concerns/reactive_caching_spec.rb create mode 100644 spec/support/reactive_caching_helpers.rb create mode 100644 spec/workers/reactive_caching_worker_spec.rb diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb new file mode 100644 index 00000000000..2db67a3b57f --- /dev/null +++ b/app/models/concerns/reactive_caching.rb @@ -0,0 +1,110 @@ +# The ReactiveCaching concern is used to fetch some data in the background and +# store it in the Rails cache, keeping it up-to-date for as long as it is being +# requested. If the data hasn't been requested for +reactive_cache_lifetime+, +# it stop being refreshed, and then be removed. +# +# Example of use: +# +# class Foo < ActiveRecord::Base +# include ReactiveCaching +# +# self.reactive_cache_key = ->(thing) { ["foo", thing.id] } +# +# after_save :clear_reactive_cache! +# +# def calculate_reactive_cache +# # Expensive operation here. The return value of this method is cached +# end +# +# def result +# with_reactive_cache do |data| +# # ... +# end +# end +# end +# +# In this example, the first time `#result` is called, it will return `nil`. +# However, it will enqueue a background worker to call `#calculate_reactive_cache` +# and set an initial cache lifetime of ten minutes. +# +# Each time the background job completes, it stores the return value of +# `#calculate_reactive_cache`. It is also re-enqueued to run again after +# `reactive_cache_refresh_interval`, so keeping the stored value up to date. +# Calculations are never run concurrently. +# +# Calling `#result` while a value is in the cache will call the block given to +# `#with_reactive_cache`, yielding the cached value. It will also extend the +# lifetime by `reactive_cache_lifetime`. +# +# Once the lifetime has expired, no more background jobs will be enqueued and +# calling `#result` will again return `nil` - starting the process all over +# again +module ReactiveCaching + extend ActiveSupport::Concern + + included do + class_attribute :reactive_cache_lease_timeout + + class_attribute :reactive_cache_key + class_attribute :reactive_cache_lifetime + class_attribute :reactive_cache_refresh_interval + + # defaults + self.reactive_cache_lease_timeout = 2.minutes + + self.reactive_cache_refresh_interval = 1.minute + self.reactive_cache_lifetime = 10.minutes + + def with_reactive_cache(&blk) + within_reactive_cache_lifetime do + data = Rails.cache.read(full_reactive_cache_key) + yield data if data.present? + end + ensure + Rails.cache.write(full_reactive_cache_key('alive'), true, expires_in: self.class.reactive_cache_lifetime) + ReactiveCachingWorker.perform_async(self.class, id) + end + + def clear_reactive_cache! + Rails.cache.delete(full_reactive_cache_key) + end + + def exclusively_update_reactive_cache! + locking_reactive_cache do + within_reactive_cache_lifetime do + enqueuing_update do + value = calculate_reactive_cache + Rails.cache.write(full_reactive_cache_key, value) + end + end + end + end + + private + + def full_reactive_cache_key(*qualifiers) + prefix = self.class.reactive_cache_key + prefix = prefix.call(self) if prefix.respond_to?(:call) + + ([prefix].flatten + qualifiers).join(':') + end + + def locking_reactive_cache + lease = Gitlab::ExclusiveLease.new(full_reactive_cache_key, timeout: reactive_cache_lease_timeout) + uuid = lease.try_obtain + yield if uuid + ensure + Gitlab::ExclusiveLease.cancel(full_reactive_cache_key, uuid) + end + + def within_reactive_cache_lifetime + yield if Rails.cache.read(full_reactive_cache_key('alive')) + end + + def enqueuing_update + yield + ensure + ReactiveCachingWorker.perform_in(self.class.reactive_cache_refresh_interval, self.class, id) + end + end +end diff --git a/app/workers/reactive_caching_worker.rb b/app/workers/reactive_caching_worker.rb new file mode 100644 index 00000000000..9af9dae04f0 --- /dev/null +++ b/app/workers/reactive_caching_worker.rb @@ -0,0 +1,15 @@ +class ReactiveCachingWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue + + def perform(class_name, id) + klass = begin + Kernel.const_get(class_name) + rescue NameError + nil + end + return unless klass + + klass.find_by(id: id).try(:exclusively_update_reactive_cache!) + end +end diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 69136b73946..c22964179d9 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -46,5 +46,6 @@ - [repository_check, 1] - [system_hook, 1] - [git_garbage_collect, 1] + - [reactive_caching, 1] - [cronjob, 1] - [default, 1] diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb new file mode 100644 index 00000000000..a0765a264cf --- /dev/null +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -0,0 +1,145 @@ +require 'spec_helper' + +describe ReactiveCaching, caching: true do + include ReactiveCachingHelpers + + class CacheTest + include ReactiveCaching + + self.reactive_cache_key = ->(thing) { ["foo", thing.id] } + + self.reactive_cache_lifetime = 5.minutes + self.reactive_cache_refresh_interval = 15.seconds + + attr_reader :id + + def initialize(id, &blk) + @id = id + @calculator = blk + end + + def calculate_reactive_cache + @calculator.call + end + + def result + with_reactive_cache do |data| + data / 2 + end + end + end + + let(:now) { Time.now.utc } + + around(:each) do |example| + Timecop.freeze(now) { example.run } + end + + let(:calculation) { -> { 2 + 2 } } + let(:cache_key) { "foo:666" } + let(:instance) { CacheTest.new(666, &calculation) } + + describe '#with_reactive_cache' do + before { stub_reactive_cache } + subject(:go!) { instance.result } + + context 'when cache is empty' do + it { is_expected.to be_nil } + + it 'queues a background worker' do + expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666) + + go! + end + + it 'updates the cache lifespan' do + go! + + expect(reactive_cache_alive?(instance)).to be_truthy + end + end + + context 'when the cache is full' do + before { stub_reactive_cache(instance, 4) } + + it { is_expected.to eq(2) } + + context 'and expired' do + before { invalidate_reactive_cache(instance) } + it { is_expected.to be_nil } + end + end + end + + describe '#clear_reactive_cache!' do + before do + stub_reactive_cache(instance, 4) + instance.clear_reactive_cache! + end + + it { expect(instance.result).to be_nil } + end + + describe '#exclusively_update_reactive_cache!' do + subject(:go!) { instance.exclusively_update_reactive_cache! } + + context 'when the lease is free and lifetime is not exceeded' do + before { stub_reactive_cache(instance, "preexisting") } + + it 'takes and releases the lease' do + expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return("000000") + expect(Gitlab::ExclusiveLease).to receive(:cancel).with(cache_key, "000000") + + go! + end + + it 'caches the result of #calculate_reactive_cache' do + go! + + expect(read_reactive_cache(instance)).to eq(calculation.call) + end + + it "enqueues a repeat worker" do + expect_reactive_cache_update_queued(instance) + + go! + end + + context 'and #calculate_reactive_cache raises an exception' do + before { stub_reactive_cache(instance, "preexisting") } + let(:calculation) { -> { raise "foo"} } + + it 'leaves the cache untouched' do + expect { go! }.to raise_error("foo") + expect(read_reactive_cache(instance)).to eq("preexisting") + end + + it 'enqueues a repeat worker' do + expect_reactive_cache_update_queued(instance) + + expect { go! }.to raise_error("foo") + end + end + end + + context 'when lifetime is exceeded' do + it 'skips the calculation' do + expect(instance).to receive(:calculate_reactive_cache).never + + go! + end + end + + context 'when the lease is already taken' do + before do + expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(nil) + end + + it 'skips the calculation' do + expect(instance).to receive(:calculate_reactive_cache).never + + go! + end + end + end +end diff --git a/spec/support/reactive_caching_helpers.rb b/spec/support/reactive_caching_helpers.rb new file mode 100644 index 00000000000..279db3c5748 --- /dev/null +++ b/spec/support/reactive_caching_helpers.rb @@ -0,0 +1,38 @@ +module ReactiveCachingHelpers + def reactive_cache_key(subject, *qualifiers) + ([subject.class.reactive_cache_key.call(subject)].flatten + qualifiers).join(':') + end + + def stub_reactive_cache(subject = nil, data = nil) + allow(ReactiveCachingWorker).to receive(:perform_async) + allow(ReactiveCachingWorker).to receive(:perform_in) + write_reactive_cache(subject, data) if data + end + + def read_reactive_cache(subject) + Rails.cache.read(reactive_cache_key(subject)) + end + + def write_reactive_cache(subject, data) + start_reactive_cache_lifetime(subject) + Rails.cache.write(reactive_cache_key(subject), data) + end + + def reactive_cache_alive?(subject) + Rails.cache.read(reactive_cache_key(subject, 'alive')) + end + + def invalidate_reactive_cache(subject) + Rails.cache.delete(reactive_cache_key(subject, 'alive')) + end + + def start_reactive_cache_lifetime(subject) + Rails.cache.write(reactive_cache_key(subject, 'alive'), true) + end + + def expect_reactive_cache_update_queued(subject) + expect(ReactiveCachingWorker). + to receive(:perform_in). + with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id) + end +end diff --git a/spec/workers/reactive_caching_worker_spec.rb b/spec/workers/reactive_caching_worker_spec.rb new file mode 100644 index 00000000000..5f4453c15d6 --- /dev/null +++ b/spec/workers/reactive_caching_worker_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe ReactiveCachingWorker do + let(:project) { create(:kubernetes_project) } + let(:service) { project.deployment_service } + subject { described_class.new.perform("KubernetesService", service.id) } + + describe '#perform' do + it 'calls #exclusively_update_reactive_cache!' do + expect_any_instance_of(KubernetesService).to receive(:exclusively_update_reactive_cache!) + + subject + end + end +end From c3d972f4e861059312c2708dacb57999416fcc70 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 22 Nov 2016 19:55:56 +0000 Subject: [PATCH 341/386] Add terminals to the Kubernetes deployment service --- .../projects/environments_controller.rb | 25 +++- app/models/concerns/reactive_caching.rb | 4 + app/models/environment.rb | 8 ++ .../project_services/deployment_service.rb | 18 +++ .../project_services/kubernetes_service.rb | 67 +++++++++-- app/serializers/environment_entity.rb | 8 ++ lib/gitlab/kubernetes.rb | 80 +++++++++++++ lib/gitlab/workhorse.rb | 13 +++ .../projects/environments_controller_spec.rb | 68 +++++++++++ spec/factories/projects.rb | 2 +- spec/lib/gitlab/kubernetes_spec.rb | 39 +++++++ spec/lib/gitlab/workhorse_spec.rb | 36 ++++++ spec/models/environment_spec.rb | 60 +++++++++- .../kubernetes_service_spec.rb | 109 ++++++++++++++---- spec/support/kubernetes_helpers.rb | 52 +++++++++ 15 files changed, 546 insertions(+), 43 deletions(-) create mode 100644 lib/gitlab/kubernetes.rb create mode 100644 spec/lib/gitlab/kubernetes_spec.rb create mode 100644 spec/support/kubernetes_helpers.rb diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 6bd4cb3f2f5..a1b39c6a78a 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -9,7 +9,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController def index @scope = params[:scope] @environments = project.environments - + respond_to do |format| format.html format.json do @@ -56,6 +56,29 @@ class Projects::EnvironmentsController < Projects::ApplicationController redirect_to polymorphic_path([project.namespace.becomes(Namespace), project, new_action]) end + def terminal + # Currently, this acts as a hint to load the terminal details into the cache + # if they aren't there already. In the future, users will need these details + # to choose between terminals to connect to. + @terminals = environment.terminals + end + + # GET .../terminal.ws : implemented in gitlab-workhorse + def terminal_websocket_authorize + Gitlab::Workhorse.verify_api_request!(request.headers) + + # Just return the first terminal for now. If the list is in the process of + # being looked up, this may result in a 404 response, so the frontend + # should retry + terminal = environment.terminals.try(:first) + if terminal + set_workhorse_internal_api_content_type + render json: Gitlab::Workhorse.terminal_websocket(terminal) + else + render text: 'Not found', status: 404 + end + end + private def environment_params diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb index 2db67a3b57f..944519a3070 100644 --- a/app/models/concerns/reactive_caching.rb +++ b/app/models/concerns/reactive_caching.rb @@ -55,6 +55,10 @@ module ReactiveCaching self.reactive_cache_refresh_interval = 1.minute self.reactive_cache_lifetime = 10.minutes + def calculate_reactive_cache + raise NotImplementedError + end + def with_reactive_cache(&blk) within_reactive_cache_lifetime do data = Rails.cache.read(full_reactive_cache_key) diff --git a/app/models/environment.rb b/app/models/environment.rb index 8ef1c841ea3..5cde94b3509 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -128,6 +128,14 @@ class Environment < ActiveRecord::Base end end + def has_terminals? + project.deployment_service.present? && available? && last_deployment.present? + end + + def terminals + project.deployment_service.terminals(self) if has_terminals? + end + # An environment name is not necessarily suitable for use in URLs, DNS # or other third-party contexts, so provide a slugified version. A slug has # the following properties: diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb index da6be9dd7b7..ab353a1abe6 100644 --- a/app/models/project_services/deployment_service.rb +++ b/app/models/project_services/deployment_service.rb @@ -12,4 +12,22 @@ class DeploymentService < Service def predefined_variables [] end + + # Environments may have a number of terminals. Should return an array of + # hashes describing them, e.g.: + # + # [{ + # :selectors => {"a" => "b", "foo" => "bar"}, + # :url => "wss://external.example.com/exec", + # :headers => {"Authorization" => "Token xxx"}, + # :subprotocols => ["foo"], + # :ca_pem => "----BEGIN CERTIFICATE...", # optional + # :created_at => Time.now.utc + # }] + # + # Selectors should be a set of values that uniquely identify a particular + # terminal + def terminals(environment) + raise NotImplementedError + end end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index f5fbf8b353b..085125ca9dc 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -1,4 +1,9 @@ class KubernetesService < DeploymentService + include Gitlab::Kubernetes + include ReactiveCaching + + self.reactive_cache_key = ->(service) { [ service.class.model_name.singular, service.project_id ] } + # Namespace defaults to the project path, but can be overridden in case that # is an invalid or inappropriate name prop_accessor :namespace @@ -25,6 +30,8 @@ class KubernetesService < DeploymentService length: 1..63 end + after_save :clear_reactive_cache! + def initialize_properties if properties.nil? self.properties = {} @@ -41,7 +48,8 @@ class KubernetesService < DeploymentService end def help - '' + 'To enable terminal access to Kubernetes environments, label your ' \ + 'deployments with `app=$CI_ENVIRONMENT_SLUG`' end def to_param @@ -75,9 +83,9 @@ class KubernetesService < DeploymentService # Check we can connect to the Kubernetes API def test(*args) - kubeclient = build_kubeclient - kubeclient.discover + kubeclient = build_kubeclient! + kubeclient.discover { success: kubeclient.discovered, result: "Checked API discovery endpoint" } rescue => err { success: false, result: err } @@ -93,20 +101,48 @@ class KubernetesService < DeploymentService variables end + # Constructs a list of terminals from the reactive cache + # + # Returns nil if the cache is empty, in which case you should try again a + # short time later + def terminals(environment) + with_reactive_cache do |data| + pods = data.fetch(:pods, nil) + filter_pods(pods, app: environment.slug). + flat_map { |pod| terminals_for_pod(api_url, namespace, pod) }. + map { |terminal| add_terminal_auth(terminal, token, ca_pem) } + end + end + + # Caches all pods in the namespace so other calls don't need to block on + # network access. + def calculate_reactive_cache + return unless active? && project && !project.pending_delete? + + kubeclient = build_kubeclient! + + # Store as hashes, rather than as third-party types + pods = begin + kubeclient.get_pods(namespace: namespace).as_json + rescue KubeException => err + raise err unless err.error_code == 404 + [] + end + + # We may want to cache extra things in the future + { pods: pods } + end + private - def build_kubeclient(api_path = '/api', api_version = 'v1') - return nil unless api_url && namespace && token - - url = URI.parse(api_url) - url.path = url.path[0..-2] if url.path[-1] == "/" - url.path += api_path + def build_kubeclient!(api_path: 'api', api_version: 'v1') + raise "Incomplete settings" unless api_url && namespace && token ::Kubeclient::Client.new( - url, + join_api_url(api_path), api_version, - ssl_options: kubeclient_ssl_options, auth_options: kubeclient_auth_options, + ssl_options: kubeclient_ssl_options, http_proxy_uri: ENV['http_proxy'] ) end @@ -125,4 +161,13 @@ class KubernetesService < DeploymentService def kubeclient_auth_options { bearer_token: token } end + + def join_api_url(*parts) + url = URI.parse(api_url) + prefix = url.path.sub(%r{/+\z}, '') + + url.path = [ prefix, *parts ].join("/") + + url.to_s + end end diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index 7e0fc9c071e..e7ef01258ef 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -8,6 +8,7 @@ class EnvironmentEntity < Grape::Entity expose :environment_type expose :last_deployment, using: DeploymentEntity expose :stoppable? + expose :has_terminals?, as: :has_terminals expose :environment_path do |environment| namespace_project_environment_path( @@ -23,5 +24,12 @@ class EnvironmentEntity < Grape::Entity environment) end + expose :terminal_path, if: ->(environment, _) { environment.has_terminals? } do |environment| + terminal_namespace_project_environment_path( + environment.project.namespace, + environment.project, + environment) + end + expose :created_at, :updated_at end diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb new file mode 100644 index 00000000000..288771c1c12 --- /dev/null +++ b/lib/gitlab/kubernetes.rb @@ -0,0 +1,80 @@ +module Gitlab + # Helper methods to do with Kubernetes network services & resources + module Kubernetes + # This is the comand that is run to start a terminal session. Kubernetes + # expects `command=foo&command=bar, not `command[]=foo&command[]=bar` + EXEC_COMMAND = URI.encode_www_form( + ['sh', '-c', 'bash || sh'].map { |value| ['command', value] } + ) + + # Filters an array of pods (as returned by the kubernetes API) by their labels + def filter_pods(pods, labels = {}) + pods.select do |pod| + metadata = pod.fetch("metadata", {}) + pod_labels = metadata.fetch("labels", nil) + next unless pod_labels + + labels.all? { |k, v| pod_labels[k.to_s] == v } + end + end + + # Converts a pod (as returned by the kubernetes API) into a terminal + def terminals_for_pod(api_url, namespace, pod) + metadata = pod.fetch("metadata", {}) + status = pod.fetch("status", {}) + spec = pod.fetch("spec", {}) + + containers = spec["containers"] + pod_name = metadata["name"] + phase = status["phase"] + + return unless containers.present? && pod_name.present? && phase == "Running" + + created_at = DateTime.parse(metadata["creationTimestamp"]) rescue nil + + containers.map do |container| + { + selectors: { pod: pod_name, container: container["name"] }, + url: container_exec_url(api_url, namespace, pod_name, container["name"]), + subprotocols: ['channel.k8s.io'], + headers: Hash.new { |h, k| h[k] = [] }, + created_at: created_at, + } + end + end + + def add_terminal_auth(terminal, token, ca_pem = nil) + terminal[:headers]['Authorization'] << "Bearer #{token}" + terminal[:ca_pem] = ca_pem if ca_pem.present? + terminal + end + + def container_exec_url(api_url, namespace, pod_name, container_name) + url = URI.parse(api_url) + url.path = [ + url.path.sub(%r{/+\z}, ''), + 'api', 'v1', + 'namespaces', ERB::Util.url_encode(namespace), + 'pods', ERB::Util.url_encode(pod_name), + 'exec' + ].join('/') + + url.query = { + container: container_name, + tty: true, + stdin: true, + stdout: true, + stderr: true, + }.to_query + '&' + EXEC_COMMAND + + case url.scheme + when 'http' + url.scheme = 'ws' + when 'https' + url.scheme = 'wss' + end + + url.to_s + end + end +end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index aeb1a26e1ba..d28bb583fe7 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -95,6 +95,19 @@ module Gitlab ] end + def terminal_websocket(terminal) + details = { + 'Terminal' => { + 'Subprotocols' => terminal[:subprotocols], + 'Url' => terminal[:url], + 'Header' => terminal[:headers] + } + } + details['Terminal']['CAPem'] = terminal[:ca_pem] if terminal.has_key?(:ca_pem) + + details + end + def version path = Rails.root.join(VERSION_FILE) path.readable? ? path.read.chomp : 'unknown' diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index bc5e2711125..7afa8b1bc28 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -71,6 +71,74 @@ describe Projects::EnvironmentsController do end end + describe 'GET #terminal' do + context 'with valid id' do + it 'responds with a status code 200' do + get :terminal, environment_params + + expect(response).to have_http_status(200) + end + + it 'loads the terminals for the enviroment' do + expect_any_instance_of(Environment).to receive(:terminals) + + get :terminal, environment_params + end + end + + context 'with invalid id' do + it 'responds with a status code 404' do + get :terminal, environment_params(id: 666) + + expect(response).to have_http_status(404) + end + end + end + + describe 'GET #terminal_websocket_authorize' do + context 'with valid workhorse signature' do + before do + allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_return(nil) + end + + context 'and valid id' do + it 'returns the first terminal for the environment' do + expect_any_instance_of(Environment). + to receive(:terminals). + and_return([:fake_terminal]) + + expect(Gitlab::Workhorse). + to receive(:terminal_websocket). + with(:fake_terminal). + and_return(workhorse: :response) + + get :terminal_websocket_authorize, environment_params + + expect(response).to have_http_status(200) + expect(response.headers["Content-Type"]).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(response.body).to eq('{"workhorse":"response"}') + end + end + + context 'and invalid id' do + it 'returns 404' do + get :terminal_websocket_authorize, environment_params(id: 666) + expect(response).to have_http_status(404) + end + end + end + + context 'with invalid workhorse signature' do + it 'aborts with an exception' do + allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_raise(JWT::DecodeError) + + expect { get :terminal_websocket_authorize, environment_params }.to raise_error(JWT::DecodeError) + # controller tests don't set the response status correctly. It's enough + # to check that the action raised an exception + end + end + end + def environment_params(opts = {}) opts.reverse_merge(namespace_id: project.namespace, project_id: project, diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 0d072d6a690..c941fb5ef4b 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -140,7 +140,7 @@ FactoryGirl.define do active: true, properties: { namespace: project.path, - api_url: 'https://kubernetes.example.com/api', + api_url: 'https://kubernetes.example.com', token: 'a' * 40, } ) diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb new file mode 100644 index 00000000000..c9bd52a3b8f --- /dev/null +++ b/spec/lib/gitlab/kubernetes_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Gitlab::Kubernetes do + include described_class + + describe '#container_exec_url' do + let(:api_url) { 'https://example.com' } + let(:namespace) { 'default' } + let(:pod_name) { 'pod1' } + let(:container_name) { 'container1' } + + subject(:result) { URI::parse(container_exec_url(api_url, namespace, pod_name, container_name)) } + + it { expect(result.scheme).to eq('wss') } + it { expect(result.host).to eq('example.com') } + it { expect(result.path).to eq('/api/v1/namespaces/default/pods/pod1/exec') } + it { expect(result.query).to eq('container=container1&stderr=true&stdin=true&stdout=true&tty=true&command=sh&command=-c&command=bash+%7C%7C+sh') } + + context 'with a HTTP API URL' do + let(:api_url) { 'http://example.com' } + + it { expect(result.scheme).to eq('ws') } + end + + context 'with a path prefix in the API URL' do + let(:api_url) { 'https://example.com/prefix/' } + it { expect(result.path).to eq('/prefix/api/v1/namespaces/default/pods/pod1/exec') } + end + + context 'with arguments that need urlencoding' do + let(:namespace) { 'default namespace' } + let(:pod_name) { 'pod 1' } + let(:container_name) { 'container 1' } + + it { expect(result.path).to eq('/api/v1/namespaces/default%20namespace/pods/pod%201/exec') } + it { expect(result.query).to match(/\Acontainer=container\+1&/) } + end + end +end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index b5b685da904..61da91dcbd3 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -37,6 +37,42 @@ describe Gitlab::Workhorse, lib: true do end end + describe '.terminal_websocket' do + def terminal(ca_pem: nil) + out = { + subprotocols: ['foo'], + url: 'wss://example.com/terminal.ws', + headers: { 'Authorization' => ['Token x'] } + } + out[:ca_pem] = ca_pem if ca_pem + out + end + + def workhorse(ca_pem: nil) + out = { + 'Terminal' => { + 'Subprotocols' => ['foo'], + 'Url' => 'wss://example.com/terminal.ws', + 'Header' => { 'Authorization' => ['Token x'] } + } + } + out['Terminal']['CAPem'] = ca_pem if ca_pem + out + end + + context 'without ca_pem' do + subject { Gitlab::Workhorse.terminal_websocket(terminal) } + + it { is_expected.to eq(workhorse) } + end + + context 'with ca_pem' do + subject { Gitlab::Workhorse.terminal_websocket(terminal(ca_pem: "foo")) } + + it { is_expected.to eq(workhorse(ca_pem: "foo")) } + end + end + describe '.send_git_diff' do let(:diff_refs) { double(base_sha: "base", head_sha: "head") } subject { described_class.send_git_patch(repository, diff_refs) } diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 97cbb093ed2..93eb402e060 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' describe Environment, models: true do - subject(:environment) { create(:environment) } + let(:project) { create(:empty_project) } + subject(:environment) { create(:environment, project: project) } it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:deployments) } @@ -31,6 +32,8 @@ describe Environment, models: true do end describe '#includes_commit?' do + let(:project) { create(:project) } + context 'without a last deployment' do it "returns false" do expect(environment.includes_commit?('HEAD')).to be false @@ -38,9 +41,6 @@ describe Environment, models: true do end context 'with a last deployment' do - let(:project) { create(:project) } - let(:environment) { create(:environment, project: project) } - let!(:deployment) do create(:deployment, environment: environment, sha: project.commit('master').id) end @@ -65,7 +65,6 @@ describe Environment, models: true do describe '#first_deployment_for' do let(:project) { create(:project) } - let!(:environment) { create(:environment, project: project) } let!(:deployment) { create(:deployment, environment: environment, ref: commit.parent.id) } let!(:deployment1) { create(:deployment, environment: environment, ref: commit.id) } let(:head_commit) { project.commit } @@ -196,6 +195,57 @@ describe Environment, models: true do end end + describe '#has_terminals?' do + subject { environment.has_terminals? } + + context 'when the enviroment is available' do + context 'with a deployment service' do + let(:project) { create(:kubernetes_project) } + + context 'and a deployment' do + let!(:deployment) { create(:deployment, environment: environment) } + it { is_expected.to be_truthy } + end + + context 'but no deployments' do + it { is_expected.to be_falsy } + end + end + + context 'without a deployment service' do + it { is_expected.to be_falsy } + end + end + + context 'when the environment is unavailable' do + let(:project) { create(:kubernetes_project) } + before { environment.stop } + it { is_expected.to be_falsy } + end + end + + describe '#terminals' do + let(:project) { create(:kubernetes_project) } + subject { environment.terminals } + + context 'when the environment has terminals' do + before { allow(environment).to receive(:has_terminals?).and_return(true) } + + it 'returns the terminals from the deployment service' do + expect(project.deployment_service). + to receive(:terminals).with(environment). + and_return(:fake_terminals) + + is_expected.to eq(:fake_terminals) + end + end + + context 'when the environment does not have terminals' do + before { allow(environment).to receive(:has_terminals?).and_return(false) } + it { is_expected.to eq(nil) } + end + end + describe '#slug' do it "is automatically generated" do expect(environment.slug).not_to be_nil diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 3603602e41d..4f3cd14e941 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -1,7 +1,29 @@ require 'spec_helper' -describe KubernetesService, models: true do - let(:project) { create(:empty_project) } +describe KubernetesService, models: true, caching: true do + include KubernetesHelpers + include ReactiveCachingHelpers + + let(:project) { create(:kubernetes_project) } + let(:service) { project.kubernetes_service } + + # We use Kubeclient to interactive with the Kubernetes API. It will + # GET /api/v1 for a list of resources the API supports. This must be stubbed + # in addition to any other HTTP requests we expect it to perform. + let(:discovery_url) { service.api_url + '/api/v1' } + let(:discovery_response) { { body: kube_discovery_body.to_json } } + + let(:pods_url) { service.api_url + "/api/v1/namespaces/#{service.namespace}/pods" } + let(:pods_response) { { body: kube_pods_body(kube_pod).to_json } } + + def stub_kubeclient_discover + WebMock.stub_request(:get, discovery_url).to_return(discovery_response) + end + + def stub_kubeclient_pods + stub_kubeclient_discover + WebMock.stub_request(:get, pods_url).to_return(pods_response) + end describe "Associations" do it { is_expected.to belong_to :project } @@ -65,22 +87,15 @@ describe KubernetesService, models: true do end describe '#test' do - let(:project) { create(:kubernetes_project) } - let(:service) { project.kubernetes_service } - let(:discovery_url) { service.api_url + '/api/v1' } - - # JSON response body from Kubernetes GET /api/v1 request - let(:discovery_response) { { "kind" => "APIResourceList", "groupVersion" => "v1", "resources" => [] }.to_json } + before do + stub_kubeclient_discover + end context 'with path prefix in api_url' do let(:discovery_url) { 'https://kubernetes.example.com/prefix/api/v1' } - before do - service.api_url = 'https://kubernetes.example.com/prefix/' - end - it 'tests with the prefix' do - WebMock.stub_request(:get, discovery_url).to_return(body: discovery_response) + service.api_url = 'https://kubernetes.example.com/prefix/' expect(service.test[:success]).to be_truthy expect(WebMock).to have_requested(:get, discovery_url).once @@ -88,17 +103,12 @@ describe KubernetesService, models: true do end context 'with custom CA certificate' do - let(:certificate) { "CA PEM DATA" } - before do - service.update_attributes!(ca_pem: certificate) - end - it 'is added to the certificate store' do - cert = double("certificate") + service.ca_pem = "CA PEM DATA" - expect(OpenSSL::X509::Certificate).to receive(:new).with(certificate).and_return(cert) + cert = double("certificate") + expect(OpenSSL::X509::Certificate).to receive(:new).with(service.ca_pem).and_return(cert) expect_any_instance_of(OpenSSL::X509::Store).to receive(:add_cert).with(cert) - WebMock.stub_request(:get, discovery_url).to_return(body: discovery_response) expect(service.test[:success]).to be_truthy expect(WebMock).to have_requested(:get, discovery_url).once @@ -107,17 +117,15 @@ describe KubernetesService, models: true do context 'success' do it 'reads the discovery endpoint' do - WebMock.stub_request(:get, discovery_url).to_return(body: discovery_response) - expect(service.test[:success]).to be_truthy expect(WebMock).to have_requested(:get, discovery_url).once end end context 'failure' do - it 'fails to read the discovery endpoint' do - WebMock.stub_request(:get, discovery_url).to_return(status: 404) + let(:discovery_response) { { status: 404 } } + it 'fails to read the discovery endpoint' do expect(service.test[:success]).to be_falsy expect(WebMock).to have_requested(:get, discovery_url).once end @@ -156,4 +164,55 @@ describe KubernetesService, models: true do ) end end + + describe '#terminals' do + let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") } + subject { service.terminals(environment) } + + context 'with invalid pods' do + it 'returns no terminals' do + stub_reactive_cache(service, pods: [ { "bad" => "pod" } ]) + + is_expected.to be_empty + end + end + + context 'with valid pods' do + let(:pod) { kube_pod(app: environment.slug) } + let(:terminals) { kube_terminals(service, pod) } + + it 'returns terminals' do + stub_reactive_cache(service, pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ]) + + is_expected.to eq(terminals + terminals) + end + end + end + + describe '#calculate_reactive_cache' do + before { stub_kubeclient_pods } + subject { service.calculate_reactive_cache } + + context 'when service is inactive' do + before { service.active = false } + + it { is_expected.to be_nil } + end + + context 'when kubernetes responds with valid pods' do + it { is_expected.to eq(pods: [kube_pod]) } + end + + context 'when kubernetes responds with 500' do + let(:pods_response) { { status: 500 } } + + it { expect { subject }.to raise_error(KubeException) } + end + + context 'when kubernetes responds with 404' do + let(:pods_response) { { status: 404 } } + + it { is_expected.to eq(pods: []) } + end + end end diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb new file mode 100644 index 00000000000..6c4c246a68b --- /dev/null +++ b/spec/support/kubernetes_helpers.rb @@ -0,0 +1,52 @@ +module KubernetesHelpers + include Gitlab::Kubernetes + + def kube_discovery_body + { "kind" => "APIResourceList", + "resources" => [ + { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, + ], + } + end + + def kube_pods_body(*pods) + { "kind" => "PodList", + "items" => [ kube_pod ], + } + end + + # This is a partial response, it will have many more elements in reality but + # these are the ones we care about at the moment + def kube_pod(app: "valid-pod-label") + { "metadata" => { + "name" => "kube-pod", + "creationTimestamp" => "2016-11-25T19:55:19Z", + "labels" => { "app" => app }, + }, + "spec" => { + "containers" => [ + { "name" => "container-0" }, + { "name" => "container-1" }, + ], + }, + "status" => { "phase" => "Running" }, + } + end + + def kube_terminals(service, pod) + pod_name = pod['metadata']['name'] + containers = pod['spec']['containers'] + + containers.map do |container| + terminal = { + selectors: { pod: pod_name, container: container['name'] }, + url: container_exec_url(service.api_url, service.namespace, pod_name, container['name']), + subprotocols: ['channel.k8s.io'], + headers: { 'Authorization' => ["Bearer #{service.token}"] }, + created_at: DateTime.parse(pod['metadata']['creationTimestamp']) + } + terminal[:ca_pem] = service.ca_pem if service.ca_pem.present? + terminal + end + end +end From f347d5fcadb4b05f367ed571b27e73040f625520 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 19 Dec 2016 14:13:06 -0600 Subject: [PATCH 342/386] Remove inline-block styling from status --- app/assets/stylesheets/framework/page-header.scss | 1 - app/assets/stylesheets/pages/status.scss | 1 - 2 files changed, 2 deletions(-) diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss index fff7d7f7524..625bea96aaa 100644 --- a/app/assets/stylesheets/framework/page-header.scss +++ b/app/assets/stylesheets/framework/page-header.scss @@ -57,7 +57,6 @@ } .ci-status-link { - svg { position: relative; top: 2px; diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 055dacd81f4..a810ed32327 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,6 +1,5 @@ .container-fluid { .ci-status { - display: inline-block; padding: 2px 7px; margin-right: 10px; border: 1px solid $gray-darker; From 95e0fac59ae8174d11873e95a3ef579af476f215 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 19 Dec 2016 22:36:47 +0200 Subject: [PATCH 343/386] Fix Route#rename_children behavior Given group `gitlab` and `gitlab-org` exists. When rename `gitlab` it will rename `gitlab-org` group route too. This commit fixes it Signed-off-by: Dmitriy Zaporozhets --- app/models/route.rb | 2 +- changelogs/unreleased/dz-fix-route-rename.yml | 4 ++++ spec/models/route_spec.rb | 8 +++++--- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/dz-fix-route-rename.yml diff --git a/app/models/route.rb b/app/models/route.rb index d40214b9da6..caf596efa79 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -13,7 +13,7 @@ class Route < ActiveRecord::Base def rename_children # We update each row separately because MySQL does not have regexp_replace. # rubocop:disable Rails/FindEach - Route.where('path LIKE ?', "#{path_was}%").each do |route| + Route.where('path LIKE ?', "#{path_was}/%").each do |route| # Note that update column skips validation and callbacks. # We need this to avoid recursive call of rename_children method route.update_column(:path, route.path.sub(path_was, path)) diff --git a/changelogs/unreleased/dz-fix-route-rename.yml b/changelogs/unreleased/dz-fix-route-rename.yml new file mode 100644 index 00000000000..a649fb169a5 --- /dev/null +++ b/changelogs/unreleased/dz-fix-route-rename.yml @@ -0,0 +1,4 @@ +--- +title: Fix Route#rename_children behavior +merge_request: +author: diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb index 6f491fdf9a0..8481a9bef16 100644 --- a/spec/models/route_spec.rb +++ b/spec/models/route_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Route, models: true do - let!(:group) { create(:group) } + let!(:group) { create(:group, path: 'gitlab') } let!(:route) { group.route } describe 'relationships' do @@ -17,13 +17,15 @@ describe Route, models: true do describe '#rename_children' do let!(:nested_group) { create(:group, path: "test", parent: group) } let!(:deep_nested_group) { create(:group, path: "foo", parent: nested_group) } + let!(:similar_group) { create(:group, path: 'gitlab-org') } + + before { route.update_attributes(path: 'bar') } it "updates children routes with new path" do - route.update_attributes(path: 'bar') - expect(described_class.exists?(path: 'bar')).to be_truthy expect(described_class.exists?(path: 'bar/test')).to be_truthy expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy + expect(described_class.exists?(path: 'gitlab-org')).to be_truthy end end end From 62aa468d1967e83febd840b4a95f066084aee197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Wed, 14 Dec 2016 17:00:57 -0300 Subject: [PATCH 344/386] Update CHANGELOG.md for 8.14.5 [ci skip] --- CHANGELOG.md | 15 +++++++++++++++ ...-project-and-leave-group-should-be-buttons.yml | 5 ----- ...s-settings-hidden-when-builds-are-disabled.yml | 4 ---- changelogs/unreleased/fix-milestone-summary.yml | 4 ---- .../group-members-in-project-members-view.yml | 4 ---- changelogs/unreleased/issue_24020.yml | 4 ---- changelogs/unreleased/issue_25030.yml | 4 ---- changelogs/unreleased/timeago-perf-fix.yml | 4 ---- changelogs/unreleased/unescape-relative-path.yml | 4 ---- 9 files changed, 15 insertions(+), 33 deletions(-) delete mode 100644 changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml delete mode 100644 changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml delete mode 100644 changelogs/unreleased/fix-milestone-summary.yml delete mode 100644 changelogs/unreleased/group-members-in-project-members-view.yml delete mode 100644 changelogs/unreleased/issue_24020.yml delete mode 100644 changelogs/unreleased/issue_25030.yml delete mode 100644 changelogs/unreleased/timeago-perf-fix.yml delete mode 100644 changelogs/unreleased/unescape-relative-path.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index fb13db4dd1c..f1d2f09e34f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.14.5 (2016-12-14) + +- Moved Leave Project and Leave Group buttons to access_request_buttons from the settings dropdown. !7600 +- fix display hook error message. !7775 (basyura) +- Remove wrong '.builds-feature' class from the MR settings fieldset. !7930 +- Avoid escaping relative links in Markdown twice. !7940 (winniehell) +- API: Memoize the current_user so that sudo can work properly. !8017 +- Displays milestone remaining days only when it's present. +- Allow branch names with dots on API endpoint. +- Issue#visible_to_user moved to IssuesFinder to prevent accidental use. +- Shows group members in project members list. +- Encode input when migrating ProcessCommitWorker jobs to prevent migration errors. +- Fixed timeago re-rendering every timeago. +- Fix missing Note access checks by moving Note#search to updated NoteFinder. + ## 8.14.4 (2016-12-08) - Fix diff view permalink highlighting. !7090 diff --git a/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml b/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml deleted file mode 100644 index 99dbe4a32a0..00000000000 --- a/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Moved Leave Project and Leave Group buttons to access_request_buttons from - the settings dropdown -merge_request: 7600 -author: diff --git a/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml b/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml deleted file mode 100644 index a7576e2cbdb..00000000000 --- a/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove wrong '.builds-feature' class from the MR settings fieldset -merge_request: 7930 -author: diff --git a/changelogs/unreleased/fix-milestone-summary.yml b/changelogs/unreleased/fix-milestone-summary.yml deleted file mode 100644 index 3045a15054c..00000000000 --- a/changelogs/unreleased/fix-milestone-summary.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Displays milestone remaining days only when it's present -merge_request: -author: diff --git a/changelogs/unreleased/group-members-in-project-members-view.yml b/changelogs/unreleased/group-members-in-project-members-view.yml deleted file mode 100644 index 415e2b6b1e2..00000000000 --- a/changelogs/unreleased/group-members-in-project-members-view.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Shows group members in project members list -merge_request: -author: diff --git a/changelogs/unreleased/issue_24020.yml b/changelogs/unreleased/issue_24020.yml deleted file mode 100644 index 87310b75296..00000000000 --- a/changelogs/unreleased/issue_24020.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "fix display hook error message" -merge_request: 7775 -author: basyura diff --git a/changelogs/unreleased/issue_25030.yml b/changelogs/unreleased/issue_25030.yml deleted file mode 100644 index e18b8d6a79b..00000000000 --- a/changelogs/unreleased/issue_25030.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow branch names with dots on API endpoint -merge_request: -author: diff --git a/changelogs/unreleased/timeago-perf-fix.yml b/changelogs/unreleased/timeago-perf-fix.yml deleted file mode 100644 index 265e7db29a9..00000000000 --- a/changelogs/unreleased/timeago-perf-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed timeago re-rendering every timeago -merge_request: -author: diff --git a/changelogs/unreleased/unescape-relative-path.yml b/changelogs/unreleased/unescape-relative-path.yml deleted file mode 100644 index 755b0379a16..00000000000 --- a/changelogs/unreleased/unescape-relative-path.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Avoid escaping relative links in Markdown twice -merge_request: 7940 -author: winniehell From 5f1739638882dd630cd814c4501c4147737f4663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Wed, 14 Dec 2016 17:04:12 -0300 Subject: [PATCH 345/386] Update CHANGELOG.md for 8.13.10 [ci skip] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d2f09e34f..786b0128aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -279,6 +279,13 @@ entry. - Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix 404 when visit /projects page +## 8.13.10 (2016-12-14) + +- API: Memoize the current_user so that sudo can work properly. !8017 +- Filter `authentication_token`, `incoming_email_token` and `runners_token` parameters. +- Issue#visible_to_user moved to IssuesFinder to prevent accidental use. +- Fix missing Note access checks by moving Note#search to updated NoteFinder. + ## 8.13.9 (2016-12-08) - Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615 From 00f076d71b3ba831051d627271e8b0cec6f5324b Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 19 Dec 2016 14:54:07 -0600 Subject: [PATCH 346/386] Update font size of detail page header to 14px --- app/assets/stylesheets/pages/detail_page.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 80baebd5ea3..9b28df1afc5 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -2,7 +2,6 @@ padding: $gl-padding-top 0; border-bottom: 1px solid $border-color; color: $gl-text-color-dark; - font-size: 16px; line-height: 34px; .author { From d41c8f48143acf865183a324e3c18dd286d423ba Mon Sep 17 00:00:00 2001 From: Ryan Harris Date: Fri, 16 Dec 2016 20:03:57 -0500 Subject: [PATCH 347/386] Even out padding on plus button in breadcrumb menu Changed plus button padding to 6px 10px --- app/assets/stylesheets/pages/tree.scss | 1 + changelogs/unreleased/25740-fix-new-branch-button-padding.yml | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 changelogs/unreleased/25740-fix-new-branch-button-padding.yml diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index c0341db7289..13be08104e6 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -14,6 +14,7 @@ .add-to-tree { vertical-align: top; + padding: 6px 10px; } .tree-table { diff --git a/changelogs/unreleased/25740-fix-new-branch-button-padding.yml b/changelogs/unreleased/25740-fix-new-branch-button-padding.yml new file mode 100644 index 00000000000..7da8f9357a7 --- /dev/null +++ b/changelogs/unreleased/25740-fix-new-branch-button-padding.yml @@ -0,0 +1,4 @@ +--- +title: Made the padding on the plus button in the breadcrumb menu even +merge_request: +author: Ryan Harris From e06f88effa842c73d3827593f8d28846207bfca0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 19 Dec 2016 22:12:30 +0100 Subject: [PATCH 348/386] Fix specs --- features/project/service.feature | 6 ++-- features/steps/project/services.rb | 6 ++-- .../services/slack_slash_command_spec.rb | 36 ++++++++----------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/features/project/service.feature b/features/project/service.feature index 892db48d785..cce5f58adec 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -37,10 +37,10 @@ Feature: Project Services And I fill Assembla settings Then I should see Assembla service settings saved - Scenario: Activate Slack service + Scenario: Activate Slack notifications service When I visit project "Shop" services page - And I click Slack Notifications service link - And I fill Slack Notifications settings + And I click Slack notifications service link + And I fill Slack notifications settings Then I should see Slack Notifications service settings saved Scenario: Activate Pushover service diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 06a1afedbd9..a4d29770922 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -137,11 +137,11 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(find_field('Colorize messages').value).to eq '1' end - step 'I click Slack Notifications service link' do - click_link 'Slack Notifications' + step 'I click Slack notifications service link' do + click_link 'Slack notifications' end - step 'I fill Slack Notifications settings' do + step 'I fill Slack notifications settings' do check 'Active' fill_in 'Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' click_button 'Save' diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb index 70e203efcf5..32b32f7ae8e 100644 --- a/spec/features/projects/services/slack_slash_command_spec.rb +++ b/spec/features/projects/services/slack_slash_command_spec.rb @@ -12,37 +12,29 @@ feature 'Slack slash commands', feature: true do login_as(user) end - scenario 'user visits the slack slash command config page', js: true do - it 'shows a help message' do - visit edit_namespace_project_service_path(project.namespace, project, service) + scenario 'user visits the slack slash command config page and shows a help message', js: true do + visit edit_namespace_project_service_path(project.namespace, project, service) - wait_for_ajax + wait_for_ajax - expect(page).to have_content('This service allows GitLab users to perform common') - end + expect(page).to have_content('This service allows GitLab users to perform common') end - scenario 'saving a token' do - given(:token) { ('a'..'z').to_a.join } + scenario 'shows the token after saving' do + visit edit_namespace_project_service_path(project.namespace, project, service) - it 'shows the token after saving' do - visit edit_namespace_project_service_path(project.namespace, project, service) + fill_in 'service_token', with: 'token' + click_on 'Save' - fill_in 'service_token', with: token - click_on 'Save' + value = find_field('service_token').value - value = find_field('service_token').value - - expect(value).to eq(token) - end + expect(value).to eq('token') end - scenario 'the trigger url' do - it 'shows the correct url' do - visit edit_namespace_project_service_path(project.namespace, project, service) + scenario 'shows the correct trigger url' do + visit edit_namespace_project_service_path(project.namespace, project, service) - value = find_field('url').value - expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger") - end + value = find_field('url').value + expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger") end end From 3db5b7033b13c21b904a21f751bc0f19156ea155 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Thu, 15 Dec 2016 00:59:04 +0000 Subject: [PATCH 349/386] Add terminal UI and controller actions --- .../components/environment.js.es6 | 3 ++ .../components/environment_item.js.es6 | 16 ++++++++++ .../environment_terminal_button.js.es6 | 27 ++++++++++++++++ app/assets/stylesheets/framework/buttons.scss | 7 ++++ app/assets/stylesheets/pages/pipelines.scss | 20 ++++++++++++ .../projects/environments_controller.rb | 14 +++++--- app/serializers/environment_entity.rb | 10 +++--- app/serializers/request_aware_entity.rb | 4 +++ .../environments/_terminal_button.html.haml | 3 ++ .../projects/environments/index.html.haml | 1 + .../projects/environments/show.html.haml | 1 + .../projects/environments/terminal.html.haml | 22 +++++++++++++ app/views/shared/icons/_icon_terminal.svg | 1 + config/routes/project.rb | 2 ++ .../projects/environments_controller_spec.rb | 1 + spec/factories/projects.rb | 10 ++++-- spec/features/environment_spec.rb | 32 +++++++++++++++++++ spec/features/environments_spec.rb | 28 ++++++++++++++++ 18 files changed, 189 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/environments/components/environment_terminal_button.js.es6 create mode 100644 app/views/projects/environments/_terminal_button.html.haml create mode 100644 app/views/projects/environments/terminal.html.haml create mode 100644 app/views/shared/icons/_icon_terminal.svg diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index d04adecd207..facd653fd72 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -76,6 +76,7 @@ helpPagePath: environmentsData.helpPagePath, commitIconSvg: environmentsData.commitIconSvg, playIconSvg: environmentsData.playIconSvg, + terminalIconSvg: environmentsData.terminalIconSvg, }; }, @@ -230,6 +231,7 @@ :can-create-deployment="canCreateDeploymentParsed" :can-read-environment="canReadEnvironmentParsed" :play-icon-svg="playIconSvg" + :terminal-icon-svg="terminalIconSvg" :commit-icon-svg="commitIconSvg"> diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 4674d5202e6..b26a40aa268 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -8,6 +8,7 @@ /*= require ./environment_external_url */ /*= require ./environment_stop */ /*= require ./environment_rollback */ +/*= require ./environment_terminal_button */ (() => { /** @@ -33,6 +34,7 @@ 'external-url-component': window.gl.environmentsList.ExternalUrlComponent, 'stop-component': window.gl.environmentsList.StopComponent, 'rollback-component': window.gl.environmentsList.RollbackComponent, + 'terminal-button-component': window.gl.environmentsList.TerminalButtonComponent, }, props: { @@ -68,6 +70,12 @@ type: String, required: false, }, + + terminalIconSvg: { + type: String, + required: false, + }, + }, data() { @@ -506,6 +514,14 @@ +
    + + +
    +
    { + window.gl = window.gl || {}; + window.gl.environmentsList = window.gl.environmentsList || {}; + + window.gl.environmentsList.TerminalButtonComponent = Vue.component('terminal-button-component', { + props: { + terminalPath: { + type: String, + default: '', + }, + terminalIconSvg: { + type: String, + default: '', + }, + }, + + template: ` + + + + `, + }); +})(); diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 59ff17ad2c1..a11f1cd7735 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -230,6 +230,13 @@ } } +.btn-terminal { + svg { + height: 14px; + width: 18px; + } +} + .btn-lg { padding: 12px 20px; } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index be22e7bdc79..7905d2f2e7c 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -734,3 +734,23 @@ padding: 5px 5px 5px 7px; } } + +.terminal-icon { + margin-left: 3px; +} + +.terminal-container { + .content-block { + border-bottom: none; + } + + #terminal { + margin-top: 10px; + min-height: 450px; + box-sizing: border-box; + + > div { + min-height: 450px; + } + } +} diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index a1b39c6a78a..87cc36253f1 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -4,7 +4,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :authorize_create_environment!, only: [:new, :create] before_action :authorize_create_deployment!, only: [:stop] before_action :authorize_update_environment!, only: [:edit, :update] - before_action :environment, only: [:show, :edit, :update, :stop] + before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize] + before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize] + before_action :verify_api_request!, only: :terminal_websocket_authorize def index @scope = params[:scope] @@ -14,7 +16,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController format.html format.json do render json: EnvironmentSerializer - .new(project: @project) + .new(project: @project, user: current_user) .represent(@environments) end end @@ -65,11 +67,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController # GET .../terminal.ws : implemented in gitlab-workhorse def terminal_websocket_authorize - Gitlab::Workhorse.verify_api_request!(request.headers) - # Just return the first terminal for now. If the list is in the process of # being looked up, this may result in a 404 response, so the frontend - # should retry + # should retry those errors terminal = environment.terminals.try(:first) if terminal set_workhorse_internal_api_content_type @@ -81,6 +81,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController private + def verify_api_request! + Gitlab::Workhorse.verify_api_request!(request.headers) + end + def environment_params params.require(:environment).permit(:name, :external_url) end diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index e7ef01258ef..5d15eb8d3d3 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -8,7 +8,6 @@ class EnvironmentEntity < Grape::Entity expose :environment_type expose :last_deployment, using: DeploymentEntity expose :stoppable? - expose :has_terminals?, as: :has_terminals expose :environment_path do |environment| namespace_project_environment_path( @@ -25,10 +24,11 @@ class EnvironmentEntity < Grape::Entity end expose :terminal_path, if: ->(environment, _) { environment.has_terminals? } do |environment| - terminal_namespace_project_environment_path( - environment.project.namespace, - environment.project, - environment) + can?(request.user, :admin_environment, environment.project) && + terminal_namespace_project_environment_path( + environment.project.namespace, + environment.project, + environment) end expose :created_at, :updated_at diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb index ff8c1142abc..e159d750cb7 100644 --- a/app/serializers/request_aware_entity.rb +++ b/app/serializers/request_aware_entity.rb @@ -8,4 +8,8 @@ module RequestAwareEntity def request @options.fetch(:request) end + + def can?(object, action, subject) + Ability.allowed?(object, action, subject) + end end diff --git a/app/views/projects/environments/_terminal_button.html.haml b/app/views/projects/environments/_terminal_button.html.haml new file mode 100644 index 00000000000..97de9c95de7 --- /dev/null +++ b/app/views/projects/environments/_terminal_button.html.haml @@ -0,0 +1,3 @@ +- if environment.has_terminals? && can?(current_user, :admin_environment, @project) + = link_to terminal_namespace_project_environment_path(@project.namespace, @project, environment), class: 'btn terminal-button' do + = icon('terminal') diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 6aae035b3e0..0c6f696f5b9 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -15,4 +15,5 @@ "help-page-path" => help_page_path("ci/environments"), "css-class" => container_class, "commit-icon-svg" => custom_icon("icon_commit"), + "terminal-icon-svg" => custom_icon("icon_terminal"), "play-icon-svg" => custom_icon("icon_play")}} diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index bcac73d3698..6e0d9456900 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -8,6 +8,7 @@ %h3.page-title= @environment.name.capitalize .col-md-3 .nav-controls + = render 'projects/environments/terminal_button', environment: @environment = render 'projects/environments/external_url', environment: @environment - if can?(current_user, :update_environment, @environment) = link_to 'Edit', edit_namespace_project_environment_path(@project.namespace, @project, @environment), class: 'btn' diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml new file mode 100644 index 00000000000..a6726e509e0 --- /dev/null +++ b/app/views/projects/environments/terminal.html.haml @@ -0,0 +1,22 @@ +- @no_container = true +- page_title "Terminal for environment", @environment.name += render "projects/pipelines/head" + +- content_for :page_specific_javascripts do + = stylesheet_link_tag "xterm/xterm" + = page_specific_javascript_tag("terminal/terminal_bundle.js") + +%div{class: container_class} + .top-area + .row + .col-sm-6 + %h3.page-title + Terminal for environment + = @environment.name + + .col-sm-6 + .nav-controls + = render 'projects/deployments/actions', deployment: @environment.last_deployment + +.terminal-container{class: container_class} + #terminal{data:{project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws"}} diff --git a/app/views/shared/icons/_icon_terminal.svg b/app/views/shared/icons/_icon_terminal.svg new file mode 100644 index 00000000000..c80f44c3edf --- /dev/null +++ b/app/views/shared/icons/_icon_terminal.svg @@ -0,0 +1 @@ + diff --git a/config/routes/project.rb b/config/routes/project.rb index e17d6bae10c..80cc47ab9a8 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -148,6 +148,8 @@ constraints(ProjectUrlConstrainer.new) do resources :environments, except: [:destroy] do member do post :stop + get :terminal + get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil } end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 7afa8b1bc28..7ac1d62d1b1 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -123,6 +123,7 @@ describe Projects::EnvironmentsController do context 'and invalid id' do it 'returns 404' do get :terminal_websocket_authorize, environment_params(id: 666) + expect(response).to have_http_status(404) end end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index c941fb5ef4b..f7fa834d7a2 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -42,6 +42,12 @@ FactoryGirl.define do end end + trait :test_repo do + after :create do |project| + TestEnv.copy_repo(project) + end + end + # Nest Project Feature attributes transient do wiki_access_level ProjectFeature::ENABLED @@ -91,9 +97,7 @@ FactoryGirl.define do factory :project, parent: :empty_project do path { 'gitlabhq' } - after :create do |project| - TestEnv.copy_repo(project) - end + test_repo end factory :forked_project_with_submodules, parent: :empty_project do diff --git a/spec/features/environment_spec.rb b/spec/features/environment_spec.rb index 0c1939fd885..56f6cd2e095 100644 --- a/spec/features/environment_spec.rb +++ b/spec/features/environment_spec.rb @@ -38,6 +38,10 @@ feature 'Environment', :feature do scenario 'does not show a re-deploy button for deployment without build' do expect(page).not_to have_link('Re-deploy') end + + scenario 'does not show terminal button' do + expect(page).not_to have_terminal_button + end end context 'with related deployable present' do @@ -60,6 +64,10 @@ feature 'Environment', :feature do expect(page).not_to have_link('Stop') end + scenario 'does not show terminal button' do + expect(page).not_to have_terminal_button + end + context 'with manual action' do given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') } @@ -84,6 +92,26 @@ feature 'Environment', :feature do end end + context 'with terminal' do + let(:project) { create(:kubernetes_project, :test_repo) } + + context 'for project master' do + let(:role) { :master } + + scenario 'it shows the terminal button' do + expect(page).to have_terminal_button + end + end + + context 'for developer' do + let(:role) { :developer } + + scenario 'does not show terminal button' do + expect(page).not_to have_terminal_button + end + end + end + context 'with stop action' do given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') } given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') } @@ -158,4 +186,8 @@ feature 'Environment', :feature do environment.project, environment) end + + def have_terminal_button + have_link(nil, href: terminal_namespace_project_environment_path(project.namespace, project, environment)) + end end diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index e1b97b31e5d..72b984cfab8 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -113,6 +113,10 @@ feature 'Environments page', :feature, :js do expect(page).not_to have_css('external-url') end + scenario 'does not show terminal button' do + expect(page).not_to have_terminal_button + end + context 'with external_url' do given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') } given(:build) { create(:ci_build, pipeline: pipeline) } @@ -145,6 +149,26 @@ feature 'Environments page', :feature, :js do end end end + + context 'with terminal' do + let(:project) { create(:kubernetes_project, :test_repo) } + + context 'for project master' do + let(:role) { :master } + + scenario 'it shows the terminal button' do + expect(page).to have_terminal_button + end + end + + context 'for developer' do + let(:role) { :developer } + + scenario 'does not show terminal button' do + expect(page).not_to have_terminal_button + end + end + end end end end @@ -195,6 +219,10 @@ feature 'Environments page', :feature, :js do end end + def have_terminal_button + have_link(nil, href: terminal_namespace_project_environment_path(project.namespace, project, environment)) + end + def visit_environments(project) visit namespace_project_environments_path(project.namespace, project) end From 7b7781654e9c01f5928850df0daaf50fd1d89e8a Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 16 Dec 2016 21:41:33 +0000 Subject: [PATCH 350/386] Add changelog entry --- ...thomas-gitlab-ce-22864-kubernetes-deploy-with-terminal.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/nick-thomas-gitlab-ce-22864-kubernetes-deploy-with-terminal.yml diff --git a/changelogs/unreleased/nick-thomas-gitlab-ce-22864-kubernetes-deploy-with-terminal.yml b/changelogs/unreleased/nick-thomas-gitlab-ce-22864-kubernetes-deploy-with-terminal.yml new file mode 100644 index 00000000000..bb4edf80d94 --- /dev/null +++ b/changelogs/unreleased/nick-thomas-gitlab-ce-22864-kubernetes-deploy-with-terminal.yml @@ -0,0 +1,4 @@ +--- +title: Add online terminal support for Kubernetes +merge_request: 7690 +author: From d2212a8b5f2ebc25ab8a007aa09a728779dd9212 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 19 Dec 2016 19:18:16 +0000 Subject: [PATCH 351/386] Add online terminal documentation --- doc/README.md | 1 + .../high_availability/load_balancer.md | 26 ++++--- doc/administration/integration/terminal.md | 73 ++++++++++++++++++ doc/ci/environments.md | 45 ++++++++++- .../environments_terminal_button_on_index.png | Bin 0 -> 79725 bytes .../environments_terminal_button_on_show.png | Bin 0 -> 73210 bytes doc/ci/img/environments_terminal_page.png | Bin 0 -> 117863 bytes doc/project_services/kubernetes.md | 14 ++++ doc/user/permissions.md | 1 + 9 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 doc/administration/integration/terminal.md create mode 100644 doc/ci/img/environments_terminal_button_on_index.png create mode 100644 doc/ci/img/environments_terminal_button_on_show.png create mode 100644 doc/ci/img/environments_terminal_page.png diff --git a/doc/README.md b/doc/README.md index a60a5359540..8bf33cad5e4 100644 --- a/doc/README.md +++ b/doc/README.md @@ -34,6 +34,7 @@ - [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, Twitter. - [Issue closing pattern](administration/issue_closing_pattern.md) Customize how to close an issue from commit messages. - [Koding](administration/integration/koding.md) Set up Koding to use with GitLab. +- [Online terminals](administration/integration/terminal.md) Provide terminal access to environments from within GitLab. - [Libravatar](customization/libravatar.md) Use Libravatar instead of Gravatar for user avatars. - [Log system](administration/logs.md) Log system. - [Environment Variables](administration/environment_variables.md) to configure GitLab. diff --git a/doc/administration/high_availability/load_balancer.md b/doc/administration/high_availability/load_balancer.md index 136f570ac27..e61ea359a6a 100644 --- a/doc/administration/high_availability/load_balancer.md +++ b/doc/administration/high_availability/load_balancer.md @@ -10,11 +10,11 @@ you need to use with GitLab. ## Basic ports -| LB Port | Backend Port | Protocol | -| ------- | ------------ | -------- | -| 80 | 80 | HTTP | -| 443 | 443 | HTTPS [^1] | -| 22 | 22 | TCP | +| LB Port | Backend Port | Protocol | +| ------- | ------------ | --------------- | +| 80 | 80 | HTTP [^1] | +| 443 | 443 | HTTPS [^1] [^2] | +| 22 | 22 | TCP | ## GitLab Pages Ports @@ -25,8 +25,8 @@ GitLab Pages requires a separate VIP. Configure DNS to point the | LB Port | Backend Port | Protocol | | ------- | ------------ | -------- | -| 80 | Varies [^2] | HTTP | -| 443 | Varies [^2] | TCP [^3] | +| 80 | Varies [^3] | HTTP | +| 443 | Varies [^3] | TCP [^4] | ## Alternate SSH Port @@ -50,13 +50,19 @@ Read more on high-availability configuration: 1. [Configure NFS](nfs.md) 1. [Configure the GitLab application servers](gitlab.md) -[^1]: When using HTTPS protocol for port 443, you will need to add an SSL +[^1]: [Terminal support](../../ci/environments.md#terminal-support) requires + your load balancer to correctly handle WebSocket connections. When using + HTTP or HTTPS proxying, this means your load balancer must be configured + to pass through the `Connection` and `Upgrade` hop-by-hop headers. See the + [online terminal](../integration/terminal.md) integration guide for + more details. +[^2]: When using HTTPS protocol for port 443, you will need to add an SSL certificate to the load balancers. If you wish to terminate SSL at the GitLab application server instead, use TCP protocol. -[^2]: The backend port for GitLab Pages depends on the +[^3]: The backend port for GitLab Pages depends on the `gitlab_pages['external_http']` and `gitlab_pages['external_https']` setting. See [GitLab Pages documentation][gitlab-pages] for more details. -[^3]: Port 443 for GitLab Pages should always use the TCP protocol. Users can +[^4]: Port 443 for GitLab Pages should always use the TCP protocol. Users can configure custom domains with custom SSL, which would not be possible if SSL was terminated at the load balancer. diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md new file mode 100644 index 00000000000..05d0a97e554 --- /dev/null +++ b/doc/administration/integration/terminal.md @@ -0,0 +1,73 @@ +# Online terminals + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7690) +in GitLab 8.15. Only project masters and owners can access online terminals. + +With the introduction of the [Kubernetes](../../project_services/kubernetes.md) +project service, GitLab gained the ability to store and use credentials for a +Kubernetes cluster. One of the things it uses these credentials for is providing +access to [online terminals](../../ci/environments.html#online-terminals) +for environments. + +## How it works + +A detailed overview of the architecture of online terminals and how they work +can be found in [this document](https://gitlab.com/gitlab-org/gitlab-workhorse/blob/master/doc/terminal.md). +In brief: + +* GitLab relies on the user to provide their own Kubernetes credentials, and to + appropriately label the pods they create when deploying. +* When a user navigates to the terminal page for an environment, they are served + a JavaScript application that opens a WebSocket connection back to GitLab. +* The WebSocket is handled in [Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse), + rather than the Rails application server. +* Workhorse queries Rails for connection details and user permissions; Rails + queries Kubernetes for them in the background, using [Sidekiq](../troubleshooting/sidekiq.md) +* Workhorse acts as a proxy server between the user's browser and the Kubernetes + API, passing WebSocket frames between the two. +* Workhorse regularly polls Rails, terminating the WebSocket connection if the + user no longer has permission to access the terminal, or if the connection + details have changed. + +## Enabling and disabling terminal support + +As online terminals use WebSockets, every HTTP/HTTPS reverse proxy in front of +Workhorse needs to be configured to pass the `Connection` and `Upgrade` headers +through to the next one in the chain. If you installed Gitlab using Omnibus, or +from source, starting with GitLab 8.15, this should be done by the default +configuration, so there's no need for you to do anything. + +However, if you run a [load balancer](../high_availability/load_balancer.md) in +front of GitLab, you may need to make some changes to your configuration. These +guides document the necessary steps for a selection of popular reverse proxies: + +* [Apache](https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html) +* [NGINX](https://www.nginx.com/blog/websocket-nginx/) +* [HAProxy](http://blog.haproxy.com/2012/11/07/websockets-load-balancing-with-haproxy/) +* [Varnish](https://www.varnish-cache.org/docs/4.1/users-guide/vcl-example-websockets.html) + +Workhorse won't let WebSocket requests through to non-WebSocket endpoints, so +it's safe to enable support for these headers globally. If you'd rather had a +narrower set of rules, you can restrict it to URLs ending with `/terminal.ws` +(although this may still have a few false positives). + +If you installed from source, or have made any configuration changes to your +Omnibus installation before upgrading to 8.15, you may need to make some +changes to your configuration. See the [8.14 to 8.15 upgrade](../../update/8.14-to-8.15.md#nginx-configuration) +document for more details. + +If you'd like to disable online terminal support in GitLab, just stop passing +the `Connection` and `Upgrade` hop-by-hop headers in the *first* HTTP reverse +proxy in the chain. For most users, this will be the NGINX server bundled with +Omnibus Gitlab, in which case, you need to: + +* Find the `nginx['proxy_set_headers']` section of your `gitlab.rb` file +* Ensure the whole block is uncommented, and then comment out or remove the + `Connection` and `Upgrade` lines. + +For your own load balancer, just reverse the configuration changes recommended +by the above guides. + +When these headers are not passed through, Workhorse will return a +`400 Bad Request` response to users attempting to use an online terminal. In +turn, they will receive a `Connection failed` message. diff --git a/doc/ci/environments.md b/doc/ci/environments.md index bad0233a9ce..07d92bb746c 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -25,7 +25,9 @@ Environments are like tags for your CI jobs, describing where code gets deployed Deployments are created when [jobs] deploy versions of code to environments, so every environment can have one or more deployments. GitLab keeps track of your deployments, so you always know what is currently being deployed on your -servers. +servers. If you have a deployment service such as [Kubernetes][kubernetes-service] +enabled for your project, you can use it to assist with your deployments, and +can even access a terminal for your environment from within GitLab! To better understand how environments and deployments work, let's consider an example. We assume that you have already created a project in GitLab and set up @@ -233,6 +235,46 @@ Remember that if your environment's name is `production` (all lowercase), then it will get recorded in [Cycle Analytics](../user/project/cycle_analytics.md). Double the benefit! +## Terminal support + +>**Note:** +Terminal support was added in GitLab 8.15 and is only available to project +masters and owners. + +If you deploy to your environments with the help of a deployment service (e.g., +the [Kubernetes](../project_services/kubernetes.md) service), GitLab can open +a terminal session to your environment! This is a very powerful feature that +allows you to debug issues without leaving the comfort of your web browser. To +enable it, just follow the instructions given in the service documentation. + +Once enabled, your environments will gain a "terminal" button: + +![Terminal button on environment index](img/environments_terminal_button_on_index.png) + +You can also access the terminal button from the page for a specific environment: + +![Terminal button for an environment](img/environments_terminal_button_on_show.png) + +Wherever you find it, clicking the button will take you to a separate page to +establish the terminal session: + +![Terminal page](img/environments_terminal_page.png) + +This works just like any other terminal - you'll be in the container created +by your deployment, so you can run shell commands and get responses in real +time, check the logs, try out configuration or code tweaks, etc. You can open +multiple terminals to the same environment - they each get their own shell +session - and even a multiplexer like `screen` or `tmux`! + +>**Note:** +Container-based deployments often lack basic tools (like an editor), and may +be stopped or restarted at any time. If this happens, you will lose all your +changes! Treat this as a debugging tool, not a comprehensive online IDE. You +can use [Koding](../administration/integration/koding.md) for online +development. + +--- + While this is fine for deploying to some stable environments like staging or production, what happens for branches? So far we haven't defined anything regarding deployments for branches other than `master`. Dynamic environments @@ -524,6 +566,7 @@ Below are some links you may find interesting: [Pipelines]: pipelines.md [jobs]: yaml/README.md#jobs [yaml]: yaml/README.md +[kubernetes-service]: ../project_services/kubernetes.md] [environments]: #environments [deployments]: #deployments [permissions]: ../user/permissions.md diff --git a/doc/ci/img/environments_terminal_button_on_index.png b/doc/ci/img/environments_terminal_button_on_index.png new file mode 100644 index 0000000000000000000000000000000000000000..6f05b2aa343eedc7e20f6f3b2e3f4adb2aa6fbfb GIT binary patch literal 79725 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

    3(E4xqvTCx7%qfdqJodDr*~O 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 B6x;v+ literal 0 HcmV?d00001 diff --git a/doc/ci/img/environments_terminal_page.png b/doc/ci/img/environments_terminal_page.png new file mode 100644 index 0000000000000000000000000000000000000000..fde1bf325a6546abb932bd7fa2a5923d16fec860 GIT binary patch literal 117863 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 TUxvU9wgLzDn(QI$whR9cDX5py literal 0 HcmV?d00001 diff --git a/doc/project_services/kubernetes.md b/doc/project_services/kubernetes.md index fda364b864e..0c5c88dd983 100644 --- a/doc/project_services/kubernetes.md +++ b/doc/project_services/kubernetes.md @@ -47,3 +47,17 @@ GitLab CI build environment: - `KUBE_TOKEN` - `KUBE_NAMESPACE` - `KUBE_CA_PEM` - only if a custom CA bundle was specified + +## Terminal support + +>**NOTE:** +Added in GitLab 8.15. You must be the project owner or have `master` permissions +to use terminals. Support is currently limited to the first container in the +first pod of your environment. + +When enabled, the Kubernetes service adds online [terminal support](../ci/environments.md#terminal-support) +to your environments. This is based on the `exec` functionality found in +Docker and Kubernetes, so you get a new shell session within your existing +containers. To use this integration, you should deploy to Kubernetes using +the deployment variables above, ensuring any pods you create are labelled with +`app=$CI_ENVIRONMENT_SLUG`. diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 39fe2409a29..5ada8748d85 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -33,6 +33,7 @@ The following table depicts the various user permission levels in a project. | See a container registry | | ✓ | ✓ | ✓ | ✓ | | See environments | | ✓ | ✓ | ✓ | ✓ | | Create new environments | | | ✓ | ✓ | ✓ | +| Use environment terminals | | | | ✓ | ✓ | | Stop environments | | | ✓ | ✓ | ✓ | | See a list of merge requests | | ✓ | ✓ | ✓ | ✓ | | Manage/Accept merge requests | | | ✓ | ✓ | ✓ | From 34e317d21ce49c122beb73d2eb695311b75d0b89 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Dec 2016 17:58:46 +0000 Subject: [PATCH 352/386] Adds loading to improve UX --- .../mini_pipeline_graph_dropdown.js.es6 | 42 ++++++++++++++----- app/assets/stylesheets/pages/pipelines.scss | 12 ++++-- .../projects/ci/pipelines/_pipeline.html.haml | 10 ++++- app/views/projects/pipelines/_stage.html.haml | 10 ++--- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 index ce24cbdb705..1db1ad6f017 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 @@ -18,31 +18,34 @@ class MiniPipelineGraph { constructor({ container }) { this.container = container; + this.dropdownListSelector = '.js-builds-dropdown-container'; this.getBuildsList = this.getBuildsList.bind(this); this.bindEvents(); } /** - * Adds an removes the event listener. - * TODO: Remove jQuery when we have a way to handle events properly. + * Adds and removes the event listener. */ bindEvents() { - $(this.container).off('click', 'button.js-builds-dropdown-button', this.getBuildsList); - $(this.container).on('click', 'button.js-builds-dropdown-button', this.getBuildsList); + const dropdownButtonSelector = 'button.js-builds-dropdown-button'; + + $(this.container).off('click', dropdownButtonSelector, this.getBuildsList); + $(this.container).on('click', dropdownButtonSelector, this.getBuildsList); } /** - * For the clicked stage, renders the received html in the sibiling - * element with the `js-builds-dropdown-container` clas + * For the clicked stage, renders the given data in the dropdown list. * - * @param {Element} stageContainer + * @param {HTMLElement} stageContainer * @param {Object} data */ renderBuildsList(stageContainer, data) { - const dropdownContainer = stageContainer.parentElement.querySelector('.js-builds-dropdown-container'); + const dropdownContainer = stageContainer.parentElement.querySelector( + `${this.dropdownListSelector} .js-builds-dropdown-list`, + ); - dropdownContainer.innerHTML = data.html; + dropdownContainer.innerHTML = data; } /** @@ -58,10 +61,29 @@ dataType: 'json', type: 'GET', url: endpoint, - success: data => this.renderBuildsList(e.currentTarget, data), + beforeSend: () => { + this.renderBuildsList(e.currentTarget, ''); + this.toggleLoading(e.currentTarget); + }, + success: (data) => { + this.toggleLoading(e.currentTarget); + this.renderBuildsList(e.currentTarget, data.html); + }, error: () => new Flash('An error occurred while fetching the builds.', 'alert'), }); } + + /** + * Toggles the visibility of the loading icon. + * + * @param {HTMLElement} stageContainer + * @return {type} + */ + toggleLoading(stageContainer) { + stageContainer.parentElement.querySelector( + `${this.dropdownListSelector} .js-builds-dropdown-loading`, + ).classList.toggle('hidden'); + } } window.gl = window.gl || {}; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index b3deac3ab75..ae7b40a9416 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -753,13 +753,19 @@ margin: 0; } + .builds-dropdown-loading { + margin: 10px auto; + width: 18px; + } + .grouped-pipeline-dropdown { right: -172px; top: 23px; - } + min-height: 191px; - .grouped-pipeline-dropdown a { - color: $gl-text-color-light; + a { + color: $gl-text-color-light; + } } .arrow-up { diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index d488eeda2fe..6dfc55aa23c 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -53,13 +53,19 @@ .stage-container.mini-pipeline-graph - if hasMultipleBuilds .dropdown.inline.build-content - %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} + %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} %span{ class: klass } %span.mini-pipeline-graph-icon-container %span{ class: icon_status_klass }= custom_icon(icon_status) = icon('caret-down', class: 'dropdown-caret') - %div.js-builds-dropdown-container + .js-builds-dropdown-container + .dropdown-menu.grouped-pipeline-dropdown + .arrow-up + .js-builds-dropdown-list + + .js-builds-dropdown-loading.builds-dropdown-loading.hidden + %span.fa.fa-spinner.fa-spin - else - if detailed_status.has_details? = link_to detailed_status.details_path, class: klass, title: tooltip do diff --git a/app/views/projects/pipelines/_stage.html.haml b/app/views/projects/pipelines/_stage.html.haml index 83fd518726d..20456e792e7 100644 --- a/app/views/projects/pipelines/_stage.html.haml +++ b/app/views/projects/pipelines/_stage.html.haml @@ -1,6 +1,4 @@ -.dropdown-menu.grouped-pipeline-dropdown - .arrow-up - %ul - - @stage.statuses.each do |status| - %li.dropdown-build - = render 'ci/status/graph_badge', subject: status +%ul + - @stage.statuses.each do |status| + %li.dropdown-build + = render 'ci/status/graph_badge', subject: status From 9edef45ae169e5882dab4950a8143acf7623a06d Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Dec 2016 23:44:34 +0000 Subject: [PATCH 353/386] Resolve conflict Fix tooltips in dropdown --- app/assets/stylesheets/pages/pipelines.scss | 75 ++------------------- app/views/ci/status/_graph_badge.html.haml | 12 ++-- 2 files changed, 10 insertions(+), 77 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index bb320da619f..832d8443dc8 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -463,15 +463,11 @@ white-space: normal; color: $gl-text-color-light; -<<<<<<< HEAD - .dropdown-menu-toggle { background-color: transparent; border: none; padding: 0; color: $gl-text-color-light; - flex-grow: 1; - &:focus { outline: none; @@ -486,10 +482,6 @@ } } - &:hover { - background-color: $stage-hover-bg; - border: 1px solid $stage-hover-border; -======= > .build-content { display: inline-block; padding: 8px 10px 9px; @@ -497,8 +489,7 @@ border: 1px solid $border-color; border-radius: 30px; background-color: $white-light; ->>>>>>> master - + &:hover { background-color: $stage-hover-bg; border: 1px solid $stage-hover-border; @@ -603,22 +594,7 @@ } } } - - // Position in the pipeline graph - .grouped-pipeline-dropdown { -<<<<<<< HEAD - right: -206px; - top: -11px; - } } -======= - padding: 0; - width: 191px; - left: auto; - right: -195px; - top: -4px; - box-shadow: 0 1px 5px $black-transparent; ->>>>>>> master .dropdown-counter-badge { float: right; @@ -632,8 +608,8 @@ padding: 0; width: 191px; left: auto; - right: -206px; - top: -11px; + right: -195px; + top: -4px; box-shadow: 0 1px 5px $black-transparent; a { @@ -644,53 +620,10 @@ } } -<<<<<<< HEAD ul { max-height: 245px; overflow: auto; margin: 5px 0; -======= - .dropdown-build { - color: $gl-text-color-light; - - .build-content { - width: 100%; - } - - .ci-action-icon-container { - font-size: 11px; - position: absolute; - right: 4px; - - i { - width: 25px; - height: 25px; - font-size: 11px; - margin-top: 0; - - &::before { - top: 1px; - left: 1px; - } - } - } - - &:hover { - background-color: $stage-hover-bg; - border-radius: 3px; - color: $gl-text-color; - } - - .stage { - max-width: 100px; - width: 100px; - } - - .ci-status-icon svg { - height: 18px; - width: 18px; - } ->>>>>>> master li { padding-top: 2px; @@ -747,7 +680,7 @@ .dropdown-build { color: $gl-text-color-light; - a.ci-action-icon-container { + .ci-action-icon-container { padding: 0; font-size: 11px; float: right; diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml index 52b4d77d074..dd2f649de9a 100644 --- a/app/views/ci/status/_graph_badge.html.haml +++ b/app/views/ci/status/_graph_badge.html.haml @@ -3,18 +3,18 @@ - subject = local_assigns.fetch(:subject) - status = subject.detailed_status(current_user) - klass = "ci-status-icon ci-status-icon-#{status.group}" +- tooltip = "#{subject.name} - #{status.label}" - if status.has_details? - = link_to status.details_path, class: 'build-content' do + = link_to status.details_path, class: 'build-content has-tooltip', data: { toggle: 'tooltip', title: tooltip } do %span{ class: klass }= custom_icon(status.icon) - .ci-status-text{ 'data-toggle' => 'tooltip', 'data-title' => "#{subject.name} - #{status.label}" }= subject.name + .ci-status-text= subject.name - else - .build-content + .build-content.has-tooltip{ data: { toggle: 'tooltip', title: tooltip } } %span{ class: klass }= custom_icon(status.icon) - .ci-status-text{ 'data-toggle' => 'tooltip', 'data-title' => "#{subject.name} - #{status.label}" }= subject.name + .ci-status-text= subject.name - if status.has_action? - = link_to status.action_path, method: status.action_method, - title: status.action_title, class: 'ci-action-icon-container' do + = link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do %i.ci-action-icon-wrapper = icon(status.action_icon, class: status.action_class) From ac86c495a3fc54be6984c4df2b363e9b4e414b4d Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Dec 2016 23:45:24 +0000 Subject: [PATCH 354/386] Pipeline graph node was one pixel off --- app/assets/stylesheets/pages/pipelines.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 832d8443dc8..3639db026cf 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -489,7 +489,7 @@ border: 1px solid $border-color; border-radius: 30px; background-color: $white-light; - + &:hover { background-color: $stage-hover-bg; border: 1px solid $stage-hover-border; @@ -547,7 +547,7 @@ content: ''; position: absolute; top: 48%; - right: -49px; + right: -48px; border-top: 2px solid $border-color; width: 48px; height: 1px; From 8275efa36bd51ad0d46495341d524b215c01bfff Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Mon, 19 Dec 2016 19:48:49 -0200 Subject: [PATCH 355/386] Fix member with expiration date feature spec --- .../members/master_adds_member_with_expiration_date_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index 27a83fdcd1f..b7273021c95 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -24,7 +24,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature: click_on 'Add to project' end - page.within '.project_member:first-child' do + page.within "#project_member_#{new_member.project_members.first.id}" do expect(page).to have_content('Expires in 4 days') end end @@ -35,7 +35,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature: project.team.add_users([new_member.id], :developer, expires_at: '2016-09-06') visit namespace_project_project_members_path(project.namespace, project) - page.within '.project_member:first-child' do + page.within "#project_member_#{new_member.project_members.first.id}" do find('.js-access-expiration-date').set '2016-08-09' wait_for_ajax expect(page).to have_content('Expires in 3 days') From 212967aefb55e0792a36e67a881b66c8bd871e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 20 Dec 2016 09:45:37 +0100 Subject: [PATCH 356/386] Reject blank environment vcariables in Gitlab::Git::RevList MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/git/rev_list.rb | 4 ++-- spec/lib/gitlab/git/rev_list_spec.rb | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb index 25e9d619697..79dd0cf7df2 100644 --- a/lib/gitlab/git/rev_list.rb +++ b/lib/gitlab/git/rev_list.rb @@ -22,7 +22,7 @@ module Gitlab def valid? environment_variables.all? do |(name, value)| - value.start_with?(project.repository.path_to_repo) + value.to_s.start_with?(project.repository.path_to_repo) end end @@ -35,7 +35,7 @@ module Gitlab end def environment_variables - @environment_variables ||= env.slice(*ALLOWED_VARIABLES) + @environment_variables ||= env.slice(*ALLOWED_VARIABLES).compact end end end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 444639acbaa..1f9c987be0b 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -26,6 +26,13 @@ describe Gitlab::Git::RevList, lib: true do expect(rev_list).not_to be_valid end + + it "ignores nil values" do + env = { var => nil } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).to be_valid + end end end end From 2b0b53cddd7d57ca5dd93437fdffefd7a07af91e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 20 Dec 2016 11:00:56 +0100 Subject: [PATCH 357/386] Add tests for stage API endpoint --- .../projects/pipelines_controller.rb | 4 +-- app/models/ci/pipeline.rb | 5 ++++ app/models/ci/stage.rb | 4 +++ .../projects/pipelines/pipelines_spec.rb | 29 +++++++++++++++++++ spec/models/ci/pipeline_spec.rb | 20 +++++++++++++ spec/models/ci/stage_spec.rb | 11 +++++++ 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 0147072b0f1..cc347922c6a 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -42,9 +42,7 @@ class Projects::PipelinesController < Projects::ApplicationController end def stage - @stage = pipeline.stages.find do |stage| - stage.name == params[:stage] - end + @stage = pipeline.stage(params[:stage]) return not_found unless @stage respond_to do |format| diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 48354cdbefb..f2f6453b3b9 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -116,6 +116,11 @@ module Ci where.not(duration: nil).sum(:duration) end + def stage(name) + stage = Ci::Stage.new(self, name: name) + stage unless stage.statuses_count.zero? + end + def stages_count statuses.select(:stage).distinct.count end diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 7ef59445d77..d035eda6df5 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -18,6 +18,10 @@ module Ci name end + def statuses_count + @statuses_count ||= statuses.count + end + def status @status ||= statuses.latest.status end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index f3731698a18..e1c6b4c115c 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -152,6 +152,35 @@ describe "Pipelines" do end end + describe 'GET /:project/pipelines/stage?name=stage' do + let!(:pipeline) do + create(:ci_empty_pipeline, project: project, ref: 'master', + status: 'running') + end + + context 'when accessing existing stage' do + let!(:build) do + create(:ci_build, pipeline: pipeline, stage: 'build') + end + + before do + visit stage_namespace_project_pipeline_path( + project.namespace, project, pipeline, format: :json, stage: 'build') + end + + it { expect(page).to have_http_status(:ok) } + end + + context 'when accessing unknown stage' do + before do + visit stage_namespace_project_pipeline_path( + project.namespace, project, pipeline, format: :json, stage: 'test') + end + + it { expect(page).to have_http_status(:not_found) } + end + end + describe 'POST /:project/pipelines' do let(:project) { create(:project) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 52dd41065e9..67cc3e6be68 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -175,6 +175,26 @@ describe Ci::Pipeline, models: true do end end + describe '#stage' do + subject { pipeline.stage('test') } + + context 'with status in stage' do + let!(:status) { create(:commit_status, pipeline: pipeline, stage: 'test') } + + it 'return stage object' do + is_expected.to be_a(Ci::Stage) + end + end + + context 'without status in stage' do + let!(:status) { create(:commit_status, pipeline: pipeline, stage: 'build') } + + it 'return stage object' do + is_expected.to be_nil + end + end + end + describe 'state machine' do let(:current) { Time.now.change(usec: 0) } let(:build) { create_build('build1', 0) } diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index 8fff38f7cda..d8dce0f1cc6 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -28,6 +28,17 @@ describe Ci::Stage, models: true do end end + describe '#statuses_count' do + let!(:stage_build) { create_job(:ci_build) } + let!(:other_build) { create_job(:ci_build, stage: 'other stage') } + + subject { stage.statuses_count } + + it "statuses only from current stage" do + is_expected.to eq(1) + end + end + describe '#builds' do let!(:stage_build) { create_job(:ci_build) } let!(:commit_status) { create_job(:commit_status) } From 5ec1c140d991b37d665c47e52dba4a453cc305a4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 20 Dec 2016 11:26:24 +0100 Subject: [PATCH 358/386] Improve specs --- spec/features/projects/pipelines/pipelines_spec.rb | 7 +++++-- spec/models/ci/pipeline_spec.rb | 14 +++++++++----- spec/models/ci/stage_spec.rb | 6 ++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index e1c6b4c115c..24e501b0151 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -152,7 +152,7 @@ describe "Pipelines" do end end - describe 'GET /:project/pipelines/stage?name=stage' do + describe 'GET /:project/pipelines/stage.json?name=stage' do let!(:pipeline) do create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running') @@ -168,7 +168,10 @@ describe "Pipelines" do project.namespace, project, pipeline, format: :json, stage: 'build') end - it { expect(page).to have_http_status(:ok) } + it do + expect(page).to have_http_status(:ok) + expect(JSON.parse(page.source)).to include("html") + end end context 'when accessing unknown stage' do diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 67cc3e6be68..5e1a9fa8dd8 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -179,15 +179,19 @@ describe Ci::Pipeline, models: true do subject { pipeline.stage('test') } context 'with status in stage' do - let!(:status) { create(:commit_status, pipeline: pipeline, stage: 'test') } - - it 'return stage object' do - is_expected.to be_a(Ci::Stage) + before do + create(:commit_status, pipeline: pipeline, stage: 'test') end + + it { expect(subject).to be_a(Ci::Stage) } + it { expect(subject.name).to eq('stage') } + it { expect(subject.statues).not_to be_empty } end context 'without status in stage' do - let!(:status) { create(:commit_status, pipeline: pipeline, stage: 'build') } + before do + create(:commit_status, pipeline: pipeline, stage: 'build') + end it 'return stage object' do is_expected.to be_nil diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index d8dce0f1cc6..65a302e9d9b 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -29,8 +29,10 @@ describe Ci::Stage, models: true do end describe '#statuses_count' do - let!(:stage_build) { create_job(:ci_build) } - let!(:other_build) { create_job(:ci_build, stage: 'other stage') } + before do + create_job(:ci_build) } + create_job(:ci_build, stage: 'other stage') + end subject { stage.statuses_count } From 51353a6bc7257612412c8712230c0d47667da021 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 20 Dec 2016 11:27:24 +0100 Subject: [PATCH 359/386] Always show dropdown --- .../projects/ci/pipelines/_pipeline.html.haml | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 6dfc55aa23c..ce8b9f9e8af 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -45,32 +45,25 @@ - if stage.status - detailed_status = stage.detailed_status(current_user) - klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status.group}" - - hasMultipleBuilds = stage.statuses.count > 1 - icon_status = "#{detailed_status.icon}_borderless" - icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status.group}" - tooltip = "#{stage.name}: #{detailed_status.label || 'not found'}" .stage-container.mini-pipeline-graph - - if hasMultipleBuilds - .dropdown.inline.build-content - %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} - %span{ class: klass } - %span.mini-pipeline-graph-icon-container - %span{ class: icon_status_klass }= custom_icon(icon_status) - = icon('caret-down', class: 'dropdown-caret') - - .js-builds-dropdown-container - .dropdown-menu.grouped-pipeline-dropdown - .arrow-up - .js-builds-dropdown-list - - .js-builds-dropdown-loading.builds-dropdown-loading.hidden - %span.fa.fa-spinner.fa-spin - - else - - if detailed_status.has_details? - = link_to detailed_status.details_path, class: klass, title: tooltip do + .dropdown.inline.build-content + %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} + %span{ class: klass } %span.mini-pipeline-graph-icon-container %span{ class: icon_status_klass }= custom_icon(icon_status) + = icon('caret-down', class: 'dropdown-caret') + + .js-builds-dropdown-container + .dropdown-menu.grouped-pipeline-dropdown + .arrow-up + .js-builds-dropdown-list + + .js-builds-dropdown-loading.builds-dropdown-loading.hidden + %span.fa.fa-spinner.fa-spin %td - if pipeline.duration From ff31f4f922e2b35f20dc4d2ba8be951d95c887be Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 20 Dec 2016 11:05:39 +0000 Subject: [PATCH 360/386] Rename "Online terminal" to "Web terminal" in the docs --- doc/README.md | 2 +- .../high_availability/load_balancer.md | 4 ++-- doc/administration/integration/terminal.md | 16 ++++++++-------- doc/ci/environments.md | 6 +++--- doc/project_services/kubernetes.md | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/README.md b/doc/README.md index 8bf33cad5e4..ee69684b53b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -34,7 +34,7 @@ - [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, Twitter. - [Issue closing pattern](administration/issue_closing_pattern.md) Customize how to close an issue from commit messages. - [Koding](administration/integration/koding.md) Set up Koding to use with GitLab. -- [Online terminals](administration/integration/terminal.md) Provide terminal access to environments from within GitLab. +- [Web terminals](administration/integration/terminal.md) Provide terminal access to environments from within GitLab. - [Libravatar](customization/libravatar.md) Use Libravatar instead of Gravatar for user avatars. - [Log system](administration/logs.md) Log system. - [Environment Variables](administration/environment_variables.md) to configure GitLab. diff --git a/doc/administration/high_availability/load_balancer.md b/doc/administration/high_availability/load_balancer.md index e61ea359a6a..1824829903c 100644 --- a/doc/administration/high_availability/load_balancer.md +++ b/doc/administration/high_availability/load_balancer.md @@ -50,11 +50,11 @@ Read more on high-availability configuration: 1. [Configure NFS](nfs.md) 1. [Configure the GitLab application servers](gitlab.md) -[^1]: [Terminal support](../../ci/environments.md#terminal-support) requires +[^1]: [Web terminal](../../ci/environments.md#web-terminals) support requires your load balancer to correctly handle WebSocket connections. When using HTTP or HTTPS proxying, this means your load balancer must be configured to pass through the `Connection` and `Upgrade` hop-by-hop headers. See the - [online terminal](../integration/terminal.md) integration guide for + [web terminal](../integration/terminal.md) integration guide for more details. [^2]: When using HTTPS protocol for port 443, you will need to add an SSL certificate to the load balancers. If you wish to terminate SSL at the diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md index 05d0a97e554..a1d1bb03b50 100644 --- a/doc/administration/integration/terminal.md +++ b/doc/administration/integration/terminal.md @@ -1,17 +1,17 @@ -# Online terminals +# Web terminals > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7690) -in GitLab 8.15. Only project masters and owners can access online terminals. +in GitLab 8.15. Only project masters and owners can access web terminals. With the introduction of the [Kubernetes](../../project_services/kubernetes.md) project service, GitLab gained the ability to store and use credentials for a Kubernetes cluster. One of the things it uses these credentials for is providing -access to [online terminals](../../ci/environments.html#online-terminals) +access to [web terminals](../../ci/environments.html#web-terminals) for environments. ## How it works -A detailed overview of the architecture of online terminals and how they work +A detailed overview of the architecture of web terminals and how they work can be found in [this document](https://gitlab.com/gitlab-org/gitlab-workhorse/blob/master/doc/terminal.md). In brief: @@ -31,7 +31,7 @@ In brief: ## Enabling and disabling terminal support -As online terminals use WebSockets, every HTTP/HTTPS reverse proxy in front of +As web terminals use WebSockets, every HTTP/HTTPS reverse proxy in front of Workhorse needs to be configured to pass the `Connection` and `Upgrade` headers through to the next one in the chain. If you installed Gitlab using Omnibus, or from source, starting with GitLab 8.15, this should be done by the default @@ -56,7 +56,7 @@ Omnibus installation before upgrading to 8.15, you may need to make some changes to your configuration. See the [8.14 to 8.15 upgrade](../../update/8.14-to-8.15.md#nginx-configuration) document for more details. -If you'd like to disable online terminal support in GitLab, just stop passing +If you'd like to disable web terminal support in GitLab, just stop passing the `Connection` and `Upgrade` hop-by-hop headers in the *first* HTTP reverse proxy in the chain. For most users, this will be the NGINX server bundled with Omnibus Gitlab, in which case, you need to: @@ -69,5 +69,5 @@ For your own load balancer, just reverse the configuration changes recommended by the above guides. When these headers are not passed through, Workhorse will return a -`400 Bad Request` response to users attempting to use an online terminal. In -turn, they will receive a `Connection failed` message. +`400 Bad Request` response to users attempting to use a web terminal. In turn, +they will receive a `Connection failed` message. diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 07d92bb746c..98cd29c9567 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -27,7 +27,7 @@ so every environment can have one or more deployments. GitLab keeps track of your deployments, so you always know what is currently being deployed on your servers. If you have a deployment service such as [Kubernetes][kubernetes-service] enabled for your project, you can use it to assist with your deployments, and -can even access a terminal for your environment from within GitLab! +can even access a web terminal for your environment from within GitLab! To better understand how environments and deployments work, let's consider an example. We assume that you have already created a project in GitLab and set up @@ -235,10 +235,10 @@ Remember that if your environment's name is `production` (all lowercase), then it will get recorded in [Cycle Analytics](../user/project/cycle_analytics.md). Double the benefit! -## Terminal support +## Web terminals >**Note:** -Terminal support was added in GitLab 8.15 and is only available to project +Web terminals were added in GitLab 8.15 and are only available to project masters and owners. If you deploy to your environments with the help of a deployment service (e.g., diff --git a/doc/project_services/kubernetes.md b/doc/project_services/kubernetes.md index 0c5c88dd983..59d5da702f8 100644 --- a/doc/project_services/kubernetes.md +++ b/doc/project_services/kubernetes.md @@ -48,16 +48,16 @@ GitLab CI build environment: - `KUBE_NAMESPACE` - `KUBE_CA_PEM` - only if a custom CA bundle was specified -## Terminal support +## Web terminals >**NOTE:** Added in GitLab 8.15. You must be the project owner or have `master` permissions to use terminals. Support is currently limited to the first container in the first pod of your environment. -When enabled, the Kubernetes service adds online [terminal support](../ci/environments.md#terminal-support) -to your environments. This is based on the `exec` functionality found in +When enabled, the Kubernetes service adds [web terminal](../ci/environments.md#web-terminals) +support to your environments. This is based on the `exec` functionality found in Docker and Kubernetes, so you get a new shell session within your existing containers. To use this integration, you should deploy to Kubernetes using the deployment variables above, ensuring any pods you create are labelled with -`app=$CI_ENVIRONMENT_SLUG`. +`app=$CI_ENVIRONMENT_SLUG`. GitLab will do the rest! From 11040589c803410837d67fb481221e9d6ef1d969 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 20 Dec 2016 10:08:15 +0000 Subject: [PATCH 361/386] Adds tests for the MiniPipelineGraph class --- .../mini_pipeline_graph_dropdown.js.es6 | 6 ++- .../fixtures/mini_dropdown_graph.html.haml | 8 +++ .../mini_pipeline_graph_dropdown_spec.js.es6 | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 spec/javascripts/fixtures/mini_dropdown_graph.html.haml create mode 100644 spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6 diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 index 1db1ad6f017..73d48074e41 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 @@ -16,8 +16,8 @@ */ (() => { class MiniPipelineGraph { - constructor({ container }) { - this.container = container; + constructor(opts = {}) { + this.container = opts.container || ''; this.dropdownListSelector = '.js-builds-dropdown-container'; this.getBuildsList = this.getBuildsList.bind(this); @@ -57,6 +57,8 @@ getBuildsList(e) { const endpoint = e.currentTarget.dataset.stageEndpoint; + console.log('ENDPOINT', endpoint); + return $.ajax({ dataType: 'json', type: 'GET', diff --git a/spec/javascripts/fixtures/mini_dropdown_graph.html.haml b/spec/javascripts/fixtures/mini_dropdown_graph.html.haml new file mode 100644 index 00000000000..e9bf7568e95 --- /dev/null +++ b/spec/javascripts/fixtures/mini_dropdown_graph.html.haml @@ -0,0 +1,8 @@ +%div.js-builds-dropdown-tests + %button.dropdown.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar'} + Dropdown + %div.js-builds-dropdown-container + %div.js-builds-dropdown-list + + %div.js-builds-dropdown-loading.builds-dropdown-loading.hidden + %span.fa.fa-spinner.fa-spin diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6 b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6 new file mode 100644 index 00000000000..d1793e9308e --- /dev/null +++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6 @@ -0,0 +1,51 @@ +/* eslint-disable no-new */ + +//= require flash +//= require mini_pipeline_graph_dropdown + +(() => { + describe('Mini Pipeline Graph Dropdown', () => { + fixture.preload('mini_dropdown_graph'); + + beforeEach(() => { + fixture.load('mini_dropdown_graph'); + }); + + describe('When is initialized', () => { + it('should initialize without errors when no options are given', () => { + const miniPipelineGraph = new window.gl.MiniPipelineGraph(); + + expect(miniPipelineGraph.dropdownListSelector).toEqual('.js-builds-dropdown-container'); + }); + + it('should set the container as the given prop', () => { + const container = '.foo'; + + const miniPipelineGraph = new window.gl.MiniPipelineGraph({ container }); + + expect(miniPipelineGraph.container).toEqual(container); + }); + }); + + describe('When dropdown is clicked', () => { + it('should call getBuildsList', () => { + const getBuildsListSpy = spyOn(gl.MiniPipelineGraph.prototype, 'getBuildsList').and.callFake(function () {}); + + new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }); + + document.querySelector('.js-builds-dropdown-button').click(); + + expect(getBuildsListSpy).toHaveBeenCalled(); + }); + + it('should make a request to the endpoint provided in the html', () => { + const ajaxSpy = spyOn($, 'ajax').and.callFake(function () {}); + + new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }); + + document.querySelector('.js-builds-dropdown-button').click(); + expect(ajaxSpy.calls.allArgs()[0][0].url).toEqual('foobar'); + }); + }); + }); +})(); From dd5965a15ad3fcb0f411b25e2079ef20f311e446 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 20 Dec 2016 11:14:46 +0000 Subject: [PATCH 362/386] Remove console.log --- app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 index 73d48074e41..31b65a22723 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 @@ -57,8 +57,6 @@ getBuildsList(e) { const endpoint = e.currentTarget.dataset.stageEndpoint; - console.log('ENDPOINT', endpoint); - return $.ajax({ dataType: 'json', type: 'GET', From c87d93d462bb83caebd22bd759d8a1ead845d6a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 19 Dec 2016 16:26:59 +0100 Subject: [PATCH 363/386] Improve specs for Repositories API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../4269-public-repositories-api.yml | 2 +- spec/requests/api/repositories_spec.rb | 378 +++++++++++------- spec/support/api/status_shared_examples.rb | 42 ++ 3 files changed, 287 insertions(+), 135 deletions(-) create mode 100644 spec/support/api/status_shared_examples.rb diff --git a/changelogs/unreleased/4269-public-repositories-api.yml b/changelogs/unreleased/4269-public-repositories-api.yml index b88ce63845d..861307a022b 100644 --- a/changelogs/unreleased/4269-public-repositories-api.yml +++ b/changelogs/unreleased/4269-public-repositories-api.yml @@ -1,4 +1,4 @@ --- title: Allow Repositories API GET endpoints to be requested anonymously -merge_request: +merge_request: 8148 author: diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 67f0bc537fe..fe28ad1d1a1 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -7,174 +7,239 @@ describe API::Repositories, api: true do include WorkhorseHelpers let(:user) { create(:user) } - let(:user2) { create(:user) } + let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } } let!(:project) { create(:project, creator_id: user.id) } let!(:master) { create(:project_member, :master, user: user, project: project) } - let!(:guest) { create(:project_member, :guest, user: user2, project: project) } + + shared_context 'disabled repository' do + before do + project.project_feature.update_attributes!( + repository_access_level: ProjectFeature::DISABLED, + merge_requests_access_level: ProjectFeature::DISABLED, + builds_access_level: ProjectFeature::DISABLED + ) + expect(project.feature_available?(:repository, current_user)).to be false + end + end describe "GET /projects/:id/repository/tree" do - context "authorized user" do - before { project.team << [user2, :reporter] } + let(:route) { "/projects/#{project.id}/repository/tree" } - shared_examples_for 'repository tree' do - it 'returns the repository tree' do - get api("/projects/#{project.id}/repository/tree", current_user) + shared_examples_for 'repository tree' do + it 'returns the repository tree' do + get api(route, current_user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(200) - first_commit = json_response.first + first_commit = json_response.first - expect(json_response).to be_an Array - expect(first_commit['name']).to eq('bar') - expect(first_commit['type']).to eq('tree') - expect(first_commit['mode']).to eq('040000') + expect(json_response).to be_an Array + expect(first_commit['name']).to eq('bar') + expect(first_commit['type']).to eq('tree') + expect(first_commit['mode']).to eq('040000') + end + + context 'when ref does not exist' do + it_behaves_like '404 response' do + let(:request) { get api("#{route}?ref_name=foo", current_user) } + let(:message) { '404 Tree Not Found' } end end - context 'when unauthenticated' do - it_behaves_like 'repository tree' do + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get api(route, current_user) } + end + end + + context 'with recursive=1' do + it 'returns recursive project paths tree' do + get api("#{route}?recursive=1", current_user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response[4]['name']).to eq('html') + expect(json_response[4]['path']).to eq('files/html') + expect(json_response[4]['type']).to eq('tree') + expect(json_response[4]['mode']).to eq('040000') + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get api(route, current_user) } + end + end + + context 'when ref does not exist' do + it_behaves_like '404 response' do + let(:request) { get api("#{route}?recursive=1&ref_name=foo", current_user) } + let(:message) { '404 Tree Not Found' } + end + end + end + end + + context 'when unauthenticated', 'and project is public' do + it_behaves_like 'repository tree' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route) } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do + it_behaves_like 'repository tree' do + let(:current_user) { user } + end + end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest) } + end + end + end + + { + 'blobs/:sha' => 'blobs/master', + 'commits/:sha/blob' => 'commits/master/blob' + }.each do |desc_path, example_path| + describe "GET /projects/:id/repository/#{desc_path}" do + let(:route) { "/projects/#{project.id}/repository/#{example_path}?filepath=README.md" } + + shared_examples_for 'repository blob' do + it 'returns the repository blob' do + get api(route, current_user) + + expect(response).to have_http_status(200) + end + + context 'when sha does not exist' do + it_behaves_like '404 response' do + let(:request) { get api(route.sub('master', 'invalid_branch_name'), current_user) } + let(:message) { '404 Commit Not Found' } + end + end + + context 'when filepath does not exist' do + it_behaves_like '404 response' do + let(:request) { get api(route.sub('README.md', 'README.invalid'), current_user) } + let(:message) { '404 File Not Found' } + end + end + + context 'when no filepath is given' do + it_behaves_like '400 response' do + let(:request) { get api(route.sub('?filepath=README.md', ''), current_user) } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get api(route, current_user) } + end + end + end + + context 'when unauthenticated', 'and project is public' do + it_behaves_like 'repository blob' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do - it_behaves_like 'repository tree' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route) } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do + it_behaves_like 'repository blob' do let(:current_user) { user } end end - it 'returns a 404 for unknown ref' do - get api("/projects/#{project.id}/repository/tree?ref_name=foo", user) - expect(response).to have_http_status(404) - - expect(json_response).to be_an Object - json_response['message'] == '404 Tree Not Found' + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest) } + end end end - - context "unauthorized user" do - it "does not return project commits" do - get api("/projects/#{project.id}/repository/tree") - - expect(response).to have_http_status(404) - end - end - end - - describe 'GET /projects/:id/repository/tree?recursive=1' do - context 'authorized user' do - before { project.team << [user2, :reporter] } - - it 'should return recursive project paths tree' do - get api("/projects/#{project.id}/repository/tree?recursive=1", user) - - expect(response.status).to eq(200) - - expect(json_response).to be_an Array - expect(json_response[4]['name']).to eq('html') - expect(json_response[4]['path']).to eq('files/html') - expect(json_response[4]['type']).to eq('tree') - expect(json_response[4]['mode']).to eq('040000') - end - - it 'returns a 404 for unknown ref' do - get api("/projects/#{project.id}/repository/tree?ref_name=foo&recursive=1", user) - expect(response).to have_http_status(404) - - expect(json_response).to be_an Object - json_response['message'] == '404 Tree Not Found' - end - end - - context "unauthorized user" do - it "does not return project commits" do - get api("/projects/#{project.id}/repository/tree?recursive=1") - - expect(response).to have_http_status(404) - end - end - end - - describe "GET /projects/:id/repository/blobs/:sha & /projects/:id/repository/commits/:sha" do - shared_examples_for 'repository blob' do - it 'returns the repository blob for /repository/blobs/master' do - get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", current_user) - - expect(response).to have_http_status(200) - end - - it 'returns the repository blob for /repository/commits/master' do - get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", current_user) - - expect(response).to have_http_status(200) - end - end - - context 'when unauthenticated' do - it_behaves_like 'repository blob' do - let(:project) { create(:project, :public) } - let(:current_user) { nil } - end - end - - context 'when authenticated' do - it_behaves_like 'repository blob' do - let(:current_user) { user } - end - end - - it "returns 404 for invalid branch_name" do - get api("/projects/#{project.id}/repository/blobs/invalid_branch_name?filepath=README.md", user) - expect(response).to have_http_status(404) - end - - it "returns 404 for invalid file" do - get api("/projects/#{project.id}/repository/blobs/master?filepath=README.invalid", user) - expect(response).to have_http_status(404) - end - - it "returns a 400 error if filepath is missing" do - get api("/projects/#{project.id}/repository/blobs/master", user) - expect(response).to have_http_status(400) - end end describe "GET /projects/:id/repository/raw_blobs/:sha" do + let(:route) { "/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}" } + shared_examples_for 'repository raw blob' do it 'returns the repository raw blob' do - get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", current_user) + get api(route, current_user) expect(response).to have_http_status(200) end + + context 'when sha does not exist' do + it_behaves_like '404 response' do + let(:request) { get api(route.sub(sample_blob.oid, '123456'), current_user) } + let(:message) { '404 Blob Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get api(route, current_user) } + end + end end - context 'when unauthenticated' do + context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository raw blob' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route) } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do it_behaves_like 'repository raw blob' do let(:current_user) { user } end end - it 'returns a 404 for unknown blob' do - get api("/projects/#{project.id}/repository/raw_blobs/123456", user) - expect(response).to have_http_status(404) - - expect(json_response).to be_an Object - json_response['message'] == '404 Blob Not Found' + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest) } + end end end describe "GET /projects/:id/repository/archive(.:format)?:sha" do + let(:route) { "/projects/#{project.id}/repository/archive" } + shared_examples_for 'repository archive' do it 'returns the repository archive' do - get api("/projects/#{project.id}/repository/archive", current_user) + get api(route, current_user) expect(response).to have_http_status(200) @@ -208,31 +273,48 @@ describe API::Repositories, api: true do expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) end + + context 'when sha does not exist' do + it_behaves_like '404 response' do + let(:request) { get api("#{route}?sha=xxx", current_user) } + let(:message) { '404 File Not Found' } + end + end end - context 'when unauthenticated' do + context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository archive' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route) } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do it_behaves_like 'repository archive' do let(:current_user) { user } end end - it "returns 404 for invalid sha" do - get api("/projects/#{project.id}/repository/archive/?sha=xxx", user) - expect(response).to have_http_status(404) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest) } + end end end describe 'GET /projects/:id/repository/compare' do + let(:route) { "/projects/#{project.id}/repository/compare" } + shared_examples_for 'repository compare' do it "compares branches" do - get api("/projects/#{project.id}/repository/compare", current_user), from: 'master', to: 'feature' + get api(route, current_user), from: 'master', to: 'feature' expect(response).to have_http_status(200) expect(json_response['commits']).to be_present @@ -240,7 +322,7 @@ describe API::Repositories, api: true do end it "compares tags" do - get api("/projects/#{project.id}/repository/compare", current_user), from: 'v1.0.0', to: 'v1.1.0' + get api(route, current_user), from: 'v1.0.0', to: 'v1.1.0' expect(response).to have_http_status(200) expect(json_response['commits']).to be_present @@ -248,7 +330,7 @@ describe API::Repositories, api: true do end it "compares commits" do - get api("/projects/#{project.id}/repository/compare", current_user), from: sample_commit.id, to: sample_commit.parent_id + get api(route, current_user), from: sample_commit.id, to: sample_commit.parent_id expect(response).to have_http_status(200) expect(json_response['commits']).to be_empty @@ -257,7 +339,7 @@ describe API::Repositories, api: true do end it "compares commits in reverse order" do - get api("/projects/#{project.id}/repository/compare", current_user), from: sample_commit.parent_id, to: sample_commit.id + get api(route, current_user), from: sample_commit.parent_id, to: sample_commit.id expect(response).to have_http_status(200) expect(json_response['commits']).to be_present @@ -265,7 +347,7 @@ describe API::Repositories, api: true do end it "compares same refs" do - get api("/projects/#{project.id}/repository/compare", current_user), from: 'master', to: 'master' + get api(route, current_user), from: 'master', to: 'master' expect(response).to have_http_status(200) expect(json_response['commits']).to be_empty @@ -274,24 +356,39 @@ describe API::Repositories, api: true do end end - context 'when unauthenticated' do + context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository compare' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route) } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do it_behaves_like 'repository compare' do let(:current_user) { user } end end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest) } + end + end end describe 'GET /projects/:id/repository/contributors' do + let(:route) { "/projects/#{project.id}/repository/contributors" } + shared_examples_for 'repository contributors' do it 'returns valid data' do - get api("/projects/#{project.id}/repository/contributors", user) + get api(route, current_user) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -306,17 +403,30 @@ describe API::Repositories, api: true do end end - context 'when unauthenticated' do + context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository contributors' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route) } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do it_behaves_like 'repository contributors' do let(:current_user) { user } end end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest) } + end + end end end diff --git a/spec/support/api/status_shared_examples.rb b/spec/support/api/status_shared_examples.rb new file mode 100644 index 00000000000..3481749a7f0 --- /dev/null +++ b/spec/support/api/status_shared_examples.rb @@ -0,0 +1,42 @@ +# Specs for status checking. +# +# Requires an API request: +# let(:request) { get api("/projects/#{project.id}/repository/branches", user) } +shared_examples_for '400 response' do + before do + # Fires the request + request + end + + it 'returns 400' do + expect(response).to have_http_status(400) + end +end + +shared_examples_for '403 response' do + before do + # Fires the request + request + end + + it 'returns 403' do + expect(response).to have_http_status(403) + end +end + +shared_examples_for '404 response' do + let(:message) { nil } + before do + # Fires the request + request + end + + it 'returns 404' do + expect(response).to have_http_status(404) + expect(json_response).to be_an Object + + if message.present? + expect(json_response['message']).to eq(message) + end + end +end From 27c936adf9728cd21114aa3f2f9b44deb0296eb4 Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Fri, 16 Dec 2016 15:52:27 +0000 Subject: [PATCH 364/386] Milestoneish SQL performance partially improved and memoized Memoize Milestoneish#issues_visible_to_user and counts to reduce lookups Milstoneish SQL optimised with project, but still slow on GlobalMilestone --- app/models/concerns/milestoneish.rb | 28 +++++++++++++++++-- app/models/global_milestone.rb | 10 +++++-- app/models/milestone.rb | 4 +++ ...j-memoize-milestoneish-visible-to-user.yml | 4 +++ 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/jej-memoize-milestoneish-visible-to-user.yml diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index 4359f1d7b06..8f02c226f0b 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -1,10 +1,15 @@ module Milestoneish def closed_items_count(user) - issues_visible_to_user(user).closed.size + merge_requests.closed_and_merged.size + memoize_per_user(user, :closed_items_count) do + (count_issues_by_state(user)['closed'] || 0) + merge_requests.closed_and_merged.size + end end def total_items_count(user) - issues_visible_to_user(user).size + merge_requests.size + memoize_per_user(user, :total_items_count) do + issues_count = count_issues_by_state(user).values.sum + issues_count + merge_requests.size + end end def complete?(user) @@ -30,7 +35,10 @@ module Milestoneish end def issues_visible_to_user(user) - IssuesFinder.new(user).execute.where(id: issues) + memoize_per_user(user, :issues_visible_to_user) do + params = try(:project_id) ? { project_id: project_id } : {} + IssuesFinder.new(user, params).execute.where(milestone_id: milestoneish_ids) + end end def upcoming? @@ -50,4 +58,18 @@ module Milestoneish def expired? due_date && due_date.past? end + + private + + def count_issues_by_state(user) + memoize_per_user(user, :count_issues_by_state) do + issues_visible_to_user(user).reorder(nil).group(:state).count + end + end + + def memoize_per_user(user, method_name) + @memoized ||= {} + @memoized[method_name] ||= {} + @memoized[method_name][user.try!(:id)] ||= yield + end end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index b01607dcda9..a54e478f5f8 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -24,12 +24,16 @@ class GlobalMilestone @first_milestone = milestones.find {|m| m.description.present? } || milestones.first end + def milestoneish_ids + milestones.select(:id) + end + def safe_title @title.to_slug.normalize.to_s end def projects - @projects ||= Project.for_milestones(milestones.select(:id)) + @projects ||= Project.for_milestones(milestoneish_ids) end def state @@ -49,11 +53,11 @@ class GlobalMilestone end def issues - @issues ||= Issue.of_milestones(milestones.select(:id)).includes(:project, :assignee, :labels) + @issues ||= Issue.of_milestones(milestoneish_ids).includes(:project, :assignee, :labels) end def merge_requests - @merge_requests ||= MergeRequest.of_milestones(milestones.select(:id)).includes(:target_project, :assignee, :labels) + @merge_requests ||= MergeRequest.of_milestones(milestoneish_ids).includes(:target_project, :assignee, :labels) end def participants diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 45ca97adad1..0dcfec89f14 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -129,6 +129,10 @@ class Milestone < ActiveRecord::Base self.title end + def milestoneish_ids + id + end + def can_be_closed? active? && issues.opened.count.zero? end diff --git a/changelogs/unreleased/jej-memoize-milestoneish-visible-to-user.yml b/changelogs/unreleased/jej-memoize-milestoneish-visible-to-user.yml new file mode 100644 index 00000000000..ab7f39a4178 --- /dev/null +++ b/changelogs/unreleased/jej-memoize-milestoneish-visible-to-user.yml @@ -0,0 +1,4 @@ +--- +title: Milestoneish SQL performance partially improved and memoized +merge_request: 8146 +author: From a2f57f23616bc1ab7547e5a7c517cdd49b172bd8 Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Mon, 19 Dec 2016 21:26:15 +0000 Subject: [PATCH 365/386] Fix N+1 queries on milestone show pages --- app/models/issue.rb | 2 ++ app/views/shared/milestones/_tabs.html.haml | 2 +- changelogs/unreleased/jej-fix-n-1-queries-milestones-show.yml | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/jej-fix-n-1-queries-milestones-show.yml diff --git a/app/models/issue.rb b/app/models/issue.rb index 738c96e4db3..6825553512f 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -39,6 +39,8 @@ class Issue < ActiveRecord::Base scope :created_after, -> (datetime) { where("created_at >= ?", datetime) } + scope :include_associations, -> { includes(:assignee, :labels, project: :namespace) } + attr_spammable :title, spam_title: true attr_spammable :description, spam_description: true diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index 2b6ce2d7e7a..c8f2319d95a 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -21,7 +21,7 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name + = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user).include_associations, show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-merge-requests = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-participants diff --git a/changelogs/unreleased/jej-fix-n-1-queries-milestones-show.yml b/changelogs/unreleased/jej-fix-n-1-queries-milestones-show.yml new file mode 100644 index 00000000000..ad6eba3faf2 --- /dev/null +++ b/changelogs/unreleased/jej-fix-n-1-queries-milestones-show.yml @@ -0,0 +1,4 @@ +--- +title: Fix N+1 queries on milestone show pages +merge_request: 8185 +author: From 4b9bd188433d77fbaec8ae445716de8084b6a145 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 20 Dec 2016 13:22:31 +0000 Subject: [PATCH 366/386] Adds tests for the mini pipeline graph --- app/assets/stylesheets/pages/pipelines.scss | 2 +- .../projects/ci/pipelines/_pipeline.html.haml | 4 +- .../projects/pipelines/pipelines_spec.rb | 50 +++++++++++++++++-- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 3639db026cf..fb6ddc34f1f 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -748,7 +748,7 @@ .grouped-pipeline-dropdown { right: -172px; top: 23px; - min-height: 191px; + min-height: 50px; a { color: $gl-text-color-light; diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index ce8b9f9e8af..8541b351e2b 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -83,7 +83,7 @@ .btn-group.inline - if actions.any? .btn-group - %a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'} + %a.dropdown-toggle.btn.btn-default.js-pipeline-dropdown-manual-actions{type: 'button', 'data-toggle' => 'dropdown'} = custom_icon('icon_play') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right @@ -94,7 +94,7 @@ %span= build.name.humanize - if artifacts.present? .btn-group - %a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'} + %a.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{type: 'button', 'data-toggle' => 'dropdown'} = icon("download") = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 24e501b0151..d1ebb12715f 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' -describe "Pipelines" do +describe "Pipelines", feature: true, js:true do include GitlabRoutingHelper + include WaitForAjax let(:project) { create(:empty_project) } let(:user) { create(:user) } @@ -76,7 +77,11 @@ describe "Pipelines" do it { expect(page).to have_link('Manual build') } context 'when playing' do - before { click_link('Manual build') } + + before do + find('.js-pipeline-dropdown-manual-actions').click + click_link('Manual build') + end it { expect(manual.reload).to be_pending } end @@ -131,7 +136,10 @@ describe "Pipelines" do before { visit namespace_project_pipelines_path(project.namespace, project) } it { expect(page).to have_selector('.build-artifacts') } - it { expect(page).to have_link(with_artifacts.name) } + it do + find('.js-pipeline-dropdown-download').click + expect(page).to have_link(with_artifacts.name) + end end context 'with artifacts expired' do @@ -150,6 +158,42 @@ describe "Pipelines" do it { expect(page).not_to have_selector('.build-artifacts') } end end + + context 'mini pipleine graph' do + let!(:build) do + create(:ci_build, pipeline: pipeline, stage: 'build', name: 'build') + end + + before do + visit namespace_project_pipelines_path(project.namespace, project) + end + + it 'should render a mini pipeline graph' do + endpoint = stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: build.name) + + expect(page).to have_selector('.mini-pipeline-graph') + expect(page).to have_selector(".js-builds-dropdown-button[data-stage-endpoint='#{endpoint}']") + end + + context 'when clicking a graph stage' do + it 'should open a dropdown' do + find('.js-builds-dropdown-button').trigger('click') + + wait_for_ajax + + expect(page).to have_link build.name + end + + it 'should be possible to retry the failed build' do + find('.js-builds-dropdown-button').trigger('click') + + wait_for_ajax + + find('a.ci-action-icon-container').trigger('click') + expect(page).not_to have_content('Cancel running') + end + end + end end describe 'GET /:project/pipelines/stage.json?name=stage' do From 3ef1e76659d0d583a2fd72d86309c2ba6816a316 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 20 Dec 2016 13:25:58 +0000 Subject: [PATCH 367/386] Fix broken test Changes after review --- .../mini_pipeline_graph_dropdown.js.es6 | 21 ++++++++++++------- .../projects/ci/pipelines/_pipeline.html.haml | 10 ++++----- .../projects/pipelines/pipelines_spec.rb | 5 ++++- spec/models/ci/stage_spec.rb | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 index 31b65a22723..90b3366f14b 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 @@ -1,3 +1,4 @@ +/* eslint-disable no-new */ /* global Flash */ /** @@ -30,8 +31,8 @@ bindEvents() { const dropdownButtonSelector = 'button.js-builds-dropdown-button'; - $(this.container).off('click', dropdownButtonSelector, this.getBuildsList); - $(this.container).on('click', dropdownButtonSelector, this.getBuildsList); + $(this.container).off('click', dropdownButtonSelector, this.getBuildsList) + .on('click', dropdownButtonSelector, this.getBuildsList); } /** @@ -55,21 +56,25 @@ * @return {Promise} */ getBuildsList(e) { - const endpoint = e.currentTarget.dataset.stageEndpoint; + const button = e.currentTarget; + const endpoint = button.dataset.stageEndpoint; return $.ajax({ dataType: 'json', type: 'GET', url: endpoint, beforeSend: () => { - this.renderBuildsList(e.currentTarget, ''); - this.toggleLoading(e.currentTarget); + this.renderBuildsList(button, ''); + this.toggleLoading(button); }, success: (data) => { - this.toggleLoading(e.currentTarget); - this.renderBuildsList(e.currentTarget, data.html); + this.toggleLoading(button); + this.renderBuildsList(button, data.html); + }, + error: () => { + this.toggleLoading(button); + new Flash('An error occurred while fetching the builds.', 'alert'); }, - error: () => new Flash('An error occurred while fetching the builds.', 'alert'), }); } diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 8541b351e2b..2f8f153f9a9 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -44,17 +44,15 @@ - pipeline.stages.each do |stage| - if stage.status - detailed_status = stage.detailed_status(current_user) - - klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status.group}" - icon_status = "#{detailed_status.icon}_borderless" - - icon_status_klass = "ci-status-icon ci-status-icon-#{detailed_status.group}" - - tooltip = "#{stage.name}: #{detailed_status.label || 'not found'}" + - status_klass = "ci-status-icon ci-status-icon-#{detailed_status.group}" .stage-container.mini-pipeline-graph .dropdown.inline.build-content - %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} - %span{ class: klass } + %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} + %span.has-tooltip{ class: status_klass } %span.mini-pipeline-graph-icon-container - %span{ class: icon_status_klass }= custom_icon(icon_status) + %span{ class: status_klass }= custom_icon(icon_status) = icon('caret-down', class: 'dropdown-caret') .js-builds-dropdown-container diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index d1ebb12715f..5a4d14d2f95 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -74,7 +74,10 @@ describe "Pipelines", feature: true, js:true do before { visit namespace_project_pipelines_path(project.namespace, project) } - it { expect(page).to have_link('Manual build') } + it do + find('.js-pipeline-dropdown-manual-actions').click + expect(page).to have_link('Manual build') + end context 'when playing' do diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index 65a302e9d9b..786091a577d 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -36,7 +36,7 @@ describe Ci::Stage, models: true do subject { stage.statuses_count } - it "statuses only from current stage" do + it "counts statuses only from current stage" do is_expected.to eq(1) end end From b2daf9f16892f362f57a4a0f7990dbc2f6ab8429 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 20 Dec 2016 14:17:21 +0000 Subject: [PATCH 368/386] Fix broken test --- spec/features/projects/pipelines/pipelines_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 5a4d14d2f95..57abbf5d7a4 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -217,7 +217,7 @@ describe "Pipelines", feature: true, js:true do it do expect(page).to have_http_status(:ok) - expect(JSON.parse(page.source)).to include("html") + expect(page.source).to include("html") end end From 8ad5d3171ac1c9079a8a828a1ee9eb16ec21fd38 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 20 Dec 2016 08:25:39 -0600 Subject: [PATCH 369/386] Fix sizing of avatar circles; add border --- app/assets/stylesheets/framework/avatar.scss | 2 +- app/assets/stylesheets/pages/projects.scss | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 000e591e09c..48827578d94 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -64,7 +64,7 @@ &.s32 { font-size: 20px; line-height: 30px; } &.s40 { font-size: 16px; line-height: 38px; } &.s60 { font-size: 32px; line-height: 58px; } - &.s70 { font-size: 34px; line-height: 68px; } + &.s70 { font-size: 34px; line-height: 70px; } &.s90 { font-size: 36px; line-height: 88px; } &.s110 { font-size: 40px; line-height: 108px; font-weight: 300; } &.s140 { font-size: 72px; line-height: 138px; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index a443b6a37b3..e16a553bcda 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -93,7 +93,6 @@ .group-avatar { float: none; margin: 0 auto; - border: none; &.identicon { border-radius: 50%; From 9c6480db8993ee6f1d8d1fac29e044dd00d66465 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 20 Dec 2016 15:53:53 +0100 Subject: [PATCH 370/386] Move test for HTML stage endpoint to controller specs --- .../projects/pipelines_controller_spec.rb | 46 +++++++++++++++++++ .../projects/pipelines/pipeline_spec.rb | 2 +- .../projects/pipelines/pipelines_spec.rb | 32 ------------- 3 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 spec/controllers/projects/pipelines_controller_spec.rb diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb new file mode 100644 index 00000000000..94113250c9f --- /dev/null +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe Projects::PipelinesController do + include ApiHelpers + + let(:user) { create(:user) } + let(:project) { create(:empty_project, :public) } + let(:pipeline) { create(:ci_pipeline, project: project) } + + before do + sign_in(user) + end + + describe 'GET stages.json' do + def get_stage(name) + get :stage, namespace_id: project.namespace.path, + project_id: project.path, + id: pipeline.id, + stage: name, + format: :json + end + + context 'when accessing existing stage' do + before do + create(:ci_build, pipeline: pipeline, stage: 'build') + + get_stage('build') + end + + it 'returns html source for stage dropdown' do + expect(response).to have_http_status(:ok) + expect(response).to render_template('projects/pipelines/_stage') + expect(json_response).to include('html') + end + end + + context 'when accessing unknown stage' do + before do + get_stage('test') + end + + it { expect(response).to have_http_status(:not_found) } + end + + end +end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 1210e2745db..14e009daba8 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Pipelines", feature: true, js: true do +describe 'Pipeline', :feature, :js do include GitlabRoutingHelper let(:project) { create(:empty_project) } diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 57abbf5d7a4..fa8ba21b389 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -199,38 +199,6 @@ describe "Pipelines", feature: true, js:true do end end - describe 'GET /:project/pipelines/stage.json?name=stage' do - let!(:pipeline) do - create(:ci_empty_pipeline, project: project, ref: 'master', - status: 'running') - end - - context 'when accessing existing stage' do - let!(:build) do - create(:ci_build, pipeline: pipeline, stage: 'build') - end - - before do - visit stage_namespace_project_pipeline_path( - project.namespace, project, pipeline, format: :json, stage: 'build') - end - - it do - expect(page).to have_http_status(:ok) - expect(page.source).to include("html") - end - end - - context 'when accessing unknown stage' do - before do - visit stage_namespace_project_pipeline_path( - project.namespace, project, pipeline, format: :json, stage: 'test') - end - - it { expect(page).to have_http_status(:not_found) } - end - end - describe 'POST /:project/pipelines' do let(:project) { create(:project) } From ec4b1bc7556848c6683546559290a6576301c05d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 20 Dec 2016 16:06:01 +0100 Subject: [PATCH 371/386] Add isolated view spec for pipeline stage partial --- .../pipelines/_stage.html.haml_spec.rb | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 spec/views/projects/pipelines/_stage.html.haml_spec.rb diff --git a/spec/views/projects/pipelines/_stage.html.haml_spec.rb b/spec/views/projects/pipelines/_stage.html.haml_spec.rb new file mode 100644 index 00000000000..eb7f7ca4a1a --- /dev/null +++ b/spec/views/projects/pipelines/_stage.html.haml_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe 'projects/pipelines/_stage', :view do + let(:project) { create(:project) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:stage) { build(:ci_stage, pipeline: pipeline) } + + before do + assign :stage, stage + + create(:ci_build, name: 'test:build', + stage: stage.name, + pipeline: pipeline) + end + + it 'shows the builds in the stage' do + render + + expect(rendered).to have_text 'test:build' + end +end From 5652da8bb4ea26acd35a241683e242439fefdd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 18:58:24 +0100 Subject: [PATCH 372/386] Allow unauthenticated access to Repositories Files API GET endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- changelogs/unreleased/4269-public-api.yml | 2 +- .../unreleased/4269-public-files-api.yml | 4 ++ .../4269-public-repositories-api.yml | 2 +- doc/api/repository_files.md | 4 +- lib/api/files.rb | 2 - spec/requests/api/files_spec.rb | 37 +++++++++++++------ 6 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 changelogs/unreleased/4269-public-files-api.yml diff --git a/changelogs/unreleased/4269-public-api.yml b/changelogs/unreleased/4269-public-api.yml index 560bc6a4f13..9de739d0cad 100644 --- a/changelogs/unreleased/4269-public-api.yml +++ b/changelogs/unreleased/4269-public-api.yml @@ -1,4 +1,4 @@ --- -title: Allow public access to some Project API endpoints +title: Allow unauthenticated access to some Project API GET endpoints merge_request: 7843 author: diff --git a/changelogs/unreleased/4269-public-files-api.yml b/changelogs/unreleased/4269-public-files-api.yml new file mode 100644 index 00000000000..e8f9e9b5ed3 --- /dev/null +++ b/changelogs/unreleased/4269-public-files-api.yml @@ -0,0 +1,4 @@ +--- +title: Allow unauthenticated access to Repositories Files API GET endpoints +merge_request: +author: diff --git a/changelogs/unreleased/4269-public-repositories-api.yml b/changelogs/unreleased/4269-public-repositories-api.yml index 861307a022b..38984eed904 100644 --- a/changelogs/unreleased/4269-public-repositories-api.yml +++ b/changelogs/unreleased/4269-public-repositories-api.yml @@ -1,4 +1,4 @@ --- -title: Allow Repositories API GET endpoints to be requested anonymously +title: Allow unauthenticated access to Repositories API GET endpoints merge_request: 8148 author: diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md index b8c9eb2c9a8..8a6baed5987 100644 --- a/doc/api/repository_files.md +++ b/doc/api/repository_files.md @@ -6,7 +6,9 @@ ## Get file from repository -Allows you to receive information about file in repository like name, size, content. Note that file content is Base64 encoded. +Allows you to receive information about file in repository like name, size, +content. Note that file content is Base64 encoded. This endpoint can be accessed +without authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/files diff --git a/lib/api/files.rb b/lib/api/files.rb index 28f306e45f3..532a317c89e 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -1,8 +1,6 @@ module API # Projects API class Files < Grape::API - before { authenticate! } - helpers do def commit_params(attrs) { diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 2081f80ccc1..4dd312e2852 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -24,19 +24,34 @@ describe API::Files, api: true do before { project.team << [user, :developer] } describe "GET /projects/:id/repository/files" do - it "returns file info" do - params = { - file_path: file_path, - ref: 'master', - } + shared_examples_for 'repository files' do + it "returns file info" do + params = { + file_path: file_path, + ref: 'master', + } - get api("/projects/#{project.id}/repository/files", user), params + get api("/projects/#{project.id}/repository/files", current_user), params - expect(response).to have_http_status(200) - expect(json_response['file_path']).to eq(file_path) - expect(json_response['file_name']).to eq('popen.rb') - expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') - expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq('popen.rb') + expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository files' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository files' do + let(:current_user) { user } + end end it "returns a 400 bad request if no params given" do From 0349e83aa74b42c3f564fd1bc34104300a41ddf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 20 Dec 2016 15:05:57 +0100 Subject: [PATCH 373/386] Improve specs for Files API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/requests/api/files_spec.rb | 74 +++++++++++++------ spec/requests/api/repositories_spec.rb | 11 --- .../api/repositories_shared_context.rb | 10 +++ 3 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 spec/support/api/repositories_shared_context.rb diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 4dd312e2852..685da28c673 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -4,7 +4,14 @@ describe API::Files, api: true do include ApiHelpers let(:user) { create(:user) } let!(:project) { create(:project, namespace: user.namespace ) } + let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } } let(:file_path) { 'files/ruby/popen.rb' } + let(:params) do + { + file_path: file_path, + ref: 'master' + } + end let(:author_email) { FFaker::Internet.email } # I have to remove periods from the end of the name @@ -24,14 +31,11 @@ describe API::Files, api: true do before { project.team << [user, :developer] } describe "GET /projects/:id/repository/files" do + let(:route) { "/projects/#{project.id}/repository/files" } + shared_examples_for 'repository files' do it "returns file info" do - params = { - file_path: file_path, - ref: 'master', - } - - get api("/projects/#{project.id}/repository/files", current_user), params + get api(route, current_user), params expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) @@ -39,36 +43,60 @@ describe API::Files, api: true do expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end + + context 'when no params are given' do + it_behaves_like '400 response' do + let(:request) { get api(route, current_user) } + end + end + + context 'when file_path does not exist' do + let(:params) do + { + file_path: 'app/models/application.rb', + ref: 'master', + } + end + + it_behaves_like '404 response' do + let(:request) { get api(route, current_user), params } + let(:message) { '404 File Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get api(route, current_user), params } + end + end end - context 'when unauthenticated' do + context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository files' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route), params } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do it_behaves_like 'repository files' do let(:current_user) { user } end end - it "returns a 400 bad request if no params given" do - get api("/projects/#{project.id}/repository/files", user) - - expect(response).to have_http_status(400) - end - - it "returns a 404 if such file does not exist" do - params = { - file_path: 'app/models/application.rb', - ref: 'master', - } - - get api("/projects/#{project.id}/repository/files", user), params - - expect(response).to have_http_status(404) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest), params } + end end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index fe28ad1d1a1..0b19fa38c55 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -11,17 +11,6 @@ describe API::Repositories, api: true do let!(:project) { create(:project, creator_id: user.id) } let!(:master) { create(:project_member, :master, user: user, project: project) } - shared_context 'disabled repository' do - before do - project.project_feature.update_attributes!( - repository_access_level: ProjectFeature::DISABLED, - merge_requests_access_level: ProjectFeature::DISABLED, - builds_access_level: ProjectFeature::DISABLED - ) - expect(project.feature_available?(:repository, current_user)).to be false - end - end - describe "GET /projects/:id/repository/tree" do let(:route) { "/projects/#{project.id}/repository/tree" } diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb new file mode 100644 index 00000000000..ea38fe4f5b8 --- /dev/null +++ b/spec/support/api/repositories_shared_context.rb @@ -0,0 +1,10 @@ +shared_context 'disabled repository' do + before do + project.project_feature.update_attributes!( + repository_access_level: ProjectFeature::DISABLED, + merge_requests_access_level: ProjectFeature::DISABLED, + builds_access_level: ProjectFeature::DISABLED + ) + expect(project.feature_available?(:repository, current_user)).to be false + end +end From 33564b113386b3b9c1606dcc2ff7251c178667e5 Mon Sep 17 00:00:00 2001 From: jurre Date: Tue, 20 Dec 2016 15:23:20 +0100 Subject: [PATCH 374/386] Make 'unmarked as WIP' message more consistent --- app/services/system_note_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 8b48d90f60b..7613ecd5021 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -146,7 +146,7 @@ module SystemNoteService end def remove_merge_request_wip(noteable, project, author) - body = 'unmarked as a Work In Progress' + body = 'unmarked as a **Work In Progress**' create_note(noteable: noteable, project: project, author: author, note: body) end From ca979f2a14fbddf538cd933acefff05d2b049633 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 20 Dec 2016 16:50:39 +0100 Subject: [PATCH 375/386] Use gitlab-workhorse 1.2.1 --- GITLAB_WORKHORSE_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 26aaba0e866..6085e946503 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -1.2.0 +1.2.1 From 9c623e3e5d7434f2e30f7c389d13e5af4ede770a Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Tue, 20 Dec 2016 14:48:04 +0000 Subject: [PATCH 376/386] Added QueryRecorder to test N+1 fix on Milestone#show --- spec/features/milestones/show_spec.rb | 26 +++++++++++++++++ spec/support/query_recorder.rb | 40 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 spec/features/milestones/show_spec.rb create mode 100644 spec/support/query_recorder.rb diff --git a/spec/features/milestones/show_spec.rb b/spec/features/milestones/show_spec.rb new file mode 100644 index 00000000000..40b4dc63697 --- /dev/null +++ b/spec/features/milestones/show_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +describe 'Milestone show', feature: true do + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:milestone) { create(:milestone, project: project) } + let(:labels) { create_list(:label, 2, project: project) } + let(:issue_params) { { project: project, assignee: user, author: user, milestone: milestone, labels: labels } } + + before do + project.add_user(user, :developer) + login_as(user) + end + + def visit_milestone + visit namespace_project_milestone_path(project.namespace, project, milestone) + end + + it 'avoids N+1 database queries' do + create(:labeled_issue, issue_params) + control_count = ActiveRecord::QueryRecorder.new { visit_milestone }.count + create_list(:labeled_issue, 10, issue_params) + + expect { visit_milestone }.not_to exceed_query_limit(control_count) + end +end diff --git a/spec/support/query_recorder.rb b/spec/support/query_recorder.rb new file mode 100644 index 00000000000..e40d5ebd9a8 --- /dev/null +++ b/spec/support/query_recorder.rb @@ -0,0 +1,40 @@ +module ActiveRecord + class QueryRecorder + attr_reader :log + + def initialize(&block) + @log = [] + ActiveSupport::Notifications.subscribed(method(:callback), 'sql.active_record', &block) + end + + def callback(name, start, finish, message_id, values) + return if %w(CACHE SCHEMA).include?(values[:name]) + @log << values[:sql] + end + + def count + @log.count + end + + def log_message + @log.join("\n\n") + end + end +end + +RSpec::Matchers.define :exceed_query_limit do |expected| + supports_block_expectations + + match do |block| + query_count(&block) > expected + end + + failure_message_when_negated do |actual| + "Expected a maximum of #{expected} queries, got #{@recorder.count}:\n\n#{@recorder.log_message}" + end + + def query_count(&block) + @recorder = ActiveRecord::QueryRecorder.new(&block) + @recorder.count + end +end From 2b486c2bb27087e4eb306821b9fca95ff8ac74d3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 20 Dec 2016 20:07:34 +0100 Subject: [PATCH 377/386] Fix stage and pipeline specs and rubocop offenses --- .../projects/pipelines_controller_spec.rb | 19 +++++++------- .../projects/pipelines/pipelines_spec.rb | 25 +++++++++++++------ spec/models/ci/pipeline_spec.rb | 6 ++--- spec/models/ci/stage_spec.rb | 2 +- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 94113250c9f..5fe7e6407cc 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -12,14 +12,6 @@ describe Projects::PipelinesController do end describe 'GET stages.json' do - def get_stage(name) - get :stage, namespace_id: project.namespace.path, - project_id: project.path, - id: pipeline.id, - stage: name, - format: :json - end - context 'when accessing existing stage' do before do create(:ci_build, pipeline: pipeline, stage: 'build') @@ -39,8 +31,17 @@ describe Projects::PipelinesController do get_stage('test') end - it { expect(response).to have_http_status(:not_found) } + it 'responds with not found' do + expect(response).to have_http_status(:not_found) + end end + def get_stage(name) + get :stage, namespace_id: project.namespace.path, + project_id: project.path, + id: pipeline.id, + stage: name, + format: :json + end end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index fa8ba21b389..1ff57f92c4c 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Pipelines", feature: true, js:true do +describe 'Pipelines', :feature, :js do include GitlabRoutingHelper include WaitForAjax @@ -70,23 +70,32 @@ describe "Pipelines", feature: true, js:true do end context 'with manual actions' do - let!(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'manual build', stage: 'test', commands: 'test') } + let!(:manual) do + create(:ci_build, :manual, pipeline: pipeline, + name: 'manual build', + stage: 'test', + commands: 'test') + end - before { visit namespace_project_pipelines_path(project.namespace, project) } + before do + visit namespace_project_pipelines_path(project.namespace, project) + end - it do + it 'has link to the manual action' do find('.js-pipeline-dropdown-manual-actions').click - expect(page).to have_link('Manual build') - end - context 'when playing' do + expect(page).to have_link('Manual build') + end + context 'when manual action was played' do before do find('.js-pipeline-dropdown-manual-actions').click click_link('Manual build') end - it { expect(manual.reload).to be_pending } + it 'enqueues manual action job' do + expect(manual.reload).to be_pending + end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 5e1a9fa8dd8..dc377d15f15 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -183,9 +183,9 @@ describe Ci::Pipeline, models: true do create(:commit_status, pipeline: pipeline, stage: 'test') end - it { expect(subject).to be_a(Ci::Stage) } - it { expect(subject.name).to eq('stage') } - it { expect(subject.statues).not_to be_empty } + it { expect(subject).to be_a Ci::Stage } + it { expect(subject.name).to eq 'test' } + it { expect(subject.statuses).not_to be_empty } end context 'without status in stage' do diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index 786091a577d..742bedb37e4 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -30,7 +30,7 @@ describe Ci::Stage, models: true do describe '#statuses_count' do before do - create_job(:ci_build) } + create_job(:ci_build) create_job(:ci_build, stage: 'other stage') end From fed29117de6f30055d88daaa497ec18f85397ad6 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 20 Dec 2016 19:14:33 -0200 Subject: [PATCH 378/386] Rename SlackNotificationService back to SlackService --- app/models/project.rb | 2 +- ...tification_service.rb => slack_service.rb} | 4 ++-- app/models/service.rb | 2 +- ...006143943_move_slack_service_to_webhook.rb | 2 +- ...k_service_to_slack_notification_service.rb | 13 +++++-------- lib/api/services.rb | 2 +- .../import_export/test_project_export.tar.gz | Bin 679415 -> 682154 bytes .../projects/services/slack_service_spec.rb | 4 ++-- spec/lib/gitlab/import_export/all_models.yml | 2 +- ..._service_spec.rb => slack_service_spec.rb} | 2 +- spec/models/project_spec.rb | 2 +- 11 files changed, 16 insertions(+), 19 deletions(-) rename app/models/project_services/{slack_notification_service.rb => slack_service.rb} (92%) rename spec/models/project_services/{slack_notification_service_spec.rb => slack_service_spec.rb} (61%) diff --git a/app/models/project.rb b/app/models/project.rb index 72d3da64f2d..5807ea5acdc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -96,7 +96,7 @@ class Project < ActiveRecord::Base has_one :mattermost_slash_commands_service, dependent: :destroy has_one :mattermost_notification_service, dependent: :destroy has_one :slack_slash_commands_service, dependent: :destroy - has_one :slack_notification_service, dependent: :destroy + has_one :slack_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy has_one :teamcity_service, dependent: :destroy diff --git a/app/models/project_services/slack_notification_service.rb b/app/models/project_services/slack_service.rb similarity index 92% rename from app/models/project_services/slack_notification_service.rb rename to app/models/project_services/slack_service.rb index 3cbf89efba4..0583470d3b5 100644 --- a/app/models/project_services/slack_notification_service.rb +++ b/app/models/project_services/slack_service.rb @@ -1,4 +1,4 @@ -class SlackNotificationService < ChatNotificationService +class SlackService < ChatNotificationService def title 'Slack notifications' end @@ -8,7 +8,7 @@ class SlackNotificationService < ChatNotificationService end def to_param - 'slack_notification' + 'slack' end def help diff --git a/app/models/service.rb b/app/models/service.rb index 8abd8e73e43..918ed8206e0 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -222,7 +222,7 @@ class Service < ActiveRecord::Base pushover redmine slack_slash_commands - slack_notification + slack teamcity ] end diff --git a/db/migrate/20141006143943_move_slack_service_to_webhook.rb b/db/migrate/20141006143943_move_slack_service_to_webhook.rb index 42e88d6d6e3..561184615cc 100644 --- a/db/migrate/20141006143943_move_slack_service_to_webhook.rb +++ b/db/migrate/20141006143943_move_slack_service_to_webhook.rb @@ -5,7 +5,7 @@ class MoveSlackServiceToWebhook < ActiveRecord::Migration DOWNTIME_REASON = 'Move old fields "token" and "subdomain" to one single field "webhook"' def change - SlackNotificationService.all.each do |slack_service| + SlackService.all.each do |slack_service| if ["token", "subdomain"].all? { |property| slack_service.properties.key? property } token = slack_service.properties['token'] subdomain = slack_service.properties['subdomain'] diff --git a/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb b/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb index a7278d7b5a6..dc38d0ac906 100644 --- a/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb +++ b/db/migrate/20161213172958_change_slack_service_to_slack_notification_service.rb @@ -1,14 +1,11 @@ class ChangeSlackServiceToSlackNotificationService < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers - DOWNTIME = true - DOWNTIME_REASON = 'Rename SlackService to SlackNotificationService' + DOWNTIME = false - def up - execute("UPDATE services SET type = 'SlackNotificationService' WHERE type = 'SlackService'") - end - - def down - execute("UPDATE services SET type = 'SlackService' WHERE type = 'SlackNotificationService'") + # This migration is a no-op, as it existed in an RC but we renamed + # SlackNotificationService back to SlackService: + # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8191#note_20310845 + def change end end diff --git a/lib/api/services.rb b/lib/api/services.rb index aa97f6af0b2..44e668e3cf5 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -480,7 +480,7 @@ module API desc: 'The description of the tracker' } ], - 'slack-notification' => [ + 'slack' => [ { required: true, name: :webhook, diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz index d3165d07d7b9a37d532a6bf97c7d74de7d32098b..7655c2b351f9e81d820d29814af1e2554eed1797 100644 GIT binary patch delta 262492 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

    Z^0-k2=s{SYC!LNTgcd;X}fTZR|9O@4Iz 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

    BOJ_MoJntxLW|kW^sMP`617E_}CbwaC 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 YpA&!n`RAX1{vY|@02W<}3;?JM0Otixg#Z8m delta 420253 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%0AyaQ%ElTR!%T|L?H!)2oiym};m&gf$c88Rk9 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

    KgJCka=4y0pL4C2kOYwh1!yq=T+yM9N%f-} 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⋙98N6s5=X(|URr?OL-{w2IMymbSvlUiO)6?E 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í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 diff --git a/spec/features/projects/services/slack_service_spec.rb b/spec/features/projects/services/slack_service_spec.rb index 320ed13a01d..16541f51d98 100644 --- a/spec/features/projects/services/slack_service_spec.rb +++ b/spec/features/projects/services/slack_service_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' feature 'Projects > Slack service > Setup events', feature: true do let(:user) { create(:user) } - let(:service) { SlackNotificationService.new } - let(:project) { create(:project, slack_notification_service: service) } + let(:service) { SlackService.new } + let(:project) { create(:project, slack_service: service) } background do service.fields diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 7e618e2fcf5..155cf8565b5 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -137,7 +137,7 @@ project: - assembla_service - asana_service - gemnasium_service -- slack_notification_service +- slack_service - mattermost_notification_service - buildkite_service - bamboo_service diff --git a/spec/models/project_services/slack_notification_service_spec.rb b/spec/models/project_services/slack_service_spec.rb similarity index 61% rename from spec/models/project_services/slack_notification_service_spec.rb rename to spec/models/project_services/slack_service_spec.rb index 110b5bf2115..9a3ecc66d83 100644 --- a/spec/models/project_services/slack_notification_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe SlackNotificationService, models: true do +describe SlackService, models: true do it_behaves_like "slack or mattermost notifications" end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 0455cd2fe49..569071c0418 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -21,7 +21,7 @@ describe Project, models: true do it { is_expected.to have_many(:hooks).dependent(:destroy) } it { is_expected.to have_many(:protected_branches).dependent(:destroy) } it { is_expected.to have_one(:forked_project_link).dependent(:destroy) } - it { is_expected.to have_one(:slack_notification_service).dependent(:destroy) } + it { is_expected.to have_one(:slack_service).dependent(:destroy) } it { is_expected.to have_one(:mattermost_notification_service).dependent(:destroy) } it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } From d1bf557aacb5dd789ccc88786b47ec174ed1de2b Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 20 Dec 2016 19:29:39 -0200 Subject: [PATCH 379/386] Rename MattermostNotificationService back to MattermostService --- app/models/project.rb | 2 +- ...ttermost_notification_service.rb => mattermost_service.rb} | 4 ++-- app/models/service.rb | 2 +- lib/api/services.rb | 2 +- spec/lib/gitlab/import_export/all_models.yml | 2 +- ...otification_service_spec.rb => mattermost_service_spec.rb} | 2 +- spec/models/project_spec.rb | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) rename app/models/project_services/{mattermost_notification_service.rb => mattermost_service.rb} (93%) rename spec/models/project_services/{mattermost_notification_service_spec.rb => mattermost_service_spec.rb} (59%) diff --git a/app/models/project.rb b/app/models/project.rb index 5807ea5acdc..26fa20f856d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -94,7 +94,7 @@ class Project < ActiveRecord::Base has_one :asana_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy has_one :mattermost_slash_commands_service, dependent: :destroy - has_one :mattermost_notification_service, dependent: :destroy + has_one :mattermost_service, dependent: :destroy has_one :slack_slash_commands_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy diff --git a/app/models/project_services/mattermost_notification_service.rb b/app/models/project_services/mattermost_service.rb similarity index 93% rename from app/models/project_services/mattermost_notification_service.rb rename to app/models/project_services/mattermost_service.rb index de18c4b1f00..0650f930402 100644 --- a/app/models/project_services/mattermost_notification_service.rb +++ b/app/models/project_services/mattermost_service.rb @@ -1,4 +1,4 @@ -class MattermostNotificationService < ChatNotificationService +class MattermostService < ChatNotificationService def title 'Mattermost notifications' end @@ -8,7 +8,7 @@ class MattermostNotificationService < ChatNotificationService end def to_param - 'mattermost_notification' + 'mattermost' end def help diff --git a/app/models/service.rb b/app/models/service.rb index 918ed8206e0..19ef3ba9c23 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -216,7 +216,7 @@ class Service < ActiveRecord::Base jira kubernetes mattermost_slash_commands - mattermost_notification + mattermost pipelines_email pivotaltracker pushover diff --git a/lib/api/services.rb b/lib/api/services.rb index 44e668e3cf5..d11cdce4e18 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -500,7 +500,7 @@ module API desc: 'The channel name' } ], - 'mattermost-notification' => [ + 'mattermost' => [ { required: true, name: :webhook, diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 155cf8565b5..f420d71dee2 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -138,7 +138,7 @@ project: - asana_service - gemnasium_service - slack_service -- mattermost_notification_service +- mattermost_service - buildkite_service - bamboo_service - teamcity_service diff --git a/spec/models/project_services/mattermost_notification_service_spec.rb b/spec/models/project_services/mattermost_service_spec.rb similarity index 59% rename from spec/models/project_services/mattermost_notification_service_spec.rb rename to spec/models/project_services/mattermost_service_spec.rb index 7832d6f50cf..490d6aedffc 100644 --- a/spec/models/project_services/mattermost_notification_service_spec.rb +++ b/spec/models/project_services/mattermost_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe MattermostNotificationService, models: true do +describe MattermostService, models: true do it_behaves_like "slack or mattermost notifications" end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 569071c0418..88d5d14f855 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -22,7 +22,7 @@ describe Project, models: true do it { is_expected.to have_many(:protected_branches).dependent(:destroy) } it { is_expected.to have_one(:forked_project_link).dependent(:destroy) } it { is_expected.to have_one(:slack_service).dependent(:destroy) } - it { is_expected.to have_one(:mattermost_notification_service).dependent(:destroy) } + it { is_expected.to have_one(:mattermost_service).dependent(:destroy) } it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } it { is_expected.to have_many(:boards).dependent(:destroy) } From 85666b7077728aeb2015030d603bc9c9fe6686c6 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 20 Dec 2016 17:21:02 -0600 Subject: [PATCH 380/386] apply margin on alert banners only when there is one or more alerts --- app/assets/stylesheets/framework/layout.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index 59fae61a44f..5365b62e456 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -33,10 +33,12 @@ body { } .alert-wrapper { - margin-bottom: $gl-padding; - .alert { margin-bottom: 0; + + &:last-child { + margin-bottom: $gl-padding; + } } /* Stripe the background colors so that adjacent alert-warnings are distinct from one another */ From 35adeee5148eddebc003abcd690ad01a50219bde Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 12 Dec 2016 17:24:49 -0600 Subject: [PATCH 381/386] homogenize revert and cherry-pick button styles generated by commits_helper --- app/helpers/commits_helper.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 66a720a9426..4d61833d624 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -131,9 +131,9 @@ module CommitsHelper return unless current_user tooltip = "Revert this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip + btn_class = "btn btn-warning btn-#{btn_class}" unless btn_class.nil? if can_collaborate_with_project? - btn_class = "btn btn-warning btn-#{btn_class}" unless btn_class.nil? link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) continue_params = { @@ -145,8 +145,6 @@ module CommitsHelper namespace_key: current_user.namespace.id, continue: continue_params) - btn_class = "btn btn-grouped btn-warning" unless btn_class.nil? - link_to 'Revert', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) end end @@ -154,10 +152,10 @@ module CommitsHelper def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) return unless current_user - tooltip = "Cherry-pick this #{commit.change_type_title(current_user)} in a new merge request" + tooltip = "Cherry-pick this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip + btn_class = "btn btn-default btn-#{btn_class}" unless btn_class.nil? if can_collaborate_with_project? - btn_class = "btn btn-default btn-#{btn_class}" unless btn_class.nil? link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) continue_params = { @@ -169,8 +167,7 @@ module CommitsHelper namespace_key: current_user.namespace.id, continue: continue_params) - btn_class = "btn btn-grouped btn-close" unless btn_class.nil? - link_to 'Cherry-pick', fork_path, class: "#{btn_class}", method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) + link_to 'Cherry-pick', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) end end From 26a80149b0381e678ac6b4f13340e81055be3406 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 13 Dec 2016 09:49:05 -0600 Subject: [PATCH 382/386] factor out common code to satisfy rake flay --- app/helpers/commits_helper.rb | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 4d61833d624..e7d78cc422e 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -136,15 +136,7 @@ module CommitsHelper if can_collaborate_with_project? link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) - continue_params = { - to: continue_to_path, - notice: edit_in_new_fork_notice + ' Try to revert this commit again.', - notice_now: edit_in_new_fork_notice_now - } - fork_path = namespace_project_forks_path(@project.namespace, @project, - namespace_key: current_user.namespace.id, - continue: continue_params) - + fork_path = fork_path_url(continue_to_path, message: 'Try to revert this commit again.') link_to 'Revert', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) end end @@ -158,15 +150,7 @@ module CommitsHelper if can_collaborate_with_project? link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) - continue_params = { - to: continue_to_path, - notice: edit_in_new_fork_notice + ' Try to cherry-pick this commit again.', - notice_now: edit_in_new_fork_notice_now - } - fork_path = namespace_project_forks_path(@project.namespace, @project, - namespace_key: current_user.namespace.id, - continue: continue_params) - + fork_path = fork_path_url(continue_to_path, message: 'Try to cherry-pick this commit again.') link_to 'Cherry-pick', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) end end @@ -208,6 +192,20 @@ module CommitsHelper end end + def fork_path_url(continue_to_path, message: nil) + notice = edit_in_new_fork_notice + notice << " #{message}" unless message.nil? + + continue_params = { + to: continue_to_path, + notice: notice, + notice_now: edit_in_new_fork_notice_now + } + fork_path = namespace_project_forks_path(@project.namespace, @project, + namespace_key: current_user.namespace.id, + continue: continue_params) + end + def view_file_btn(commit_sha, diff_new_path, project) link_to( namespace_project_blob_path(project.namespace, project, From 4e3b9e03f8ef64a39f587c53e45b2f8d4cf571db Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 13 Dec 2016 09:57:01 -0600 Subject: [PATCH 383/386] remove button class size alteration from revert and cherry pick links --- app/helpers/commits_helper.rb | 4 ++-- app/views/projects/merge_requests/widget/_merged_buttons.haml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index e7d78cc422e..c7d1de0e3b7 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -131,7 +131,7 @@ module CommitsHelper return unless current_user tooltip = "Revert this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip - btn_class = "btn btn-warning btn-#{btn_class}" unless btn_class.nil? + btn_class = "btn btn-#{btn_class}" unless btn_class.nil? if can_collaborate_with_project? link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" @@ -145,7 +145,7 @@ module CommitsHelper return unless current_user tooltip = "Cherry-pick this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip - btn_class = "btn btn-default btn-#{btn_class}" unless btn_class.nil? + btn_class = "btn btn-#{btn_class}" unless btn_class.nil? if can_collaborate_with_project? link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" diff --git a/app/views/projects/merge_requests/widget/_merged_buttons.haml b/app/views/projects/merge_requests/widget/_merged_buttons.haml index d836a253507..81abb543d57 100644 --- a/app/views/projects/merge_requests/widget/_merged_buttons.haml +++ b/app/views/projects/merge_requests/widget/_merged_buttons.haml @@ -9,6 +9,6 @@ = icon('trash-o') Remove Source Branch - if mr_can_be_reverted - = revert_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: 'sm') + = revert_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: "warning") - if mr_can_be_cherry_picked - = cherry_pick_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: 'sm') + = cherry_pick_commit_link(@merge_request.merge_commit, namespace_project_merge_request_path(@project.namespace, @project, @merge_request), btn_class: "default") From 759e37f3b192d791e32bda795f4731b29c17d552 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 14 Dec 2016 00:30:38 -0600 Subject: [PATCH 384/386] reduce common code even further to satisfy rake flay --- app/helpers/commits_helper.rb | 48 ++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index c7d1de0e3b7..f1897119e20 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -128,31 +128,11 @@ module CommitsHelper end def revert_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) - return unless current_user - - tooltip = "Revert this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip - btn_class = "btn btn-#{btn_class}" unless btn_class.nil? - - if can_collaborate_with_project? - link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" - elsif can?(current_user, :fork_project, @project) - fork_path = fork_path_url(continue_to_path, message: 'Try to revert this commit again.') - link_to 'Revert', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) - end + commit_action_link('revert', commit, continue_to_path, btn_class: btn_class, has_tooltip: has_tooltip) end def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) - return unless current_user - - tooltip = "Cherry-pick this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip - btn_class = "btn btn-#{btn_class}" unless btn_class.nil? - - if can_collaborate_with_project? - link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" - elsif can?(current_user, :fork_project, @project) - fork_path = fork_path_url(continue_to_path, message: 'Try to cherry-pick this commit again.') - link_to 'Cherry-pick', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) - end + commit_action_link('cherry-pick', commit, continue_to_path, btn_class: btn_class, has_tooltip: has_tooltip) end protected @@ -192,6 +172,28 @@ module CommitsHelper end end + def commit_action_link(action, commit, continue_to_path, btn_class: nil, has_tooltip: true) + return unless current_user + + tooltip = "#{action.capitalize} this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip + btn_class = "btn btn-#{btn_class}" unless btn_class.nil? + + if can_collaborate_with_project? + link_to action.capitalize, "#modal-#{action}-commit", 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" + elsif can?(current_user, :fork_project, @project) + continue_params = { + to: continue_to_path, + notice: "#{edit_in_new_fork_notice} Try to #{action} this commit again.", + notice_now: edit_in_new_fork_notice_now + } + fork_path = namespace_project_forks_path(@project.namespace, @project, + namespace_key: current_user.namespace.id, + continue: continue_params) + + link_to action.capitalize, fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) + end + end + def fork_path_url(continue_to_path, message: nil) notice = edit_in_new_fork_notice notice << " #{message}" unless message.nil? @@ -201,7 +203,7 @@ module CommitsHelper notice: notice, notice_now: edit_in_new_fork_notice_now } - fork_path = namespace_project_forks_path(@project.namespace, @project, + namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id, continue: continue_params) end From c9165f845974134917927aa174a214a334d264fe Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 19 Dec 2016 11:12:30 -0600 Subject: [PATCH 385/386] remove unused helper method --- app/helpers/commits_helper.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f1897119e20..e9461b9f859 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -194,20 +194,6 @@ module CommitsHelper end end - def fork_path_url(continue_to_path, message: nil) - notice = edit_in_new_fork_notice - notice << " #{message}" unless message.nil? - - continue_params = { - to: continue_to_path, - notice: notice, - notice_now: edit_in_new_fork_notice_now - } - namespace_project_forks_path(@project.namespace, @project, - namespace_key: current_user.namespace.id, - continue: continue_params) - end - def view_file_btn(commit_sha, diff_new_path, project) link_to( namespace_project_blob_path(project.namespace, project, From eda7db3bfbf915ccbfebe045807dafcc25b0a6a1 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 19 Dec 2016 13:00:06 -0500 Subject: [PATCH 386/386] =?UTF-8?q?Set=20=E2=80=9CRemove=20branch=E2=80=9D?= =?UTF-8?q?=20button=20to=20default=20size?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/projects/merge_requests/widget/_merged_buttons.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/widget/_merged_buttons.haml b/app/views/projects/merge_requests/widget/_merged_buttons.haml index 81abb543d57..9eef011b591 100644 --- a/app/views/projects/merge_requests/widget/_merged_buttons.haml +++ b/app/views/projects/merge_requests/widget/_merged_buttons.haml @@ -5,7 +5,7 @@ - if can_remove_source_branch || mr_can_be_reverted || mr_can_be_cherry_picked .clearfix.merged-buttons - if can_remove_source_branch - = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-default btn-sm remove_source_branch" do + = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-default remove_source_branch" do = icon('trash-o') Remove Source Branch - if mr_can_be_reverted

    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 X1n=K;R+)PIr$c!eW$6k@{eb@mFWsbT literal 0 HcmV?d00001 diff --git a/doc/workflow/importing/import_projects_from_gitea.md b/doc/workflow/importing/import_projects_from_gitea.md new file mode 100644 index 00000000000..f14f8806d8d --- /dev/null +++ b/doc/workflow/importing/import_projects_from_gitea.md @@ -0,0 +1,80 @@ +# Import your project from Gitea to GitLab + +Import your projects from Gitea to GitLab with minimal effort. + +## Overview + +>**Note:** +As of Gitea `v1.0.0`, issue & pull-request comments cannot be imported! This is +a [known issue][issue-401] that should be fixed in a near-future. + +- At its current state, Gitea importer can import: + - the repository description (GitLab 8.15+) + - the Git repository data (GitLab 8.15+) + - the issues (GitLab 8.15+) + - the pull requests (GitLab 8.15+) + - the milestones (GitLab 8.15+) + - the labels (GitLab 8.15+) +- Repository public access is retained. If a repository is private in Gitea + it will be created as private in GitLab as well. + +## How it works + +Since Gitea is currently not an OAuth provider, author/assignee cannot be mapped +to users in your GitLab's instance. This means that the project creator (most of +the times the current user that started the import process) is set as the author, +but a reference on the issue about the original Gitea author is kept. + +The importer will create any new namespaces (groups) if they don't exist or in +the case the namespace is taken, the repository will be imported under the user's +namespace that started the import process. + +## Importing your Gitea repositories + +The importer page is visible when you create a new project. + +![New project page on GitLab](img/import_projects_from_gitea_new_project_page.png) + +Click on the **Gitea** link and the import authorization process will start. + +![New Gitea project import](img/import_projects_from_gitea_new_import.png) + +### Authorize access to your repositories using a personal access token + +With this method, you will perform a one-off authorization with Gitea to grant +GitLab access your repositories: + +1. Go to (replace + `you-gitea-instance` with the host of your Gitea instance). +1. Click **Generate New Token**. +1. Enter a token description. +1. Click **Generate Token**. +1. Copy the token hash. +1. Go back to GitLab and provide the token to the Gitea importer. +1. Hit the **List Your Gitea Repositories** button and wait while GitLab reads + your repositories' information. Once done, you'll be taken to the importer + page to select the repositories to import. + +### Select which repositories to import + +After you've authorized access to your Gitea repositories, you will be +redirected to the Gitea importer page. + +From there, you can see the import statuses of your Gitea repositories. + +- Those that are being imported will show a _started_ status, +- those already successfully imported will be green with a _done_ status, +- whereas those that are not yet imported will have an **Import** button on the + right side of the table. + +If you want, you can import all your Gitea projects in one go by hitting +**Import all projects** in the upper left corner. + +![Gitea importer page](img/import_projects_from_github_importer.png) + +--- + +You can also choose a different name for the project and a different namespace, +if you have the privileges to do so. + +[issue-401]: https://github.com/go-gitea/gitea/issues/401 diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md index b3660aa8030..86a016fc6d6 100644 --- a/doc/workflow/importing/import_projects_from_github.md +++ b/doc/workflow/importing/import_projects_from_github.md @@ -6,8 +6,9 @@ Import your projects from GitHub to GitLab with minimal effort. >**Note:** If you are an administrator you can enable the [GitHub integration][gh-import] -in your GitLab instance sitewide. This configuration is optional, users will be -able import their GitHub repositories with a [personal access token][gh-token]. +in your GitLab instance sitewide. This configuration is optional, users will +still be able to import their GitHub repositories with a +[personal access token][gh-token]. - At its current state, GitHub importer can import: - the repository description (GitLab 7.7+) @@ -85,7 +86,7 @@ authorization with GitHub to grant GitLab access your repositories: 1. Click **Generate token**. 1. Copy the token hash. 1. Go back to GitLab and provide the token to the GitHub importer. -1. Hit the **List your GitHub repositories** button and wait while GitLab reads +1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads your repositories' information. Once done, you'll be taken to the importer page to select the repositories to import. @@ -112,7 +113,6 @@ You can also choose a different name for the project and a different namespace, if you have the privileges to do so. [gh-import]: ../../integration/github.md "GitHub integration" -[new-project]: ../../gitlab-basics/create-project.md "How to create a new project in GitLab" [gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration [gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token [social sign-in]: ../../profile/account/social_sign_in.md From 7f963fcde2cfe577173e91fe08b14ec08ef93dd5 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 16 Dec 2016 16:24:08 +0100 Subject: [PATCH 320/386] Remove duplicate image and optimize the remaining one --- .../import_projects_from_gitea_new_import.png | Bin 27805 -> 15561 bytes ...t_projects_from_gitea_new_project_page.png | Bin 52976 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/workflow/importing/img/import_projects_from_gitea_new_project_page.png diff --git a/doc/workflow/importing/img/import_projects_from_gitea_new_import.png b/doc/workflow/importing/img/import_projects_from_gitea_new_import.png index 8d76abf102bf34489ff395be3797b3fd3953a295..a3f603cbd0a5b6ad5a2f303a3b07f9ae05692652 100644 GIT binary patch literal 15561 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�PAv38S-9vBkEH*Z6r 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 A$N&HU diff --git a/doc/workflow/importing/img/import_projects_from_gitea_new_project_page.png b/doc/workflow/importing/img/import_projects_from_gitea_new_project_page.png deleted file mode 100644 index 91dcf5a6c022ac65b2bfd169d136479820f232da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52976 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%0DcPz0C-~2oK_F)8u0_$g^Wle2WE5iIUY1;~TcN|t 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 VC3^u=}~W-^EDE_jtD_H@D^kg0VIhkNWpY_tcM; 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 delta 262100 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 cfYvAzD3kPe3#|U z!<;!4a++C0Y$y1JRT0z>QTDj}K&p^STZ8KQ;MDZg@yL-^$16J{M;g7xm$?U7y{;@r zr?<}lYAn#@^y^bgMK7OTW75*g=E)!oE~>x+6prTiupZ|RGo1d8qbU^iG)O9zM3_{i znW9@YkPZodyKXIUU^c{MBv4dQ(?YErvcTn@oHLb?Mb5H#TN9?hgSox0htTCwW-Pu{ zE`@f}GQ5FF8coUC!1_r9TzlWRN4{g3Pv(3?X%#R&W#Wu3qQOBLfv~wg#O>|E@10Z9 z%@lAT)lW?Ew9&SM=mgx{SduO7P3>T8F(aAMO-GP_jJlF24LF!jsY3a(usdsGVs!ri zC_7&N08w66mMP&{dSH{qB1`516+F$YeLI_5W7v?qE$|QAR#nqWT}+UskkN=8?#MMM zW+0neZ~;4maka(TK~*+i7&S3WrW1d|uUjZ!yWE8e4T0mIP4L>Hj%g-_0UWLol~rPt zysbNbUh)NE0J*jJ2h+vwB&jI{MMWG{Z%s=S$eLu4jh$>%o%tJ)zZypu+&f84U0LP^ zhzHWF^J!3^DE@JBZ)glRtL=b=30D?s(<5AoIS!@8esNU8X4IT?1ZvW{ zbqlr0YXEl^H?XVJc%`1fmKo@}XFs5SsPmF}Smm~xSrZ+l!aj-%=P$|^|cv$}eJxm2{S ziCB)Wu=~{#GJ?d9!Uc&2_r3w#l}$#_2Y*S|Cb*&+p#&&WY@<_H*b;BIVTJPA81rh% zTFRD2HCXA7=oFK< z7QO9^_JhYvjy}7s>9=X9@_RLZOf@w~U|9hHLd+Gf5eytzfhSNTt+x%DdTfI)lu4Sg zw3O03Z6h13y4OU8=hif_+urB3@mi86W+jmr(-Ia`epUYf0kGWgPRE15wXs)E6?{37 zmWocc%l;`Q*3&vn!BbU$P|=b2N!)Hup38hj=KeW!EHy5PU00e)^0$|NG;yUmNZ_r2 zX0c^>MAN{j=M3eGroY42~=t!eJknqJRR?~tZ~3(4JKOhtO8iIqOyfu zCi^Kj)Zu{&IQnsnTC>Nw<*AyJBh;Q7r<6LN$FhQqU&jMjJ@HNYovO4 zYN_4bD~m6fbwu03=%50+m|PO3+Ho7xy<2l=1m7X0Qj6 z(j$STXJ1jXfQ+GrpjO1s;==U4VVJb}pHni_%=ERHajsa9#Hz~rqC{2~1hEA2ZNpm? zj+f7C@~ovZC#-sZe44UT6bO|o9P2Pf^Ag^osM~^VVh^zc8U|-W;3MRzM-3dZ^#)l^ zu+qhuFsgz$<&4bCl71r#my=2jZ5)8uC@XM)=+71N4v?jTt2%h)bg)lZB2&WTCSx5w zk5sB3UYN@r0J9Rr9@aRrQ2n8D3cTvKE>V{nO1RnRVwtRe^mPp(4R%=-P&wS(kz;U0 z&9LsX>F$@M&CD6zS~^wg-_4o@yRyQpQGif(V#eSMCWn7Y+yedc_gH07QlylOQmUO4m`s9?lmSrWC>@8#JB$93WV?XKd zw0DUa4Rpsa>ngOe6oQtnX=H*$WgeGeLn{D^Re&Hhk8*Kp$UJSy`jWDvr#jM;B$(C7 z6{_0s*#fX7e`kpui{8z4AA}G!5(WXUg3`y;@K$8qdz?d4MbmFBMqiaw)A^CjIi5w5 zd7{(_ypu$xp9%Wc zGoPqBW1$nNB$8t+qFR}h`3m5%SR-*E^}Wkz05$bC*a{5?LYl7-s(KSM>GjMi78;eL zH3%byc^y31n|({ln+7eql^uwtbg$W8JEosL>i(>zX81rVAf$;*l^RT%WFA=53k|p@ zVh?<5f0?h^4?fGLrHiGycOc8lMAUTEwG}X=k<&&kugui!1YqhaU5%Q;_cuK0RfqDW zidf)|09+ysBh_-u&2XTDaG+S?>z(*Zn8!%cfAv>}QA<%-B%VUeJuDR}%L!!wyvZv7 z0Ty57W+0D9EF=3+c%2hZ)EWJLSu<2r#OqC042035jVTgK1EVx-Nw_53UiUbe>aVor z9&49*_4#&37I5^7PDLF|%<-4f#Wc?q{H6e0B!r6#Xxv~i`j4}Bi_I^&b@8 ze_|v#02e^$zr{Ed?$$(&eCGv;YgvWHqitJ~Hctxv$a%j^S5x&rjBh0*m6b6^@KIHU zXb`v}R+2J9-_i=BSaM5nGRiL$dV;g4sCt@~S_I27)vFpIft1tJ)H^do+DP*iWS7v= z2J*`;n*~rWRenBC4KnAsc4;(IqP08_e=MeWX=G&!=0vZkAr>yG0Mc0Lwgb_tlz3&~ zwRJ{c@b%Hj4J*k>R%%)WWKb@VhAhpzh)ZeQ$Sr*>rwp(0n;<#Ql^QgT2@%pR=l(wzA9AkZ1;=fd90ilVhbY_F$hl9Cqn)Xct2X?qs+eiAKvDuQ<#V^is02D%@o z5v8ihpv-DxA|XCtMMR3hh1pR3f7+4q7aF_sjSH!gm6j?fVPu9VK(nCGNgxX_`SX4V zBLLSV%>Fv2O=PTwC20hb5g7mwMS&Mmb#Z=3#G%f5b1iqJ6m+66Zx|2>z+Y`n+O{B# z{jc8@ih4~oD+Dv7LT7KgkdQ*Jzi(~+UtA|qT}d2;T3Tc`K9^QvyPdA2e_!+8_P}Zx zo?T5%B~7WIsnN&a!`l&Y^z&(6nt0}?e_79#L~KIJ%dva+ zwY3Gl#@(=C9H|q=vN9t|!Z1l7k;%6qhS*gSnhG|kok4_ofnthJ{C$WZivUOmlge>%3lCE)71wvL9Hw#^+4 zuM}h;lrUTBlKg>TVSXE)z|T8LD6E`x!Z0yJKqXtQdG++sQ5$VG@8t4$L>Hj)$`j@okL*XF@z&C&{tH= zl*>IN(*Vgme?s{z5yhAV)J2%;7qJH0U?ZoF7+ocx{7%8Za@LJTn-&}Q1nu+M*20w3 z8HQ&;UzpO=$0?;rC6Y;^AhAS-otC|ckP8F59b-%VBuask%Ji9k~g-kj^u88 z4T55p5gV_VV|gsl`KWE_LR^*x{QX6b{wb3*aSBTnvBJq@lfECAb4+p^xn{{T_GD&GrP9YYH0dFP3dEFvmpjhDF8ZT|qN zxV4EPcsQ6j^G{DtRz*ZDJ>i#aF@`EGK|SmZ@5l5Bmy_Y?qoj^SiDdHJw2-k@Uoma< zBJ10^{$QsO*Lgq!XZ$o0F8y2NG<6Ajb_5%JK=oKFj?k?Xo3Y@a7E?F zs;L_Kih=4kDi3Z+JKGi7#+XRyK`fqBOf^L=`oS0VF%}jBZ+>>bF^OfRjjALbw7N*s zD#&$Rm2}(^FY_91N6QZl6fk-Yu^bx^VG#vRXrNc zYS7BcWoFch*9Cynb76m18*|1LsE%43ni(glrMrS8Tm9r_#a1(>*FS}?wT7E*z&Fk^ z{F<{Qhb_-CXy%n%LS&~ct~K=y4WN%vDgAYEVvT2p^*O8qIO)vROqVf+jJ+L1m2yuj zf7(J_1VoT7S$!|Q#@{7Xnh8Yn!eyqYE2>fpm@Mji#0qyLC^^)2HhnD5}q> z-CmA4ujgyAWRb0RUUdUz1F^BdQ)Qs>3YwhNRA%`$V8TX)o>(WQcaWjG5{$`zQ9KST z53wcc4BD8jc5Ts(lu^hPjI{L&RETUvf23d=L1qWkK~2!R5MK-8fGyMg{Ww%nneoBerYN32EsRzar{P;*yW5vkZ?bs;;ZG zPr%aDQ=|j%fFzP;i~>#D?)6=N7bJ#zQ=atQEeuffms{qm6UdEMlV(+Se-lE%h>u!; zJtVDw03BAjBFibocdGbBnHX;2%L6 zicE_x=}JhRmbDFIMLV~f9Ct2>GPT$+w*7_ozW8;Ucn#r%nN>wKK3P{qEQKUlUZu>1 zS795e(uxRQ#q3XVP6Dgbe;;SwgQ;N-VbxRSHStXHO;1f(98kuI9;pqLnblQJqQ$`U zj^@I}mh?}7{SgqYn9&N#1Li>0b$$N6>oJaLf?W;->xiNj}6|Ywj41X+h(Mh~9Q`*+p#$ zr1)VHx~w$O>03(QdFz#nqUiuIu&`?>CZ8uw=Zl(4mC)p01Dh&I41%UMFa~R1L0~jD zCrkbt-E1-HTHNBlFX_6f`Zf|~OokddbE?eD2vXaE5E2VEf7S!%V@Rj)hQBtBuQ%%7 z5f?DbW+?t$9U8+7YKtYQ*boVielDym2q2sXob%J3WHPo}n8jBPWk$;NaY!w8HgLp) zxduUFus7SbCM3+?IigrAW=LvcjyU6zD8P`|YXj5>TU>AH*w|cRXRf*nGU@53mpaK} zs;8C(T4|=Lf4p)X9+Zk*ixp*Jp!$N?8-N+9Rl<&+GRwXKc`6oKXm1-Ok?H`^DBi@} zrN+Sejm86R=pM7kI#!o7pzCNTja){T;UlPpTBYHUm5Q4yBF7LCIdH&|`<=Gl^M4An zuAsVaO?E#Wx{V|z!G?X(_<~0>@Q^7Q$878J%A{_zks3MEl zsb(vEfYKf5F9B(4NAXPO!yEab!4v;6t|KX^a8?1np3HZI;aXREXW3%3_t3xw5NzUw@>C2`L0JP$}8u- zTFz1#qK+34ylga|F@u22&8K%=BEuQ}VJ~0x$6V9&?JrBgODv(Lf)SO^HFTdRO>T|Hf_*A^O|O?AIhWGtQ+ zWONkru%UV&C?$qZDk#K}%3=huj0MwjO~rvEma)hB+plAvKd#DpX=vmU%PkbJ7?}(Z ze@4UaBx_k)R;2)3dRUw9fUNaLTTNBeFwxRUm*qKSM5ZQ!8jv8RS#Gi;Kd1%WhzxF{ zjqY(u&#K)k7Ahx=7DZ?yK3as0Bv7~Yjll%zw)>6%Aiu52;G(FTG>VyXIb?G*NmDw= zuMLR{CW4-k>9w@}TMS3$d0kgjWmRILf1PBVVOU^ATRaIJtbl?^05UDO7CrA^s&w>J zP_1Nn-fvSy6fj8em|0~KGDym(R2wGh<8>d19tgSU`uGTZoW2R=o@gScN{JM!Ln}I& z2q4(%wYcB`+Zz{#ohy`hjhA$nNO*zL)bvz2WnA;iTS%q}a|(YdHj0w_YGWffe7ZIFNz$=kh?xPA3RvlF3Zxywn;UlwiWu{p+c|ubB&PV<#j5yr ziUsoEch#gbY6>F+*j$ws_QOo~tFvIa-30jnr>T-QibLg?#UzO(m?CKwf6@R$dy;n~ zo^`&I=v?Qe^SX?WGhEJGr}%Qq6z!2sSuIQgPb`2Dqg2qr)HV0q6Trp)0Ij?f>7J;f ztD1b%HDb|5Y4b^P%6zEOHwzV1a@3im2Vr%9vw$>{r(p4`W;GSBNfhk!Xe|Lc$!QB4 zg$uI{#k=#{Y-n8}mS+?le^;3Fy+&OmRgu;*Nc6+c^0c~w7kk_g2y6BuVSD=4f3TKM zSI$$Sj`jxO3bV8{$Ys#7phQ_re`Q-iP)po*J&yd_ zVldLBb4uDeriQ6wnW>s-CaIZMSyUvsXEr<%LH0HrldabHFZP(sGK#*M>b|Y%?Dh*4 zpwFePkt$+#(0w4PsT!0LIs8Fd>~W}8XO%t>7b_1GTG|tYM_NN zblz)5X{2#jWnF;YkX1Hsf)79y{#| z13?V2&6q#m1dT+opbMmSU-26qTMg_tzW8ZTnEXa(1a%a#)Ucn+B549bY}O&KZ3}CI zza973BqUWpMMYA_B=s6y9z<(MDnVc(YPyyXZVS9J) z{)`LN<<)aWs@_`3A}ESgkh29ARcrnoC(^fbSpt171pqfd$iKJnhXOZ11pp5~@V~bw z$O4%We-W9LbpS}&J$u&D_t`8|jUy+#RQb`&8uc(7c+>qDh#Vhl}va{H1SRtkc!m#tf+XIC-3`TCG=cX!5ft zo}06PT3TENo6L-p{47kvsab{wZ_VrVcw?sFe?_=RR@B^~`C*fvC2{d8Yn=rbRM^bZ zFv!g+P3CE^T%z49-_TIga7Hc0ld#n}{o&~khhM$zoaOoIcrduUyzH;Ixqgz)2h`+X z6|OS;@9_5R+g}a3T@%Dpa~YbLx=YPG$y-5e63i_M7G}PVreV)algWA+#yR@r@lKLy zf6+f$7>8NjTBX4xC!l0~^N`*>qk8mV5mD0w2pYSTUj}(j)9O=!V3Add1TC57Sui=L z0Zo$Savewcrf0JCzl~7v%NI0QdQ<*9ug1Jo6b9#RnWUXm}W|z_YEXS0% zbAl?*At7{GjtOWqH?F56QQ%NvGY{i14Hhk|hTl8^=q#Ahyz+d3Wv1R|NlJ^xe~4I{ zW6RB}Zyv4vRwfK=Lis$1;>Cu>7>BbcH?uTZ(z3b198S~df`Ea66fd(NCfrD3>OKo| zOv7PI&qUKGS!Srw5)@g{%qMBILXC{a%MJ;f`kngE65OkGxd@YIAU@~uwSvWhzhvwA9IJ+L z2U=h)aZS;89HzXDR;d9>tV0EDMDur)VTLXpVgq#;4# z(MCoU&^C!?z`=IP7?haTK}2MX-Y8~W5*BxLBDq$_V*rAERfc6X@K4h!Qe>7Egoh z>>K*71ebchBm$pIr9CErc677~S-1+)fXN{6CIiyZ0JCr+$V~f#e~w5lK*S9+9^0#x zr;!XPm+tk()^l~aK1X)Hjc`9FMJ?VLT^n3DaK75e+P@zsZ^W?WWG4YZpO^8JRQM zg+x5la2}*M3959>5rfQ~c&*dDAG`uw~4+F2+L%caW zI{f3e|j|zZH>bx!9_GZ{B{w< z!90iu)79YimtX(emq-70^v6GT1>Xp!fGZ!0^gy7eqwDf4qKPsw111v2P8MN+!#~bw zsn%QwWU2yJ0U`!X>qkd;G+8gGnryqyB09&>p5i;5evHk(tP^aMUMpJ#%pF98355T& z1J7)s4Ezoef1M7T{2T-hXyvD6u( z=qgziLlSP1aES195l-jfl-Ggrp75IX5XdzBSw$etv2a$wYDM@GEkmnoS``(nS?l1BE5~{{mVL_4U&8t|diWkAvm5K@$_aUfvnssM(9?ud;@%ePXv%5HgRG7$hTO zfAl%wlZD<9;yn+D+T?FL`*PeI0-FyP{8A>oAy(-M-}!;xy$nF`>0QhZf-cOe~xID!P(ahxXjyaxcO> z@XlQ-wk*GqQc*U+C;G#`-y{h)nd7glV12Ein|R4|S0sXQGTj&!ZsQ9`Kq~i%v>^0n zEc|^$;=*MNCi1>n&lk**m~~8>YzjQ-o97{DzDfgTuT*W%$}t-fDyRXP%b04>fAI@} zqTerRMg9_|)cIp55MtBe({l=`0*lcF4*p1#W%T2lZx6p3f$?JTNM<^V7GStUyzXn! z{4y~I2RN<6?BKwxSYmZ8ngzrm(+SQ4=GjahjEQM9FNs4Y&VYXs^#wMLMk8kL@w{)w z0S=-WgnVh6-%rCCpN2y{m%I$2hu@&25;) zpdvuhzUdr1I(afUNeCaKZ16ma*H?pAC+6?q^HVv_q1Z@8V4wj6#HaMA`M(j2lFLlX z@p+y@&jQ{z4VdqptrvZ}PoFuFkq=SxXW};M5~F4C-QmQw3HfHxd@Wo*e?)t$upl(a zX%n(S1V13z2(xTQhsj!)V8Vu|ez+2@?AWau9jj;j{E3+h7exILs?CW4vf6?q;9#96 zlXNkI-X-+G`-B;qHE`~mkzuh$q#Wpcau#?+O!u}sNqzLLg{h@9RT78^RnP=DvZ5U0 zBI`-EAQ(Tb*3?0Jme5$!fA%-@fuy)Foh{ao1+s7%5v@w%R=F${lxI|;I@knW$B8fr z866t}!zwD4I<2 zjQZwpXT)1^`-Nh>BRU-t!qPM(20)J|t9PMRSf>q55{YHJ98m1j5d@08X4#ZWpVPji ziqI9}&-`kIa2@$2{P}$Q+wlz*sIM z_Pa<3@F`07%olI_tmS=Y-iPm{NN7HkmlA@00)@L<&z`6G@W8FpqHRmNmw;kF9q-zu zA&vY6@cEbrThw6k8Pq6r%kDIbW??5GwvF_B-F)jIrR=GY=f$N$mx6rWiLiwOQ1fD~& zu)szje{=x3E^xKOx}tQxX4YIuxv3mA+pmqfeeO*uqV0~r$*rJjFvU)To}1Q}ctIfF zMU<_H2b)GSy@90J<(JT8msgb=vCG1{dUD41jW}k&X|l%aaI3Bh)rl#a1?g67m?xzJ z6!(f){$ioI5ux%~{OKd>)5lbqTF=QxjqGz;QAdq`mXNU(j=G% z<`%JhA@qn&Dak{YG3-!J9zY1QCj+DZs59!B(dh_!JWJ!>;#GIlL}cl(!x+1L9(;vp ze`e9IB|iUUd8dtTH8oify;}tjQlogc%JQ0a@Qj5{GrBwrV-{Se&|lKu(Im*i5$>{~ z*0o$1@mZJ5SxuuXn3Gt;OkRE_Of^B#o=a^Nff)_L446th1z*Cjseo}xdTOCjP0y=P zyCB%x%~8{sm4(2B2pS}DLk(r_-81bMf3%F7wv&G4;Ch)5`#2Ax_*T1Cb85xE`{wA* z?MlTrNn+@vpO@>siI!m>+TJsWhjd;Zm83@vD3K5&WVn$6c=$LBRo4m(or1 zx=A)q@tO%XPH*?*woc8PSbbU^8vYHw z?H*sQ^Kp&pYo_4Dz|Lb<)g?3LmWcI@iAL|zp6FE_v`@)mo$Ha#%}5FC%sA+VL(T~ zUxHtMEtUO(%YI?YotSEtWLez5hV?0Ru)F-QUZlEo!h{va>18w}5+1TO6)k1ge_K0;6y+7X z!Q$O$-9B!~Zm?Q6Sg86M)s{!!H!f1+>Xcx)-qb{ zkFPta?p^vy5bo7qPm*<u<3e`Vf9O{6wyaIT@{I0{ zoGdT!FE{{I`a=b^7B#IdTmi$K;(t{IMI86hy<>Zo01@ZTy*OI%(--$n-*y#1`>^eF zTe08oZ)L)odZ1lU?t@;qUg-HI{W!4Ufh5XB=xml)%7qE=evie+!GfeV$I(58#I*iPQxos>tk2Y)K#`R_r~wG|XUkw&+%y zsWftEn*m!q)bSC={VjtZw+Ge&0tIve?P@FhudUibUZ!m|e@pajN+eYnVHHN}wmZh! z?g%Qdz^rP6{4!~os?koP>ZB*q84X8_m&*DjYD8H%I%`=|GU|^^=apz=Vv4j_r2A|HEhf4%jbflX?u!9@dQ2vRhXBdM%*Cuq{X#_WoZie zO-JEQ)0-?2h>{7;@PAc~dqBdgLS2E6Jh!5w8$$8Me~ae&X_^(WXPZRNx&k&g#GBee zK7`I?vMybfF2fW;E$vp`wZVdUw;VR0y2;f*c!ooln>hthxXu`?Y?DJ&V=*}TrF4(A0w>IBaXR&5IZf>Ko6`Tj%KQsG@V&+&G5#e%F-=w};4MaY30FK^ zgz-Fwf9e&x6~rdavz}h!sSyPqCrnS7O)fY>Sq9wy5^GFNT%#{%2MQh?`#E$N$0Aj7 zB(QlwIpzfD=pHVK@O8xM!=Cdm=vA2Z;dwx3cqdPeO?#Y-|DoLp==+iUnt3y!EV3pnO@h#c0TAB3o@Ji({e~($g*6B=>_1Hn?qetxFW%J;D#BH92 zS7PUcymHr1t;~o09_Tlm&o`Qj_wT0D^8VdyhNct4)L`+uSMQpsUpL#EO|Uj6{ZdQ~ ze=SSn=t#IwgB4JbGz~VzO&7=qlLzK@R*UPUuM!>LFVyyWNDbn0^R#`k3b3<&<6oUn zfBR8z4&x*!9By3sZ4GrxBePBa_YIKi?i{7I{x5>!Zv~@|MANCVXRw!IIgvQrImsex zoQ)Ij&NP9)W13u8I}(v@h*QnCTImov!MB93J#LFKfUn{naFJN9%Ww0)il&q0u0jz& zhg%l9L+Nb2#Hoyv%Wr5%&EUVP;QabYe=;#*P%k!-a9Ff5)59O0Ip;r-k&GILt=6!M zp~eb@k<%A3XQVc)#f>Jjb3|N5Fj9OTEu&n|PnyA3x4}w5o`e6869*lyf9VVw@bM`u z&v5_GwvXc2LLA(su;ae@n)GfBY*}X6fN22(FlK+bNjMcdz@iILZi&R%!o_Kjf3d|w zwgTG#QyQ^=b+ofpGU1s~qhj{Pv3AAkqzL|KJ%JU4x{Zt;g1&aTd|5eqnVcu{ zD2{; zHvvrLNHk!-duWxWJ4MsiPTK;JZ_*3ACY&Di=-61mkqA~AEHV<~Gs#&Iyy=@4N#=Ai z?EJ&W2sRQmS{N6yUt&bNjLwq2Gz{?|vQDRR-%VAQ%-oDX6Gx`QqK8^PsKrU3Cf~TG zB*8fqGyksWiIsrnml2KXe^B~q8HYk7ny^=RC`8uKS-1*KhbG8do;wwaVDC!RV3lD- z1LTWQCdAgQ=_1M^cC=XM>_-;m4SStoVrRWe)Nd<@ z%Kg10nU$?)kC_c&n9{IF1a`0nrPVz@LcNk+&%?@`Eif=GPqR0QW&=Ug-G(3j#go|2 zqPf=A?a_2})!Nuy-J8Wj2?4QEH;ZBrRf}%x89~z;4MsXKy_YE_?cMfYGie=RrEU@mw97e!oA0?I0g?)|DIf zb6yYtT!Iqhf6T-jzrGpVS^C?M_xQg7aVvoMh5^wRAFtgrBpHkE_2yD?M(l^+FQ}uK zAhQjgShjm6f1z?SlZN>^jU~zk&EUG@p^7bT-dEMdcVD&}pw-PDs?8g1w_@pF_4*BV z4QzsFu7m(FHsLHxVs*@reaJ?jB{jEo6Vd95h*hMk;AOwEa#Gi>-AO1bLJ()l%Kim? zH_lrlyNJxw5n0)KHq(X>UkhGCdpAlWn*{85H6y&Re=;uO95LJo@*U7BhjaC-ca&+i z=Txzr4dA$g(XDH2M$uNA1na!5AChr4Swpyv77|UxqR_2dc1?G26ljlK1GG6+KgZ!* z?vx^S-(%i8fh}mlW;O;yR|@GPFX*W%plr zynSXmNoqks?FcXk65XAksLtkA^K`Zqw{*2$qZwXGV}2LR7>Ao-<{d#6ZY=AMm>=z% zf7hV|d=gev4edh>;c_8|qTFfj1e%@2nnhbnE7dnPh!#Zc3+v$0SFO_Cid{gcre@0( ztId`>EfeTMH|(p=JhL%RyqqC$O0io9moS&2A9=L6c1LxtVD)~xFvz;8kSz6XeBhMW z3^zP)7Sp6I_TP9ST#$$KWuai%H5cjqeE z9Ic2-vvA@6aXWD>RQMSFVqvV8e_HMGrwLUhGQkf7m%>u77@;|amr~Ch(>AsyR{PDg zBM+D0+;Hu6Put^&1?@NPL%V0bQ0iTjW@sbqX=!1tD{vC`V@_EwI@EtR`qt zJE00(;{+F$^FH4#_f_kE@17~$(Aw)BD2qj5|^oD{$M`TrA61^$2ZrlO_dGJ z>hqON;V`OcSG0_`FG{=O8cd?S`EUX{`8uSPm>wTLD%||MD=7(A5D=N^D5AX+moQp= znNQD%?HFbW$`-}P=BVF4f4c3#t$Y_%v}uv|#w|7-Z#8;Sz=_S~ir^vDyTE2t5nA03 zucG{x-U8m~6TcBZ&|lR4xju^I++VN2GSG6`~460teYnE zi}l!;s2^v8?^1{qsSsx^!)^5nKP071hhK?0$l5wbHUc%(hBbG$e=MMvN3iFMUg}}$ zE5fR5Uh4dX)TnM#z=2X-iJP z<_9`j&f2xhG_EBVS<}S4$ItgZOU-nzL_)o#^(w#9x<#$x7OdPCbTpH^)*GJtY!}HF zvkyA$leV+(>Mw&;f6)aO6-+6I5UBT3u2XH^eAv~nLqa^@CSPtbv4iwBu>ozwi<#Jk zbMxx^Lb6h&8DnA3Mj6}ajjx;(**S-Iq=8?s|2Rwc9IcEa{IWBVi(bGu6UZ%?AaR>z zUOr)M+^UHUPAhPUX1NPr@&H@wvD)8j6szz`PZYJ>QA)wyf3S>>Ht>hNue3oz85XS4 zT8YI-BscYX;B)w1k!ZPQ-#iLASZsoOHkLcXlp`PESf+E01`dA{1Ow{Xn>`s5M+|0D zC5}IimRs!RJ!#kwsul%lS+20v+BGRYobeJ3^W|zdjjVC>lPK*k&*|M1p@Rrd^|M7) zDKR;tR~`M-e+3euh}lFTyRoN4`_UDT+Xs#VjJ9S;`8FF=e}J9OxV`RgA85~%o2d2f zcgJmd?TI@!K9sM}X^jeRI6ks3rxGGLKQ{aCsDcJ`X!Ncd$&Evkw+|Cn*5DNCt!rZ_ zmHfx>OiY-au3m1~*F{`}n|NYX8*6T*K0n&hvu0nWf6g;D8fTjx4aF z>0BQH;3kLL5@f8%anC}|eco3$uT|kJT&lrWj&9%vTf&2A1MiLT__~Hp(f7L##jq4Y z`tkG_d+04M3jBh7cYhKuh>UgoQJo&E$@$x&f5BpbO9S&b_}a*r1a29ae7nK8Zakz0 z94j=O66bcS}@VTB2niH2K=vRnkXwkW}23zr$|tjq^H+BwLAS-E#_zz17zM?0S* zf3$ay#*uZUAu@46DF`;2~8e?f5t{hKXy;5~ZJgW~c1`{Mbt&+N0Ws-zMl zaW%Ar&?CltSgRXaTTk?0!H1GpVfSsVMAeZ$59H6WDde^$!3lTNu7KT}g;uJF!4~FH zp2ly>_uQb_-4GY>;H%taI+q7nydgBwl_tp8>s$vRVr!ga0q4_}Y?U(*jaVUPe6gBYpTDzzecX&`ZNe?hkBm%F}bcB9*0-{!DT>EH8$2oQ@BJGD&N3$V4d z7Vw(z?6|2Nt*=bS?oP|YtS(t|*J?P>-0L{Z?aAxc*gD}7FYjbz73dk2F-l@;0=WDX z23ezf_wJ22E?iXTQCMrs5;8|o2sIX`Gx=7fF0w^!teKX7CI?Ki%O=Z9f3(J8XQs)s zT%f6$NcLOeauzGd-W$Z0+^b+jhjos(yZq^o{A%Dz3|s{bTCja}`cXCv9(1^yZmH$I zx|=-c{ITomxF87s)92tH!SKw`S;69kS_ zv!SIMV!kHUWl2Kj%pK``5(PeJ)3!M*d@)C9TZ;!s(b~9ahyfon0mZAPQUBx(xZ}p zjpwDr5meQ$wI?EaYc%V4L*z(}&W=4@dyz4`n7f6VaCCS%QXvMZyKsczcmONg!qv3o zDuY&Jj*~B9rJ9ld~U|7WnJ{4?&!eb!{IK;%5YW@mU_XlX^N1a!R0x@ zq-gx`eg~~~3(JFixdMYsjOHJ`HwV<^z=BocerRLM_E0@HFT6~o7+m0&s5DF_f! z>7}X+z_}=sbe>2mYVRfj&itD%s`cLGyk{=n6NHIYe_Cv{T2~QTudL82eGV@m&DbZO z>0<->qWJz!;kj`v!A!6aYXYb{nZn9eHUD+TpX7Jo%y zYKbKbe}g4v<65;wD?RpkBs9gLXiWg@(JH8EPfiYwR+!x5YuV~v0nlfQ7vkR8vVgO& zj6>Gbdah8MjgI5>^lVcBQoUq0FP+;qc7xbq*q}ol+9fUp%$aN?b=ah@x6MTq5Kr*8 z+oP^$PV5`uXDecqb9L+BYd3J1?8&(da;4kie+2@49H6IM?nsh6?SE>|^_`oy{pr+v zI&GUz&FP)nJ#%#Xtroa<-?Z=CrWfrx_b+4XXk9utxn}ocYQAHR?t}UCokU~$fS@*b zb zW$xai4Ofnc(pas7D*Zmndg_I%I;U6buWx6@P4u|C?#@lT!Xd@c=$utrQebice~ZlS z-w`W?UTPE>zW@%*SKnW!vAZG?e5WuLbnJxx>8Rn;Jq{SUl?CD17iL+dy8UE2ZF=pL zqUG1WhH299zC30RrghJMzRts;kQsG5H2RP40_e@@@hnN+y4S?)^98*`f9MzoX|-Op z=IJpN^6QI5XcR1l+JC);T5$3-+)>yIyWMazT)P+EIcvR`b=RwP05nct%G8>v4ZES? z3Ve3LrnEcv_l=}5MJmlVHm?$l(Kv?rhl2wSe}aLu?Z&spT} zc&FpzO-gcfG!ZEO8mtx)+q~3c!T$!FMbUot$P5#_EDp|T)%kLOe|3198Qf`BM{kQR<{pYL`?j0h}_g zJD$)qs5W=FJaK3BUgKanJny#dF${ z$X-h=?Y6xV09HV$zq=bulYgJK!J;TOe0;NVe_t-P74j3fxbin`_QmrXfU$~jx`o+% zxo5;<1d*KI>~l$FondBChP2=m=_Pn zpB%N#u6P@38>vjSGk=ZknGyR7j1Xm3f>GJalUWqA-2u~Z4MfFk9}0GRe7V>4VPtQ7 z!Ye|aW#Hx^VSXgkn%uDiJz;M|yk@3upbV?}q;?!X(xAqCqV6UvU9{0~+jxYl!KDfG zO^*IsmBV=ZUfT&_8ZvlT$ltT~T<`8(GgDT- z0uZCLC+wtQ)@=ruKOR?Yx?c602D$~H9M4)eI z_wXL+1e3{niGM69NV=hsal3uny!fc#iaRQYIBICujF?!CI-|RHN2=Hz;aW)I$=E#e zeiBtLqh*vLn<>%QXd*JNO@^2^Ip_n1pLe*wYaFUQfY}?YWw5e0SDN7-MgTO?sq-TW zbA2oRBJv*dL8&2;G<>r_*U$s;&s>tjbHMW3eh`nXk$-Oo$+lsV$5yZ40l;sp5tL^{ zB6`~}K7$hks(N8$dvfoSoZFiro3$`K+bn(KVSqD=&k(#Fw)1I%NO&hpnnoTh&i&}7 zHqIAqMLGF=_I;tL@+2&dj3v{MYfka3@IofW*jhd)ARO$(hb2JXz7{e(aLCtr$Q{Pf z1UI>IaetPE1x&5A3@WQ73l51xXjOt3NGa(OJ*za%55rjo14NXjvnnUKhR=0bA-Sp_ z=_Ml(lu=7!d}ca*@jh0?qrG;Q*o=bwIN@0H&v+y$o?9dVNQ}K>kS9UYul*m}w(T9; zwr$(CZCg9GZSUB&XLfAuyu0_g&xttk#))|9TYq=|`s%F8%*yJH=x$uSJ;lx=!fVgb zNSE69-y`X~JojQyG`wGA=?~^6^uk8b5S{*-~c$p zKrv)cDCD$JE(BZ#jI5V8pup7JKXMl}#eci~gT*>kTr{XJN;~@O@vRw6${`DIQNmj! zrgoB~e@2eqThX3}XGSYiTW+9$(y{`z6b{6A|3(rIRr>uWZ~cS5!^Cx!nSZC%CCtVo zi7~jm);e$$03F!X>hNwb%TuMMj8d`&2Y(h4qd>}`z+B%GLPxcG8)_-3I$)Q2GJg^> z9FPG@z*o918ZMb?JG-$1rJFsLR2!tMV1=sTmXrl4BNt)k+&!XQ%Zg*s`SXo32s`bn zZmv-}zloo^8E-~Gao?+JCEMVyHuz zR%2a?Q>`bwR}13=r5%v9N)!ZCSwB*2fMT@A?Ny7{RQf=gn4st5mlA@LT-?9E-$$`n z03)20TvJ{){LVpv@!D26swHRyD+k+T&n3VRzNK<15NfEwdES!H8?kg+D zh^$yBiB^aj{Vta*8njYhEs3uQZO<=x07GNHVbZ(9zSsBp1w%gmI6_=wP!ijL_{ii3 z)DN|Ll*f>(-q#}kGJjBSlC`2PbVM95M!2PPbYFhx1@VD8e&W1IFMm@==wqB9biGqx18$Mxa84g2F33CqHu z5^eJm3oZO}kU1k=ql?LisEsW7qHFi^ide74qkm!T3K12ROAlYPgqZG8CbN+5+H0$G zJHz?E+}^#27CXr?2*-&IP#UF5`9#a-uS;m4d(nS2cL z9_wcF{?we!Yucs@PLzfNOG-|O*<2}sq7v<$`UDd}Nn2*iNt^YKwdNZ?Aih~y%2|uK zr@eAig=0?3p~JGD^nBa(=%Qyska+t<_d4bt-$qv@*30Dbn;Eu`f5xa5J(|QM#%`0W0FGpjn z9GD?_KO6Srj5g-6EpPH58Dm!gCnAf&gZbJ=ThoesonXfqWgKch_4fKr^&eAS>^@jr zPsDKU%|b0)C+f`P_aM@RI$}xx60I#1s`u+H4FPGo86N3~we*E`Ig*Vv@c3n>qYoT@U zG8ND5Dpxa4%dIWCsS^^7Jt4@x|i$_3LuRY;L10YZw2>=vUdB@ zAKf<*9ka~0=M$mp2B73d!xzFwG0c2i)I?EpO?ZK5S7q-?H_eG7v zpWB^GRh`g1lGX4MdI)sZsw99u(+GCV+_ogwrrBQ^)z2F8d>AyOZfUWIei5AM(OebV zGk;bl8cKEPiAJl?)WiMf;`RHK1pSy{{MK?TC+jd#NLH)CxU^LdPd2S&r6AZyiTD^vd6nLu7<&30>$ey{?Qcr?iKADGmAe zCK;(ZE59us-Eq4bIA{YWFi+5AvVLj@k=Im|-NM*6LG>?aF0PiJTs! zbIU+G>uuPCtfma;RHq&cDgeSXJwKs+KC)&R;x0E}s0+OIZn}wlmV1#OPeV z${U^hwW6e5Y)e6$%WQ^EWdZk48Ue6MKPtzCh%FYsJ%g|3) zao4QgJ26|;ikZjhi#Pyrl0t7oo>)6JWE_9zty#>MKt2@A>)m7m=j74_yMN;kWtQoL^1^n6$Utk32-zpuANg(C^Vh?2<)BDx(PL zCu)KKCt@}U;a#aL<0@3>4^mA-Qf@TG-qaWDL$-&M6KaY?47WKaw_uBcWkfYm-H9X> z*&#``A-YDAj>T>isQVmWHGfHJx8H$x76UI*30{0M?p9kAt=419mmeueS*5YGgtr^;KCVvpo%bYd|DF zXor0_`y0DYe3tg`La(&J{%I?&f+ia<`Ge-{VEn}^{HQR--)KXOc7I*O{Jxkkrpw=U z%%x(+B{i)l!(!Y-0U;uS3*Y_A%_zYRG>*1V_^;-*r1QF!>_ViA9I1)>Xbz1|e#zRH zeH=euG@6R;wiZc7p)@H~_?J2ak)6K{#gP6K{^~Z3Kr}?h>8bYzEo^R0*zt(wx@wvk zTd1~!+o+N<#DJOk5cwO2?R6=2#hJuR4#ovtQa zPS@6r1cQ+Q7Cc3Y557c%URbTl-l0K2xPW7LnH-(xS2376zkdQVP80mvi&8J`JuWKQ zB8uwFT!X@bY0|HD$Yg3w&o%&de$sLMe_oMj&W8$zggS7RUE)+jfwdY8uAiQoPtpKQ zl{!RJS4x<#9#C!9nKul)HMY_xA2<_H7h`k;nX);)Qv8wKtO15gY{p4nfV%JS*30fs_G(W3D;46MpFQCQ-PLgeW;}h6#=Y}Rm7MBX zuAg8j>Ft}f!CKKU0@pieVA*&|M7C<9ouFZ z_xogJ3rNs+91yH{Z42K9QWv;NOkvEe;i(I!-*j0sF}>dm+Z`AEQma`8RItrZxE{|7 zjC-}E?|+6lcnI~LS(U0fTxaVxJbZ`mCib3hvCnN;MjpoV!@#qHbR(MDo>8>iAFhF`~ZZ)9?LpGR96aN4wlG5}d&A%weKC z#5$G2ET=ZyiK@|_nGG6nDa$iU06*8*iW-pF7 zoqww)gUWUF4xr*NS4KW)^CpD_@K-k8EVn%Di6Sr@141saYpq~2XT#FrYw}qNxW=$J z17Ota=(rBJ>)0n1(8fLN1`yKwezLp$yrtAb0XP#obY{b`fEPHLRx(%+?OksSOreeL z#w_kx!gq$Tjq;T>vV+v{hT=n|wpBN+z<+fZBbL(@;(W{%QI;mAApsoEXEqMNS^D%J zr+{o{FVb!ouXvEf{iR)LuB~DXT3mcR8Ip%n7A<;ris;7yondX@H%$8CH%&g5@*;@w zCUyCq2V?zGZNIC6jV+F?To$%LhB|rE0u)IDad) zz@NA&f(W`ep%r^NcHNyH$IKL&GAovF*`Z?T0Yz#%ujNRXYsj>i<`YKKZZ!rk#a?{< zqZ*VONpO3LN;({4jHrR&xmVY^rNUc)>qj+_ehvrILIeVvvZT-y_n2bOjP5X-VoOm^ zFmXn_MmZ&M+IFJowYMX+wn5JnFMo5s29aJ*%q8C(TmpXIw~O;hc6J=GOw10!HySlN zw9i~wL0xR8HIv_3_q_Mu9}OK+K0MLW{V8ti{vnq4q(LD$!+chec9%@ruvwPFR@y~6 zZT43(C0tKMjXJwxNLa;~YQ+mNV;3TTv67hTYwaWHefR|fRfntwS?ST|(|?2hO=UW^ zjm1~XVrLcJPcIWFK0ACqqniyrSaAozx~&4dFeeB`bFz0suEsPmZdd$VbwxcPJZ@Qn zEN@&L`m88r*2`QQ1?r`;jzAIKHD}?6OzikQwi^9V{P{QZ5oK=dw`8bYwT;H24^J%# zO{~e%TB6Ilv3aVdn6(=})qj(>i1~Zdkyavml0q_UEn;?p<*roW6>MPXpR`82-P4|8 zw_`mUl_FWQ9F`zeQ1rzz+puhqWRzI&qpjs0%6_014W8i_$`|Fzc?9WnvR7UiOP4zF zT(QRQ<$Qc@_-K`PTsk4Rv|m!rEK1(EFM*U_n5D6UVVWPN1!cp#t$(m>5)4nO8JXV5 zo!ExC+&R(-D1NRu1#>srQVYWg3+kp8Lh-qG?O&SyjS?2{*rw)c@WIEjt!8hkoYc2u z%6psjyTmN(-2Cu*D0*t4mMdyaU9nH1(>zRDqaJGR10KH=MUUU%WjUlD;%54z#n*+R ze(+n>bY0!nrZ(kW=zlbA%lv=~Pz%+yBBFla#{b3pfTyE3av;wj zzG7O!V;pOHMSJF9#Wq85kkRPr3sDHg!4jrr9%)K8MNq>HZ#Th`4c^9sR9NvjHRE`?h2nt*-X)_6LMpJgts5xeD#U zSrx1XOF(Dl!+)rFzm;lOWZfEkTF=}JL!=gL(78*0g^HX1ldS!Mm$tCHQ7^dJTzT2_*?{F>9k zObD6|SrZnk0KLe@IfTc@l?ZhU86NM%*KOi1Go$2~Wq*+)W`9bXgbFOg)bG?SkPtu& zPE?y55QYTDLaLnrJ?&Z0!3yN-V=K4>FE5W5_x%BSsOP%3&U_6%4w~MXwf&~VcA5#k z;_7NYg0VnNjd8E~VZ=_fs|G1dUM=TX7P%VItFZ?U0vTwznD&yC(Nhz4W?DgVHw^_!|RAi5**Vap4-8Kg?Y(8S`)Jd z6%JZUrcs4}PAq_dL+L~Ka;BXA)#e}8CJajtEPq{AnF%>C%n^o&CoTl%bXAgIq;&qb z-rsavZ#wyy!U=0U&}5_3g?j_h$5Q zaZ7i6aE-VK>UxU>VfT?YOK*7gjYSTVDbbhaaftwNF;tGw_9NJ=QJD7Q(E%QbnPAr) z1b_0ceD&Z79xhz*JHlePP5Chp5#Hl(JmTrkJp}#FyWQU539+gN;&Fd8)ef##Vd<2P zgsViv;K8VSEt$(rPq(-MZ}&+86EQqEgOw1QuhGt9uULcpP|iWqrE`M@W9sXCtCwv? zvLMI7z@nY`EvKZ^#3Z`V^X=Ur*%%y*)_<%dgm?@{qG#SIS5z{c;c=`Sz&bQvKrzE^ z^d1}wAgH%0L>66xqMsc}?>k~Sa%MLwa5|4Ys7Hy38&ey3LlB{r1sObUD>cB%pTJIJ zytqLXce}>DbyYCm+C9onisp7Ilk5Voke7>a9ku@S_34ig*!X-T_eRC=46{ z=eK3495gasVWx~l#$jy}Ek~ptdWN<%S6&p(ZuzP5MsveUQvXf9UVt1rQzxr+?FLzL>c1XYN z_C$Ga+@XL_w2fA0@`S`;9ze$S4z%x;!J>SJ>UZxMKBNq%Lg!FcvjK@oFvIN%4;&LBwOP zoLJJQ%ExUMu8kjfWwoiunx3&t)})9U_~#9?v>uUlbBwsZ+Mc&t+QL$49ov&!NaaCV ziw!_f6bmH4Vf8(WI?zZ1C4U+z4`|OJB%u?fGojsoX$nHBk{V6kwalg6&OlQHRL{=l zXSa52mhyi}t%UAu*HM_2w>7qX&#*oYY%S~STn~Nq;IQl^>6x5dm#u%f)nRJP;wV^7 z=lH%!!A;==j^7cGS`j_cAV-HdR}gk0$M6czKu$HFaF{41dWy1`R(}k)Rtlw_(2~%6 zg77`Y_?~;B`K&#Ne>r4T-)j|sL=q6oyfR#prk~rO;|hYh-wSXVg;kJcuV?UcyQL+f z{sK-KLTs0BG@M;>FP<`JkueM$s--<+n&KJ4S@b0H>I>H$9kgu3zoC5@uRMaE8W2=` zD-dwFK2OljK(Azw)_;Mvjr;WyYYpWsupGlFhZ|N1Isc3zuKYTK!AM^lF|!lUG$0eSTvzA>XzHfLG;HQ)MhT% zLY<}ds35v5i*w2-bdhcdb{0naIDpl(+D5-6QEXbSbiAZ^zI>bT_1&jU4=2HpD+)pO)XJsLC`s%i}!gm9PD^w=Vwa zp-t^RmoGh@nqlsoMvdYs+TIyCooR@R(OFAyM%im-pMQXqUZI|xDEf^3CVM*(Xh|x0 zJVvbXIJfg#4z!TZ>wB(F{xr+0{9wA$q^GE3oX>vGd3zdVUtX#vxcFyoYuxO5`Fm!) zA#Y%OtMdzeV%!)X_b7CS&h*xHVQDr)6ar_QvvdxE0K-TH?z<#L>r(8zml1kx`JpQh zXYw$8dVlNF)%6jzO}!+o-Sm7k0GBXrf{TL(cGzv z{``U_tSRlhXAOIT*lp?yBzm~KKHvFl(^EV1>zMrdDSr<^g2+Ov%N1{O#N5bFFMI5u z(78$Qo1ho;l%>FGRuzucp8NwP>DTkNJ7t*`|EL~ap&!349cIcN7m1-b4kYp%@@^Ot zr+~&n9X40SWIjsmVvpiff`9doI92%X&86xql!cyR*^rid5aqQ1SuWaUJV;>+swZ!bhgF6;@ zgf6o-!yd2Kci42gb?Op)oO#>KPxAx2aitkp2*+(Nkc?V&pyYn=d^4j5gn~(;$=X+f zqD=G1Yv|NxG2gVWQ9#Fz0nrr@3~RiQUBYNZFV6QIPQJWYR;O7pYM4hH3x9#dkrHmH zQCV4+h7cH}IWrAZs!}#cZax{z*zjBw{$d7!l{^2JloPx-lkD&f&=_!CC+ua}H)duM zKa~u9X9bgdw&g^CON6bRUXWB(wL2~TQujd0z3|R5?Q@t(C&Ut>pO_V zMx%-DaI-=OStE|!*=_Ji#aw5~FS^V51JkKF`Z$DpBB|b&Rr_=^eiu3R1h#2a58-rN zMn%f#;-aY>3^>-0$L}nDU%S|fKZ4Xh#i-Kx-|zo|xNalUxW6le5`T#h$A%L#NEIW) z9$X_$hx6Jep;j$gN7-9&R>f#9`Dk9mb7k@6Jf#av%{8%jee{p%Od69(Q6_^|PEbA6 zj^L{K8KacPOR6;i>uMqj&ZyE3MLg=(`Uwc&a9tMMdbfIwtI>BiAsd%j0W{Frg| z5A4l1>MmJM%vJf=yidFd6hkqxBno9fkaj7ahCzDHp^SJs8%=P>7*UPsAa7OaK>Jqx z{o|i)A1m?UrSPy}p^t4rzMfr!Vb=G@^u)4DWp4s~DpE)`D}Nf+Z4i3^n9PoQjE5=# zc#;AIvmdIMd7$YByn))VjK1TK$emyB$SB=MbD6&YwXtL6)M@hm=N{>QW zz$Akc9X<}4J+Fs5yBu^Hq-MUF^`7L4eD1#+jD}vl8Xk{T)U!Po}v z*z-egdox$XBz=Ll=t9Nvbv`B`!4Ew9)^8%N>^|HLI^b;KSf9MAhSOHRh#5ml)eBdO z9vm9hYx+cf8_>R~Lde5uCLVu4`imD4WaN-31?L}4x_>rJiusHsht@kczXu3)OnrDu z!EU77V2h0ns*GQ6;AlSoT;slot}=`O?(wXz)Zxv?qW9fhK$9HpC=+dt@hEfrgQImf zUp6CwA_+V40s>jCWp0I5jW5n66cl980L^N(JS&944LC0oh8cDrm*gwrF6@eMuct?Ww7Tx16{~jed@w= z@Fe;8iL&L~q4GhP0a(SAlL18UcS;kvPGf6|JAco1P904-uSlJ|BoQGEEckS&->H=B z?Vkje*p-<_v5jhrL1p1&UsdfJSk4*}%W6Nj)w<1DW3hXkSaAJ5e!ng5DhRkf&xbtI*5NEGnFQp8TRQ*grNwm`3_l~rMW(@{4@;VS7+|-tzK6pc zc7K~)Gu)FDQ>Z2)=SVWZ5XLe23I2Cof1`0c;~8vq{@aJcT?c&zsqgH<)o}>UL_P_u zV~VEHOQf9P*o%L0ntEefQ&V;a3R<`L*U$;;X?vJo{U%_PNtud;4 zzZM$3Y$4snsat;eG;|{0{gGgqN_%_Sof=-}jJkam`jStr=fN?-ip95X#Q;KXFdbA(4dD6X&tkB0w(Tr9JQhf%*rm?Rb$M^t0nolRgCoF-aAbTT?asdf&ks2JJ7-P^ z<5o%EUNC@ajl*(w@mG@=bCN#@`F~r<=X)J$mqN@OMGvJHxD1tbGnYqcYv>Hoh{uF5 z2}0@V#;EkKEmR5=8*FV;AxLHm*fU?!1-UqXMwb08B=w1H5W|vE>{`bO78;;8cYO4& zom+=AabUCJS+O1^@88nEJB5$@ruVqIPtd|}atj#t$DLVrUkS8vil z>GQ|NX8D*%;;AY;RziXa`cg3+!Ve;VX-3D`XAQg_*pv47W)OSQ9>0RST-m+8h^(pWBSx-{?8RnOP>gcWiYIv?nI;oU z;@8!2cH>I+nr2T+Q^v901eerJ=L}0!twz%=iadWR$%Mn~63g3LDfUc% zx=t~+P%ToHX9piHS%2$dv)Af$I|Ajd5-;e}AsndghJ{MWoJs5};1j3w6G{UdV0@dF1ljRakasn3@ zEw>=;I9xKzbi^7X)I}i&0GCgXA{%i{UPlR1IZEs%kF8Y>Mt|>=K*PBrq9f@AHEJ5W zWD9KDa#xKi%B*N)t7x@*kI1=UCHSYANGv^b^@fQ%6&rnWJaMgl5tLhUjrL9D$ zlX&FRYE~-vCV$rR=lA)`6l=$Pv1bi<;O}WW0#@aINUG`HwbEpnm)J&gi#h;XBNADP ztgpzwH*a?oETQbdEFt9x8i^HC+zOaET3!o3m@}Oz8YSW)R?YIeU7pe{)Vr3V%@;+zqd*S8UF(u$skGac|RF zu5lexE+S8%IlIB_J6Sh~H8)6kFH&mL1x@cg#mE2onpo_E|BcA!ug0=%OV&$8G75*U z2>HXBTu&+jKfS2Y*~(SNNzuzDgc(#2Z|>4=LKBaVPz^aT(L{2^lIhlTN3N6~pz<|8_IUd>9IVLRbD z+tGd_42#Z%Ls#TSMuIW5lLFhy5w&&2oBLDiZ@8;%=K=wm482>=g8gIV9o9Avdm>MU zr@cKKK8qGiJww1mK&%kyDkj3qQf`dEoY8|`n}0Q1IBd6YEl{f$k47Wh>fT*dI;kW7 z`yHI*_SbRqPuKLZ8Ew&R*>4jJ2>bw=`p3$cmm8Fq5cof?hlhOQlzejSR_$GvE^&-BpGwJ$oE9 zv+~fPMJ>TRTuqQ zS)7`ECS7oG*o5dOX7uG^6^`Lt>u4^Z6gmi_M5qWNk$`lpmg%vjL*}kt^^Lf{S+fML5P!fu zSYtliUT0eLW|YCClMxk^ITpuy$*>;Ji*KxU<5CInCQ0?04!Hizc=Z6apfO?PQH`z! zW1*WP_2Q{>4HTQOGLm&&ND`<=^Nb%)9i3fzdQH>3@^Uef5y^J(ILymTINZFSekP1HN`De&&AUIF zNA)kqrtXC%O78@c)lcC^n^wCeI#tvPlG-E?*Dq%>$ZPQ0s6$4Y4E)vSAHb0#OE5}&L(zMiS^{VCI&ZwvoSD|jif?_NwB0T2Fi)4+2C;s^PeW@nt`Si z*+0_c^rl~-05%brSnYBiIjJhL@!5=sJr6Y9QI%mXw#iqm=VGa6d0sb#;iEPt2Co*c zNshmwO1`dI3PB;WC4X8>c#QjAFyndUoWC;w@7R+eUEo5Nr~bi+d8yHeJksMnDniJZ zS0M;MTxolrB0$Oz-{fzP{6>p^JB~SiMfhvH4q#334#g0fk63Z3 zchx$jYB^wi$?xfSD1O2`3(_mmPeQrQFFDRj&|_a!B!;xKVt+5O^PaA_Q}mr5>ORXX zXBM$4x$;`5Ghv^vbXp(QDi*)}(i_wW#|sFU%04j;?0q18W(B4KwOX}X<{W$uiTFwT zi>^dfH8Ge=Q9^=VNDEh2>m;zc9iZh!rHZ{Y+oOcD7TwQnKo;2*k|0qUc_;r6>%sJ% zOMjJ}yxj?NbbqQ~W>4ls6w`#P+7L6v6gCguv%sQyX5H~>p_lY{FLTe4U7ftVu!F5! zgI7FkXmXz~=IO+uGTR|AY)9sb1H-J{0N)&e2OEMw8fo)5r3>Dzi%rvJAVkfFDw7*tEPJ) znQ$Q)Fdv$?YNWm~?Dl)IE~M!KSp8)Ych%=*`HO+bgggEUDClq0dk=I3ItTNT#P^D+ znhDBE8Y#wlE+3JP8fWuno-hLe3KXHcyZ+v|_$p7>4gL*>zmrg+fZ$xcN{}Lo9F=@>k4*wDt zxWHQ;Yev7cZf=4Aw-_>xxKv~fAtVaWEZTeZh=lg98eXCnelA>ch=dS4r|C4(WGPr1 z9rG0kDSJ+7WV%k(GF)oS9% zWGEu6LBc~=^Hq)FoT=K)pKYyuYxcarde{wklRXqy)H-V|daqbTdW&izaC9J*Ly>dg z(tp+AYx^*DgVW9U%lljMmq#?+acv9WD&Z)FO=Z@r#!fdvXVPTa>)mmIna>V({#1_! zs_!>NkGrAPrp`4c?=yWKz9;8B3m7Ax=;-e!nJr#V2UXdfye)f%0dOS!2$vU`{tutP zuE7K{_CZj8Q+%2!+ZY6fIk|SnXK;8(Q9|BDWL^0n5t51b+ky zX_?7cF%1a^%dQ}e(a`p_=|Rc$IZg-ET!wL}@bTvp9etG^(|0D%?Cd;qYfNQ(Dq9x* zd7JRZd;pq15Xs*Mj_mdOXX1U9X)r|bc->QyZa&88wR;YElA3|@t(5FRX#X@*%x4S$ zG912g4>&-{BZqtTZ-<7s#1c1#On>EmeX+yqFx$AdLM=d>|Gq!X^NDD{0PKImfu4Z` z_YRe$}@*hpi2oxugb|8r|&QpwI-fB)35h~O+42VXmBA7$$P zO~^<({e;=atY5M5XV*?s5XIvVSw_+0RMONVPXe`jt+YQMA6x#Ny#8@z zg39qpS<1;zbmmW8S7{!T(J5&0&EekgK2wb!b#&59OaNf8o}{INq<<8d2I4T!F#GT6 zW$j`Ir9NG@-lmt2zSinLvAuCI|m-%UWsJpt9>s-t_rfmr_yRAGYRh}eW$x6pxXQNhwdU@)l4 zlG83c3^~2wc62}&4}UDw9x&a1dY|~8pnh!x`O^zLfHA!b@rCB=Xr>ykTm5zf5%UH( z#fFcv4rTh6E`PPcG>1oDG1dUQS>oYc=8v&n2?Z51K85xPxgRW{<@A3u=^be8VOoo3x9+o0h}gjK*G1wN`0zB>VE-Jg2Ppij4g)1)Vq|1vVIia!QPx*>v3D{Bd?gewEZ|wo zXaA;U{_vOdfK|O+NZ>^Ms@%X($;k8&arfSvC!(0z~wvj3N9r$7Yn0q{kC^Zb9ua@b$n z64+72p%*^y4%svA>P9^9AWvWgp76aIq$7Y(5w&M(0CcdW0)*fkyERU4v$z}S|1b@H zm^ypFe}~Z8*wT32AyZMLjX6eJTJzwnWQ?IN!(Dfd$A7_h=X_!o>lkQl6MktA*!jO> z_wS1Ji~o=Q{3XBrL8P*x{|D}v<=H_b3WW*iip6!fheSjcymbH3 zXCaUt0DlJ%|Nl;?a{1mE3p1 z_eUmToB*P4&{WIFb*sOjIpvY!D0DwON(w+d1Aj&SJMFLg|95Xt+<1tCp)G=1>b-gu zjv9g257R`olCPdf_+KbE$$-Occ$rHehQEEm0UUjg0F3}8G@$0z)^&u*JTj`*Cim9Y zt&Gi!^!*&^M^HWk*7%!AzdQmI=l|C{Ld4ou@H0v9m$$Ejo#11R7{bezPHaGkfG{53lue6aoPtyntC*|5qK}2L^ys0!{ussQj-Y18M-$&HWG_IagZR zHmItYf4)t%XQlwWCCy5V^;)#n7A+;sI)6bf-avu^m75E|p+B0K7aA5bJDI!ama_PO zgXV{&moQNXSQK_)5qd3_F!EWD2=)X~V&4z4<;krMGC9rDZ7pR>{*3_3VBD zc+Vy^-L|m^^S@%QO`PKJHSpq4*zP}=^OA8~zXJQSAP18Ud(+`Miw23LoO_>|M>aM+ ztFoW&!_O{m)8k^mT2J4_YpQ44>3^4p>okbe)mN%_+t2sKwcL1~uPULnD%2{LzdSeF z;KnX++NKR&Gp@~PSDUw)$7~m!Kt8ZGe^IA0>7KhjDMr6&Rn;h;vHq}VbjE+4s*lB5 zwy}o{+n2cVAXyyvN__=CMvPbr0rBv9vz-`o3bcx=QsGxlA?0+)kEU8|`k{1@)Db13l|2@e)j8oHi-fSH_E`r@AUs zOnRiTRy@Q)*F>L+iXxi0-DJHlxCZ`F2oVuXWAe)2W4AI=U^TonjgR zGeseJ#c0xCK0ntxDC38{1Y^YA3|c|j?QO5PuKZkwgrJ+^UE9cs@8r@SR;DmRv{I4% zH0sEpn9*6#@hY{OM}Ozo=1jZU8bL~hCOznwfeS+m9QC@SxAC&Tv}UX~m*d7;752uS zHuORjx=U=n1y}V5+-+KgaY5Abcq z;C~O>l^W*{R6Ps;4}hT0!; zzw-4}7d3A4b1&Qm3zZ))YOdaGG50?}8cL~$)5g>zdDy_#o}1$iJ>Xu;HYI?7%wQk( zM##TjKfkK5h-#}A=E^h`$Ut}{Ja9k#j=U`wM+k#!*q!uiZm+`;FRs^JJnjgz>r!L8 znBpO^{;@XuXMb_Wqb%K6otYB)W5|F*aUlvZlv23a&8g9ItTjx;S$b1N*r>6iqosqW zq1g6x3DsAXs3lu_0|9&I;gmtg=WgMchS`V>x=Dr8T+mO0>&(+!sMF4bsbdy$6xjX7y`t`%&u*6p=YSb0kf>9DDt}rTD#a=3@#!g}9WBo|`I9&_WvXcDDCx6kYRM?{ekuvJASh>PIAyBaB{by| znSYlaz}1ZM%QEsp_-|?a)f{g*xDaV>1`I&223@=cid;tfTiXUbLL7ziBqBw^zd=W< zzSVLyUTf~;6>42W1gD3|DF7>m@VvkOYX9E01K|P=zlVYHffC80QAsXjk$(298(b?B zj!c@iG<|g&Y&Pe?JN?tz{^N4wI~G~Bjen0cmuX|N`c}n#wKLf#W0ajZMw^fTntfrN z%p+GY{XmaaivA*2`TuW( z9;zSLvkUwg6UcA+=~uB|Cuvvn|1X5u73R2E+xH6dD+#kXMf9Qyct#}Z@V3HB^ba(uG=wv56Wzrb6*B|9j z=R)esu1;meht^&Em|vN8cYUHTqoseFmQL{2@sKl{1eC-MB$h1Z!BHzW7GA*}NM|p34m8(-{YqWycI&_Su>bM(J=hV#>-w7k(sWI`iOkpmJpZ|hHqK{%#_MB&gcm4g8!?XFa)^J3p=u9srkX8L z{M*vY%4-RJ#0vZCt zeL82QK8tF7>OA?XljG?K4^&l7-7SiT5g(-vLa@A?&u&& zZAK_XyDah@!y5*!mwn=2WA9wKN&;+RIoYmhh#-XIe|G28~jX-Tj^v%B_StZf^9D2Qvd$|4?yt0g&YkA2(!`$ ztR(pLQWPpXc$BEJtg=)Y!vU07SQ-PZQd%qK5Va4H#8IYgk56lykx^2p2*pmBCD%DRvYupo~`pDQ8!!OOF2S~yov z#$y@eN2xB^%6NG^22LMj>g?aQApGthKd+xozyE);y}r-aC-ranFK?dSgalikIoIH^ zQ);ZL=9DtMIKge2(o%`Ys2H6WJUomn+a}h8c7-vd;pfvChF}LRoQ;*1e!9AN%k5#M zQHc9@37R?)_?YSHkFIMVTgz1CYf463eU!@iO3BKTnGN1tzD5BCgC|euYQq}4@#qU@ z)x3WS76Z{JQ+X~80O@lLy_%vmG6oK!sji&7cLBaMboY6M0AO+@)&s@WZsA7nGzF-p zdL-viQ&AKE6e?m?a6i!;J%)}{^9BFM!D@lDRvgR9=xTmIax@-EXpR&Faw?&iKfXC1|uqas|gg|cRM2pX-iDVQiFL)`f;1dLbA7hd|}q_ zwPO1%3jKQbg4)E;0t`g^bjQfU^et=cz%RI$qT+gQcq=x6TF1jUxUrjjGHtj>7$X3~*{}6GH%BFw^ zThZ^?eI#yWk$(*|p2?qk(Zqr@Ll^&b#hzJ#NNEm_&bGz$q8+ zH@*sOBij=4(5QmP?VGsGr(f^hCB)6pURz!yZW<7joUAbD1uM~lGmN9);Fx~UW~Q=? zcZi^0XW^hN@r%zJ-24FP>58QavgHb5$9{)L|FDlb$TtC_|p=Kp436<|}g z4t--|fPSonwvXwC^+$wl(((PXt;im~1%<9~5dgn4zRuX|y9L%iT7ZRxza)D>TaA@= z!Ka(641?aw*kHzD6os3*$@JrmI_68YGyt>o0pm8UmA^!p-Ixy8L*ajb_ijM6fKe{+ zL45OQZw*b)Rx;Q_W+tpZe9t$P+p(=vViDY@cJzoHtotd>{ymJbl|12mlmalY*^rKZ zsqqjauwT0CU){X8*sS<=KWyQsq25k!DZkPle*DQjyw{u)dJ}{8aWM!$g&5dwKR*v> z@mcKAZ8Y&+(r=#sBJ6*YPlnxV`3ZjRj0NSJ&|g(HZcqN!rSXx?KZL=#eWh=JKd|xd z+IYO}uVDY#aa`X&_p!0b_y6$Ou@UE%m~?gef)A4amFs86U-A|71b;LYb5(gX27`A@ zYTjM?Gu-;=!QbV6Ai1qYryEPWt^5g%4k9PChfh<1HE}}8(`0`ibDEE-YRZ{3UP_wV zN0QHuu-KI1o|TS{yjsv;GAk!b%Z=+dE9iTZet19U&!P%<1T6o01MKblJuZPAmDxU@ zU7^2_-upcpezfP)h{pT97MBNa=&Dk6EqfBTHlDPSmlC_3a#%4Wq)hLrm3Si!a*ult zWJ-R%?RLH>b*fhwbUHS8_K!jM zd7-+B)QkFXj%`iM4#C3rd(2J?dg5%+%se~G6Nlery)b`rJ>HgQ7EFJfdZ-)ReD90^ z{${6{x2Kn{E`AvYWmaSjAHo_NP8F^0R6Te1;!cdW1ifp2z&1YMi{f*6I92Mxjvt7! zou6|HQ8IpKN1q$)NXIL+UIKm?;0gAe?OuGm(0$*v=F^`g?hzc3`u0+zK zS@ll0+jZ)-d$eziP><@*v*676!$Ih8_>a(*Pqu%~(7~R0P*4WP!qV0=_gF`aeCcf^ zQaZwZU`4;s2_A(3ExUYV0e0&($Wc4JRY)wg2*Y(>s9km=3hShUrIJ-E2h~#LxKus7 zue>{}RL+jyJy~SW=Kx`G5Lk`FX{a??MBuX_B|b~9$+0iNh3O?afd$ry^}Y2?%1f6F zBmRHl6SK#D@92wJogN+0&XU_)9`L9m+=Zl?NPN-3WTcJi<3Lcm0ke*|duI^wI0Uhb zVhX~~dzsKw*M$bV!@SUds4z4j&k0R0;4XOZRk1mMf8RYeVCwgX&EZ$YW>pFnJX2Su zV(0v6Xy$Bhqqffm0o81@1wbs8R`|Jp<`aLjxiu&Lnd9uDu1~t?O+^2S{fLscsD$o; z>>n=`)p4TU(pu$BiqJ)L-azI&Dz7E-YiXQGluVkeCdo>Q6q9#DdaNWz9&OhqMp0T6 zB*o`NLOeh)kVZjycbp527Z5{6$0jFGlS_&?3#Le)4+TBKlEWJS4lrBk9BoT{>$ZQr zT;sF(qXWH^La9#))s$G_LM+6XM`SIugj$JML2X{;KpzsZg66^uw3)zkDgcg$$~!p7 z7}-{0NO}deX|fhwg7>ZwD`=8G>-7SWj2L0pJnF#)pq>Tl zSft{kJwDIUH7A3|wHOEJ!Cc6uYR-R!=5UKc*0(^XJeS!p{3H;d6j*0ezK3;oLPMfq z@T{^I@yHMWy&<%Z<3PSunzBq-QkrN=C9KAXtjZK8Mx6M4-%WUNqn{Kp7@`xP02IxH z9=CbS@Gu)tge5dXy;zZIUK=e*9yZt6geOIx6k8S)C_t|gVS{>0hXof4z-fO*Cj2SZ zzMe$|8`g5EXu~TQm2(it7M!Bk^)?YJ*M@w06&SvOWW}E^qFQ>(w;O((<6JwlUBS9#-5|c4+Q_D+o+9LQOUT3|r8j zGg?j+3_=5Hip8b+reC6_LY%*V9^4C(w`d=(y<9KKNRM5Fw2W!-#OO`?`s$6PHv1rgDYdOVQQ{PiHaTLvWz}54Fpq0aI)Q_?qUuHs2UM{0}HTb zqS|R{;AQPo?Y{PJ+ReF{IusXi6j?v$4j1lhmQR{Hoj(2$={SP`C1pByS7?!4+@FWXXiTg6$CWRcmyHIUjfY{MOPCe$4^i1$9Q-5)<3-)M(@ICaOGN6f62Ax6?(+C9xB{@KC zaOMWO%`k7li^Yt-bPo|yS4}l!L!YuT(yMGNxYJ5XLO*{3>9~B{x4r!1qT5MYO``7# zuQSi~KG60YAma-#kTWD=*M-h*PxUfg)lNbe3O!ZKjJOZpC~LQxhHi11ZSa=7S(p(U z8a&-1*&YvO4z;4SOAmTtwJiX<$<$fyz7U>A3>r9S%!TT+=Iz~ThxMhk#O4yrEY3d} zSx)Y;lmUNuoQ;%SPunmQhVSz$j_58x8rjC~DuKkHLKR;Xl-(edIL=ApHHkCZ=~_+u z?>lKe8YpqOs+#lqeA(|a8javWAu|XJ0>&3*0Wt!UAY2J^!vfH2Z7_43TBWx#rg$Qj z*vdm@Sim&p$wOdi3JMFUg^^02c43|f-5vQOC?0!g=uR){>_ZjF9Y)MMuu+R)|;;1YAAUDt)@%lo^@ z%Z@;3U7}5}z_l%$p^DK{_?xm=3uUx;T2*c>R^5y)7`Kb0vBqhGzmQv-A&DUghF+jp z&J$}sP4SF*nTQpu?!~G_wG{UDbuNkmt?hpq7RU5&kHi+H=c)F_y$@{a4+dT$<-)2F z3(=3a(Y}TIPqX{J1{UzJcXC6CO7a>KWBAmWbl0O@Sxj+r*pN*_p=h$29<5_k}A#EPT(=($={CDI4!0{P;bx%&df78x0rLW%0jbve!I1(#UsvefQ7xh-r zacNM|fT?$PyP6$34Q6|Vr$N;#J`I1X8bA#SdcL!1b`fPA_B~Dwrtg8uo^(qqtFDKv zeY(Ax7G8tR@6c;d*~gc)X3MaiXzPc!AbTgWgR=cJs_gh5JMbVnw!;BQ`^o#9K;IXe*AYwMZzjsgeFaV?hsGdGWW$}1<+L17S6c6q$yC518RU-8p|r+7IZBPNI}ST!3|`x zs+`Wu+c7%~EoH4&*0?a1mSuC?n4Wmkq%|GGb-@gSiY_5#P#ZR_OIQ>P@0d9kGe93u zuu=j3YNn*NPRfm`nTZLxzrTMcX)W@S0efN@XgX&wevK&U^-fsIseoVO{rD|8uC->u!i`}%>7iG0 zV_bCO+rN^YkYrq^OSna>&_vQZh~LMD>hS%DoT2rO_+30YB%gc|dM$rHh!5j8WbD#u zRrk^DYeLo#w*jto-oyu}lrl?4WXv9UkiO38yY%oaIpLaRR_bNa8y2!+LkxNt;36lV z;3%icx?HNIc_>HZW_*b`7uKp|I4osG%R(AU&abbp;JB26f!Ekp7#cJkA=dw*v)JJ> znVfeopy>CtcJfR+pm?{*S_5z{1ppX8=fAf(i31ow1pq)mzrVLp$pfbmxBc1!nJxtY zIY7q0x0VS6IY0#f7C`C0x6ergni02dTLgSA1pp90@4vTiVhLdmw@a7=uQ3GxC_vZ0 zxBAHhpAokxvZ451#Uj*ZU(>yL5h0Eh;)lRG8inbzS)0yZbh^n zd%<&ZLYygalO7Kl>Hs+8RC0UQZI15NMnQsIa^lxQ@%M%7e=M~txLzl_(zPUaAxH$I zQYm=B$>eTj;|-IMxoO}(huS1RVusL19!n}oZnKroQRYgH?Z`iQi5)rsE1REL)o!h$ z)1mOh_AQ%ejm%n-ytCjBnpPvsZJgw`OMJ(;Cd78ybm4)t(ojV05|*|&a1Y)aPz)Y= zcg;6z+!8)Vf1b!llZkwo{Iy3DP4YVeG7fnDA;AOIvDVh;80Ec`$Wv$t#O)vecJ@61 z;~3yp=Cw0NWejZ;f-*s3A0kCP2h4Ce9XKPWrDfad3FkG3+dPP-$b3V+Sy$WwSCz(a zPX{36n(StM3nN+8qw${q=b2`iqPHM^@GMF) zHph;Kf44;)2ORrjpuW*>ZuI><65q*a6{O!BC(UigKRb0e=ms;$|v#TrWK2<2W21K^Q5Ji`MdK%Ooqx5ad&YQy4hQY5xEHGZU`!%<%t6%V}aDY8KV~pdrGg_==Yj)Fau7qhE zVnvQJALrb8z-^YQ))iQ_ZYl2GGPf6`Il z61JP#V(9`R0d2H&S&lD|Ge2#Z8q>JxxpRr#tM^=NEt+Lg*(nW9{m>=r=>GO|= zmEW6RsWrZ_%2u&J*^$DbB@4p(f98F6X3b?M>(>}rwEXWLDWqUXg^RYm7gy!$wH+Pr z9bdXn7OJ$^sM)x6KOt)eFj>DDm3ddqq5U>3=sjaNvGM&Db32_{m)K};ja9{wxX;2* zu3c~^&ka}gOQWXdJ~rhz@p#ps<)Kltnq6v8;yrOdrt1^4d@*j|-=)6)e{Id=j)xYt zX}Nww;mMY7vQ61coy%8b+B?k;mm2=5ds~wp-UV0xm>${m<2}Q^`QyA?(#5o{6VvO; zv{J1GHi>m+DIEG}`K1{xYC4WJLr){c;>#W0dupvC+t{!Fo>4LTp6JqTM;z#Uf7Gd+ z#pg}j{8^EObe716Uvm4de@;1-`1HcnDeI>ExN1=S5C7z&9t^Q8vApxi&68K39&jI} zJZp=vk2{xGI;8T>4tv8-4M13?bctS>Yc8b3#|5v9%P~I7u8w8BA0>uwuQ%wOY0rA@ zWcDD{o5iXf6QMuT|3fTE{qnr zy*1``_Ab#Obp45y$GS&}?F*LJCD)Ex|98nty?&b0Y;T7d;m<>yImeCN@yo(avyW2v zwko0BmhCJ2&8FSkkOg(imoK`bc4C!#MAnfB&&p0{eyH#G#OCG3{?5-t8f~xVWG_YM z9C-Kg!fTTgTAn5Mf9x5t?wv+oZ8(oTxqi0v_)Hf|7u{Q_`oiqhPMkS^Vfg8fmsIJ# zrVo5juWr&2{^io;W$a1a-4ze4$Hs_h!ocSB2P!+#~K!==|tF z_x9-e<=c^+-{H5KZ7P2I&&i`Qoh&}#Q{h69bsfw7BJRIYf6bCGrd%6lteCY-tBJX` z*mi#UU1Z#aUgg$*@bl$~M+%N$x*qNn`a6w;O#5Q(S9Pq+@bylU3nNDr~_0Rg5 z*IMpoFTA0lf3PF0NbZ~KFLdo*(KTk?l|I?GAKY7bP=OL#rxmBp^!_LQOS^UX$+az( z*b4o9bwihEylK*cc_$j|-86kKa{kLlQ?t+g?&7X>{l2sPQ*v4P`~BA}>AX+K^~0H4 z`!D7B`S0b`hi|WYvC_%anJu34F|l#-(KV|Jk0npuf8_6Uo*s8~1yk#YwcM%!lO|!c zx(--*r1F@0Qk}b*Ck&i(wSS>iOL{Eq{CH&Np`+#}tZ9Lr`7?W!CEG8pESsz4fK8uO z{%Y&_&TSeVzWHg9<)c5jRkZ4j^W&?#9O(PjmewdcVv*>=}0xUWb4hus^m zu2p>Hf4V_^Iwm}-<*reyMP^$T@2Q?;BFF6^)?}7Pk3KT;LQSct=XuDDiVp_XJYh%Y zTv+&b_DyH9o!QjV6@GKd(J$HzwcR_L<#~wh#I>?-lyQw zWwyBX8~S!{F+EeRzkc3a1;fXz+MfO5q9PH)+xIDXFR}53842I_oRc?a_lRE_N>$4J zda%vvZ*tuDZhY-2)#$9p3nqlcd{U@yt_d^Su6<}b*9_lv+?%=noK2JFHeNQk+0%K= zf0l7G!egnwcAbh@Tg-LyMEnPLHmtbtZs%hCJ}Gi@aP6qdtIJ~N_qWM2c;2Jz*QZT9 zwQ1nM86z%kJ^Ii3FV`LvXXIS<-RLfRDxTPMNnDxp{olro89bzRn~B804Of;w{GwUn zy9>`X8rwYa@cF}Y##58doaoiHY!~Wkf2WAAoD*`ne(Zf~_g9sRo$o(2am4lQd8XW0 zcH-o&xp;^A2%q%ad?9Jmtherhqj5?x{D}Y zX2kxl#H}CI`SZ&Y+3Kv`n;k3rkZC}%IXR2>|Kv`# zTlWV}ztLgTyXR_Ef42Wa=a$chyN~y+kmwxdxc&3;L*d@e3!PIgoImz$yJE$J>>E1l z;#tc(#Y+En_QJc1&p$2J?5OADf5i6}7PFO|%nIA`SN^G@a*zHRj2(DUCGh^#Vh zUe=sQDRk4-&D(ko8(eBDa{To1edmWA^0sk^(UCVI#x-{1@7J@Bf3=Ti>fv8^IP_hX z)tBzIj~EvfH?HT<9ZfeLT70wXy5YS)&Qv?BQln?x%76FS*7vj53<~*hTx|=kz*59$GQFXzQz| z^OYHID_Qf)(Z3fH#!yc-53hNr#~*z&$Ij~9d0)XFSKQmSe(xVg^DTWef7;Snp%sNY zi`vI*zBwc2Vz=pceiZW*UvMQ`Qs>PdKAZYHB>Udc-`h@1e>>6d*0B|%8|6H1zj2}E zkUHO93~f>2&Lm6G~0n1InD{^s^vX#wa|t#hi`M^Pwq-sIjIFz#(o>yzUgAr zrBPW!^2M|u3vYc|>p`PmRz_E9lDqj|rwbRF`~VyBP3^3IEG+G;J9>85z|~6|)z4Kp zDSn8fZ`J6ze?6Nm4qY+nLez$%t`mdj=NehE>A2K7F;*{-ueZ54%@%YlQ<-w~nAsbg0OD zVEH&{MV*k4*gt0Um^q`Wdsn}+iMiQMedql=|F2Sc?k>)M+_temt>Raw%p1RN--NFH zsC_r)e_frnFmwWSaC5d|jWdT_DpmK;x+bLtuPaq>?3U?t(=pWxf6)2Z4#$xryYf@< z?<}Zyapu>V$LFoTC-&J#iKT}3b5?!f80NhZCP(Xk;Go7a@ZplC6xQkRUm)O zZd*1MjrNAmJ-Duk82x9VKh~Z;S>R6Dabu2ryuHcj!Fj*kRO9ZZJ>i9aEp`0wc1OmJ zzVOg?zVm3lV}oVgo{nJ4_bPBJPlw3MxiXzTI%dV6{c|3ClI5Q}*Qb!v@7+8V->hP_ zf7128MfYxcxU1FQ_Eysthg{73$CFl{pWjox!O-eCvwyaB3ijnYjrLbZ_x(P*!l`W8 zN@r%TOgr2tf#@|?u2{cw>HT-VA)@oVLqt>i!dvIuQ{v|lJ8~@zsl~T^mnkle>DO~X z{jPs_cean9<9;rH7aV%+^rZUZ`$sRze{-pLNKDs&>KvO8=1a%X3aE7_(je@#abp_SKgjG(oH=|&&V?T@e~RY2b)m@2y-deKQbeVW;g3%)x%psB z_HEH~YaXvH@4P+zdC1bXRhRd$f0t>u)l#X7ZCZu8qu!~roq5)-(f#XnAJ?TLKHFJm zXZ``%itNaFeaes?cKSgp)tdPbxpMe~{(m>4p++xzbAQ6R++9*=LYv$)V7_iBGxD@#(|EkAc|o%Hm6g6-nU zVf&V?iT4bYqL#jUzqFDo#d{c6tVU~(R-C2PS-bX76?vp*!W#7Bli5b^VA#wXs}C#CC;UOq z(z|bM1kh+70TA*$Wbm+UEl=kA>9Y}+kGGuI^1E@DydfofKiPBa!n}_Ce+8$?PuARf zR<6CU=~9K~e<5Rk>;FZ|^RWRv&BBer4?T^M{I`JMeI3 z=R*gsG+#C7PE_CPMIyy9joMXdl7AD?Cp@#BepyD~3Z;^MYP z${oI8fA@6#u<|2e#-o+ZsQ!Lol5W3V_54LFyHLCE&+AvqqpChBZR2y4eo&+Mm>=eTai4xD zMdj>x{Y=F{{N{Cc*ChO1_r12vaUt!4=1i2=&-Fv_OFWEq4O19l)Yw$ej&3?v{==#WZl@uIr>`GeAR?n zf8j{K&0iLd+fpX7c+S4@3v)mIpl_x4{I!So?QmdGz13@0>>h-KymK~l$h6Pm|7pMS ze<9zmp(R}3mZe6Vj7c$3I-tykgsy{@QeK(VH z?ZNN5{l5QZ*;7@~9cH??ncOI&|B|Hk2c z+jhXHtcQQeQmu%k?#|Ne?Om5Aw|Y9Nf01jh*$+y7l+Zd?WNThve$MODB17PHo0f@4jT{GsOxz^w_fhdxpPU;91R{F zEmDHXR$%p?r{Wra^E_lrzP?rS&AxuYS)}spKA8%y+`?ulRD0Dmm;2-iV1ebToj7e+ z`=o3mJ9c@$?U~L4_g2r&?r1gbe;)Z4|M#cs4=j4pl3e}6;~M9)uDK%T`1JUWV#8Xx zzv$F^$g}wiuYK5PqO_9gSGwiugMWUoRs72OJmlMfzoA#}OxU%r`_FTF^d8Z(ajU~0{&{It=kfFQ#@kQ- z5<6o{ti>~HM%Qg(uc|w4luqn@>-f*sduI#n$^UM#Pj4MNH(~mg2gQqDoau1kGn{Rn zhYX79zb|Y}$Ibl~^xWR0e@{pQgnYWT5f)yfnETLY{Xz=frVjQ9Yj%00n_5=&RPQBS zlRgg5USx6RGS5T$y3n0>7hU-A@yXG)B}pfqhitCAQSK?>9k10QS5*1FNd5Vp=Sip6 z+Db)Uip(~%ZiMsmbKjI&w&H2hAqTm*Sg}!5nP-2jJXK_Aw4Hw-k4N-%z@V5ck1B$Y2wtPn@iuA9XYf4G|$|qvx!Z|4(ODB z_CKG0xh^IFz4YUjuE)MDFk^P9rLiZ+O!F*W6!pWd5m$Dcxbh_5mHWMuazF0gY3!GM zUFE;dRB`B??PVuqe`;J|<-qpU>sGI`tNf9f<)7rceLt2;+!DR;;hE#-uUF|bal;i$ z)+|F4Dlw&g&DLhhKmUy2+<$Hd3+C;cd*XzfVGl-K9xZ%Oqgg-u!b$@N6q8of=<~rT zPnovZ=bM@SS(@}t_<3>9gB2EiS@`75m6g^^;w~PQy~CmP-#?1!bEQV)ciV3dsm<4Z_aS=a+17^( zFWwt*;p0EY=V(5zOxam%(~o}n_1u7-+xiu5SSb8deB_Y7fR)m1*z%L}7kl%LIQ#y+ z`E#mlUXk;+e|#T&JF?I_l^bOF^UL2d&v;a@|GxX368q20k#O*d?PBG&RX4tWw$2yv z?tyvo);$!rSo&c;wW83KZ~i*F?7`sWJ(?_RaKyWCnY&W+rKgKU_AJw~e^gTTmd#zR zJl*e}eRuili&N+%dE?{r)5b3CJ9cfXw`8MClAN3D{*Fn!&`;chwnA{;{9SppY2=Uy#M{@A(i899pC$TN!#3W&qEF$=~MdP$jk$h zs#Scjf25vO*!>ULoiE$S%@!Q`W2Xt6wdmeTms@9FT_#@odfHcoCbem7&FwBx&$VlJ znWo$V>DR6Dg_z#OHrAfeKJm%l;g9MiI_*X0Z|c%|{mjB4*(QDSQRB&2;q%KYj0~MB z9%yaP{`kvDp6qu{wEdJVRqt0JfA%0-G4#&4-Rtg`tC-Yt-=2RGXE{9= zV*9zjU3g;Xj`xaU`Ae@m_we-okGg)*`NolPLym;+W?CmCWx z^v{M1-6Z={!UwauY(0GGqU+Au9w$EfW74I9e;qC|_(Zk%i^b;VE3&ZM+wLZA@1#E5xCt}X zpq0l|8vdX+zG!_8a{v4W(Mv8w^{CY1%&uOu=FQtQulTnot)rJuEiw47A17AnbF_V< z$$OT^f4`z(sAu@YqX|Dd@xmw0jQPDt{zD;6ziQX^zyeRPwUHx|mRw(Qf8O|wbC2Aa9ZOuJ?*|(9G zn)iEtsYaWxIs{G8_`rbck6Ua$J+g$*)Y8~f`rVyvavg0y zb4+icblwm4J)FN}QNs(hN=$heneFiM?e7h**=gPVq3`mE-%SnQe{ix6*{@j7xkp3F zZrIwpa*y3-=3yOLJztgouNtj>ulBUykBiC{|6xPEtf$J(JhL-<%l+Nr&qiMf-PX5U zrBY+6x4u7}JJRN_J2)M??LzCKzhxPar|b^}q?>a-D6(zi)5T=>q{{i{Wm(FdJN z{rcD3WeEds^{qDXe|r4(e510Q9lXd^u13j_Xh{<2BZ#(42J+WC9 z4Eic_ma})q+_it(f!%2TeEH=Qm6~n)xP5w5ET-lR~kbQTxLB zR*CbL!BkH4Aq=EFS~$~?K_nYv)@E%fezeq;Kceb3r|e@&Nb`@1dvvv8SGmzR9F zINM&T|FIU&exKT+{*=EeClw57mVI8|cFih{EWds1_KtbuJ{}@`_G{OCo9jp9yeK?9 zcF{eg#-4MT^Zq{Qr^NVSnX2|XJ9NHv&V)^)vU^M9zGXYyL8LHx=-CtS8(4i zsVx5;fj?Q*{_cR2S1(`eOt)!#>-zaz-+xhI=joNlM#gb1wy(RKXX5>&g5ibjNhM;p zm;2y&-S}Az_kBCF-Px)0&)%yryu;(alTKxdIk>IC(V@rB+J}r;R^Ywb1qUoT;C?>3-mSQADvoKtv1zM^dy~%P zBOZ;(zW8$Mk@wL3A9NqQb>oy-T_1Icab)Ruuwwmp_8q^Lwc*3Zt>hXL_cbAZs+KSN zX(aGvM=z&0D_L>1=H5+1vYDZ&?&` zP;^kXoGYcvx66rtu8ZMr?Wq%X^TR^ajN9F_PhM8^ljQ@d6@)2>-Le?M9^GbT%)i=XY9DDOQyP(s2)*0s9Xsh8dYDN^|t!PM^&0;9p-At;U@C_&-`0$xWkg2qEE$m_YCRI#5I9NTJT_}+mC!hw+s51MHczi^b}3G>hlU|l zLOGWZWs8%<&@h>^yQM0jc8-_q?$EH#U8{sf+w79t<8(;y`#zyIAVq-R@z^|e3EWK_ zVMW0E`XJg3{ur&2;*Q%$qD6Gtoh~UF-m_DdZGz3_wm85IyDg$uutkG^f5BD29ou-X zTLd3ZmA81D9xfW*xagI-iJS*q5`N?=p@K^S--+Eg54Zq^pj1^9sfsdfQ7Vie!!W{% z;&dnQd)^q)kQ@LHeDhFF@PMTNFWBHmA*+OfRdRT=&(6Tl&bcBbPd87m82B5_{0kHs z_(_UIQtuet2j~Af#c~Dze=r1M?8B&X@I%0r2XD=JJyA}Vav`!`*M3#hPl2;K4dTHy z*xc#%Kt#Eu7?%_!IYh~2^H?~$4L~f~5@i!CQqLH>P2hOvt0@b^jD0>;E^TEIsgy;t z29>2-4-=Gykp@2unZB}6ow696vasOFs_%5TC7>9%8^n5@mKdkYf913Y>Z@LdvPrz^ zLxmc|p(^LslVEqc!S>?Q6P{X6j8Q!?X6gww2s%|RxSnX8p3vZW`j~@{6x_h0EjGIy ze9gJt-e~Z2alYLhY|!8_i;-OJ7_ei4E!_P|T0>ae&&e?x++tsU6-c;&RR+jt2)S&|1T%3|ldBIoeffF30dyyCFE;zNco z#vU@CT-xeFQ>%+IsxHb*buq>sGN0TF)CFu28+bmX6lbwdbB#Db0`>yu22YDa+4_K5 zz+3p9Ebyufe(n&V+2M3RLxZ!}9B_r8nV}d>$}arS_9C`xw8|2cejlmg<>PGC)T^Eh?y zWOoWZy2XK=f6V{EQlJy1QW;j^P}|9&$(UwTLA5YUaA<0B=|i(hsKe>e;SyC}0B;xJ z+aDA&R@pOTZ=}tm!Y|p041@%)J%)oyg1^l1b}8jT=r4=E_ydCf2rGqA25=XUOR;{O z$HNIx;5Y)m)+f~Kva9Rd59re7kh%#@QQOj-(a|=Ke@@$hPvBRYyO$fFG5{gBr<-V# zWxsfu*wY>Z^?Rqw7HM;E_HHSE0MPBU$0Y+~;H6~m2p)dH>xKwaUJPXgnc)RmrU{uK zNRg0nloVJTqdANtQBp!NfuIBuJ|=;Sr+5$bja9|KWm2Eh&~|RgWkEyx>0ll>Z?FJ5 zT6vx7e{KYRUy2SqsXpV(2nsL*C}2p`5DI*987MFp1xVT`zym1o`SZXzhJL)@ReWTh zyKHfT-N7grkf2~7tq5x5C<@4BfWb5{)OC5Hz&_tbh~mJ3nl>ni04T!kcBjQ{b9j6{ zfd6Fo`nCz*-vPeFAVyHu07fuF8%97b10$wkf26h3)yq=XE4k8!2N{4zLytuQXNRPC zkWx>{CD;Hsz`IKd>nCh|cRbr)^mh_L5CjpSszDGtR4ZDo*jugIC01)UI_b&~JGP>z zJzAqiX;q8F9#wmF*j3tUQEk;$QHq98JZh(c!TcibxOspEOtYyosccZC7l)s{nPTM{K*nXv+?2@M*M{C`bW4999 z_`#v8D+XY<6g%s|`auDf7jD_)PDoK2KHd;ZAT^G1a8zHnoPV`QM$N+qI*f(xcr$AB zRYy12cNI3_vcjV3=OWYcZs$hN+9HFCYd_#LJAr7+`Gv)#fgPs@#Rwy7 z96nDP!z|4lAV-87JwXPuIgmN0Ip3wntbUE(yU8{%E$zF(-)d|$zj|agkE#9etzm+L z6qb6vNH@N|*}mB3x9og8g3Q11vIF7!T(Rm_)zq5RV64|swosRo^j(0rjQ>~GMi6ns z+EqLiMahY}rM6F)*LN*?e2cg`gX4i^x)F7Q)I#{OM(zY?XeGTn{Q1a6T^Kn=F!CQG zL((Ipbz}8gaziht^NMR$%j6L3{_Vg?hw$yS*%ZBn=}J@YuESh_q@&f*L(f4{YUp-kZr*clFI}k= zak(&J_%U!~K0hVtMYxZ=Z)r?{Yb}d~uoaNh@p|}#lmhSFZj)ghV9x1n_lKl|V@Q$I zPw&E&Mid@a@c%Kqemc@V)1t)2f4)^L)DQGO5;pR)x96#273&ktfh>iH)2miHwi&Hz zNt&hARfQdBR3?yn3@<>_G;Ac8(-4x5|M*Ugbj!cb1@NhKajPYveXAh)NcHRYuZd=1 zd4-s!w)Q+P$Dd!HZ`dF)O6(`3iF;n#6gu}9&bB+Qup7IOHo&0yXJNZjikfjeU(Aaf ziiz^L8(B;2Ey8k5wW<*_8l&lTf-!3RGjf?gp)rCWUBj?(!|Yg%Yi$<*yTkS;VBB)a zZeE5i`y2elO7etP(65a?--34Sfs+wu?^ib2U7EbF$f}9W_(Eb2DbKd5I5+2V@6u-n zHj*nKK)ad#)*h{p*5484oc%62)KqGohH}r-4}RjJJHpU-fg!V9$>sIBK+Q<`&>oSq zH2!vGd&X{smvF&u0#VYHm+jyPm}uunpnUs0N|DMja({|w3I5C;ShLdrdK+>}YbpJ+ zy|x=Wb)~L^{*ZJ!9R^Rn8+?WDS5%%>72O+SS*!t)zK!mQfpiz6Wx1q=)NnG^S?Em- zUv=|}_)pyPEjT}Fh5sBOmVEo$TorvxoC9Oq-7nU!z}M?rz;x3nF;1zM$6bFo3owQe z!``BGoE7zsExclh5`g6e81-JT;K>`mU_&U_l@0JErK(OjRkohu7GSf-1a?6`b8sxH zqrAinsMdv92vh$r6eJ`$@BF~UF34L+c8+`-3-A>^XT?}iE>;5pb(E(VntGBo5bM93 z&=4vi4B%GGt1{!t`orklF#ch=WZ&V+xFkfxps!fQn zZ6TG_3f8WOucUG03U*vEw|0T!oK2{KS(Wf z7gk=sy|-ic9BCW5jwRgnW35YVNc&(4>TgsYulah+kSspY*z6Z_{X_b9v!~^YA$OSN zo2AQSW4BOc!lKMhGM4#qZo2*I&5A1#uSmjwvmSaTdgV=Us>TlL5j zZZenl^KEQ#W83lTYno&s%ZXRg%2wkI&yM3Is|x9(UOW?&;OfU#nLQ%G=m z5Yw5v!8IIuFJsvj?y-0xva%?3{5^b3 zPl5MSGx503sP~&9-sxvV{l79P3i7%N7eHgQ^h1UDCd~vlb~!=8H^GltsxMo{?1a6l z=yG#Wcu`iLhRc0psf<<21HyBSNbF`t{%pCAWHW&rV+V+Mm(vEwvHn;!qZf-V$z|bNQbwhAHJq4^r#CpZM4!1QxuAPU?4tmUBe65d zd^zv;r{Elj##E!GJFJzSFJsCf+2R93bDOz`Kuf1`{o;W?Ln68>0-l0#dycE>!nu^l zsyG2hIhOT-{R)~U4Kglk=<^=54MR`iJHKvU%zTL}biY@Ki=p|oB?$P@zMedNZ|FhA z51XgGPFo(=>ofv7W0@posBWuXM!*ohKBps7WnB?8A!%LLc<)~YC9h^puG;o0KsSdw z&tIUSOjjs?>*Of(6zWQDL04n1+IPdxSFb(Yei2bo4`e@+{-ZG)bn%7EaMF}`0OGk+ z)WE%%6ppPz8 zjw-!Z?Ans^n&v8*W2KWv=CghiA1}Uj|Fz@%)@<15-WReP2ktGq@tt5>)8n`sl7=K% z%FpCqf9q^o0(yI&xW*hMM}*ltIkOQN9;AG^6#4^W-<{~C?nkd}Y(i|aZZq7%k)?R~ zC?Ovyr>~sP%>H%9om7$74tN~zEZUBAVWG318n3eMs+*gKllQMesd<^e_4Yu(PmA~dV%?jRBb!mg;08Q9xpl4CxOz%0szD> z=m#kGU*%$=sig<+{(aK}4QEt5;7L!uB7c%lTSuj^Gh^I1$FRW=w9yni@=n7pa3sCF zdtj7?zD)A*Kvjbi-f%z4sSuk=qt8;hL$mg^IsanmNZ` z_21Y0rPEC;mjzwyUDhkg2VvIYetnMC6;hMhvxt|Y4Jr2`|C2hsS92R%1hy(O$Uxu{ z)BT8eY!+q_V`*1~fq|sBK+3ino+?+(31G4Rk?5S!w{sRYMQcWZbMhX zZ>iyB^<+;K^-;-Su1`e8Rk07^3i)NMR%-~uIKCk(o6wB=&yvES%D8BG=~ocoD<~~1 z3Z~Cib3-Pgf1m5&o#*Uj9#6Rx<#$=TL#U3C6Kvy3o?Q@WElbwWtSpW+)VNY1JDHTd zH(KSXV|4CAS)$U0T=PGDJ+)hWIRE|z@>cH!<=fBKrbK2F zgReKg^7FXPv+B!ghmkX?EcdS#G6rD{S2GHlV!fx<7Hn~9?HMMBT>+WWOBrI&hS@@UDm&w`ZL{LG)MYCg|qV*p41{}^u)2<;Hz*s zwSMW2*3x4Su1hV2Q)zFNY))o_W|K;IHBTl{WvbD>CEslEKN=sO>pXVnPclYQ3Sv30 z&hI9ayM(4*G2qUGP`c5N^up;f&T*QVRGCIbUCq@TF>#xY+pXMo)tW841i7oG!F)pI zF7o{*2s@VhjamVrNylX~4ANe=yM%+kaJ{u1`zcGgZ*X>q@mE&8zT_catLfLz>d9VW zwsdSgy6kVX*2A2?9Afc`m-ujG$cKTS(i|~Pqy5%K@4v9nc-T|y3>8*af*AKNUq5d4 zX6Qm=K=q-8kBjpt!@!7TQ(&v?=0-yEtE4`?UfrK_F6 zZuV+BJggTq;t2F-p*O63Zdf1E5l&|iarHJO{uH>4RR)-vhOtRT#tNcsQp37~94^Lh zM8#9SioH~a8TOs!-!8`{;Y+K~!EeJi`+NyZH>DEeg0I~HTH~HEl=)V#Og#&*n@f{x z7RmUbt;73aOax11WGy;q0u||_MXHvbwT8Aqz00pE;*Rln{BeD}5?YF1uTt}vWSKku zv3UWSVrfHd{4;K3J8GDy_w!0tcZRJ4LhV-E&i^`P&r8!0F8*aJm_2hQCHQHeGR!Z9 zJ&pGw58P@|JpohtU+n0{)$2#OLx9^)uS9* zw{{z^f4_RdNxf+T8uGB)_IhOqPm|{FRkDq<%(M1QGq>ztP`q+6=kbgKfd`Frb~W1%e_9b1#-|5h=;H{$l@#6aoTU(f~17F!qVCP|H;`O`(m2Elfs52G}5cj_#vFecU}`6m+_}agb`avd>)ka+o*zRiY-r zMM={(qy_cG$^ELt-}DUbn35xA>A{MWC^9)IX3p>HJ_22Jb4bpCHEepCikQ zVm3KnlIP{WGO56xLp)E_uZists?J*<)#H7<}#ofl2ukY*;1n7~ajnj!b;TLaOixbZ@yM&dOR)wm12ZOuVy$xZ~ z6OUNn;g)qJ;qco#Kvg2KD?;em;C=mTspX{P}G@kbrI3N9lb1&FAD6jQo7p&Q?OOiYvFFe>HTpRFA3nzpYi^_Ue7R&y zNa9~)^hz+aE1`*=GIS|lF@0QirU7kPU~*Z&wE-a=Qbu>Pd#x}qYckh>ZWL4;CWwhk zuh=}H3+pF5k~{wjYMsRscLQVZ!tuia-_pA)3C^&SEq% ziz5*QR;;cpwfU-ff8myVIH%tUV=uAi{Z8`T(`rj;19+N$43<&#^|fq8H8Az|bGyaE zui8!?Ivz*&yh@pi1BEiUu6`%~ZWED4mVI%t(2v)RyBI&_CtTF$%+K;;-+SL%y+RbssH=}rn`fC3vbPwKno?`AwUIzl&mTR5_a~HSoZK3kR8Js9LgNH# zG+BNf*DvSiWtD% zo9y#J!d4Eckmd_NwA1Tvw)Rj8M;j$EP z?drSKr9Reoo2kX`Q#Sxy}klbeMhHuR=5aOP#>1yGA?@O%w`5Z4?t$a`cDcwQ3sQY8;Q=Fx~ zUCWzPc-np`@?DqVYe@iuB8i`1Ho3oqD{P8(eXZ$aQ>{JrvvH;?Bt(_`7~RZfRHGme z7PalTB|7}$3He{@nQp)94Bwa=OgYV#IWu7&3!8Cr@zXbsw^25iECs3JCWN}X>>~4-vAWJAoE3f(dWF-4RHK-`2q$tQ|v2i zstqDK&YGlTl#Jv!eTXm#MDQ75|2T!YN9p6CSLApOr#s9RiPc1vatcl27sK#~-Fs?FW#+}_dk&vW%-{z0WE-8jfaK$c}P|#K6{-au-&e+7%Uclv| z{B^szY}!NPJ{v&4I00nx4E^&M=-r!<|5#Ir#3X-j-F)`dXwg#hM&@~0s=)^QQkwm6 zr&G&E*Jv&QNgO4a2xS%jkd`-Ph#DZm(wVqy&|yla^)OXUhibgwl80|$XvD%jJu*o{ z?weP|c6klW+8if&ss!1x3a1NlTUfMo&>37x9bELc5iJH~Va|Xrym9fkcyA5e#Z)o@ z-u;GZBZ%(Iq<{ZaP3!*4ekKgJ4pZWuVDB6^oZQK_&XZH4rOI$2A4(0(OZ8wux&u`x zAu2%5ydGp2OVulS^~nu=Z_G%6Wawvu(F4BMF2clK62BxKjN-&Nu)E*6*y@;73T(<6 zm>d|5jSp?kZ7D`4eHiVEM>U};bn5<0KCqP&tj|w&cQ7bX;fs-b$9`VJNj7Y#5FJ03 zTVP-nj?xpXQcA9m0e%6pJ(H~k%21`%exfh?`{WwtlLH3`>6H?>+{z;p6H;(j@KMZ%$KgE(V)mk6i37!%*jQ=pe zCejy}sZ>888Vro$B~}PuJm54d7l>U^OB6AT0&t#`_f|JO386&1?ha1@wHC(+c&g!o5(8iBpyzY%q|>BxW67vc$V+J_PZEojLy9z#B0wNv2;W!VkX2i7??|$ zM+BQ1!BUgeB!qplJ;QT_aU1+H13@rD=^&(O^up?q?TG(u$2W(Dh2LX}&7vjUprpT~ z!pj0O2HZh60}KI@UtIO#5BswMj{QJz9i61&W^=?Cp{yr*#F&Vo(Kv%*bWG( zHpoyadVOre;L_2jlgb6+;^u=JZx}pHoqd9_CqYAi4>4beQAUvLa;F(`J>`+9)RDJu zd294kbx_B*a>rX?C2U`tbaRd@26GLTKuN}% zw%ECR^^af(c*DXiZ@DdD1FTvr&fG#rnnLBx8UP{jOfErRbfOnFs@o~gas~X)?z!!t zm+8XKuVeo##QKUP-F$DP!RbaKIPN+hD?q;iHIUvMYL-Nyn0=K#?h77?B-ruQ6_h})*(s*F}Bgya{BU` zW$-K|VqG-q6qe%YoRP+b@Kkd_n?lpTlwtuyPiV@5h1FYBs<4pB2*s0D`}KF}$s5$m z&iK3Br;o~R^kC!A_{6_ z&E#V{pSniQ^aeILD5q~;TJ}K5o1;avBZM0AAI<}Fdm=;#ua@& zAkFen4fN`a+<`JH>18QN%neHq@}qCpT!JJ>zT0)n_xq}<=EDW?N4S9b`Qhc+Gr1d5 z14Rwcs<6Z&|5(n`r?`Us2%BkDZ|mtX3-kE!NUTGxHkV#z*5`pCyLgucpB|qU^1bWh zmhaL5W^&Y*=SK%xZq74&xO@EORW?An^^ACUXvSqfgU)aZJmZh9P%6N!W-3))*Y6+6 z@#Z-S{2l%|?0~8Y zFNR>DH6fLVGaRiU&LS;RPz?>04rX^~^9hrGJt-$4TKw96 z^!$hh*G?wDUkt2mI1J@8z&Ml{Tr-@;YoDQ*(}=rfLMgcZA3| zV6$@G>DHb@S zYj|aHvffxfqEc+CRFV`#H6GG#CZ!rHu4|)RtL7V(s`f|C zSuCRTy7#G_u?0U#qrjovxTvY8oMP#rh!i8jLJizL0+#cpcBVF%Y-kK} zW0}Sb^X8ohWn+uEDI)fjTtRnGc!3x zm6=%vOv^LA6WK18T1#tO!~dv`fS46i>7fjSuRP)h7A@}4VG5=e&wpe>=mO05Z(hte zz@HkDR68VZm%p(6-KcRxarTR1t~OH|n)R?OA$}3M7?LT3 zskIKLT|wp=fYZI*nJ6wXPgGhX-P)#Ly7W#x`I~=yM*3>HPKN)jbYQ)zkvOY846Arc zqMjOuF1sGXnZsL6EKSWlS!RPBz|&U;RUlEne9=+vG)OQO#bgxvQVVSK@L{GPP0Lsu zu?yk`;FRHcXDWUr%-#y&n;d*j=4GiyT#10dybPionXM-pJ8vVdKfkvGF^-`t&5K9Y zf_N-6B_GECE(+^bdYQPUbG*BS+%4*lzrfH5F}J%yA3u8&U2t~a^Z4hhXVYuV?vV0k zH-zmrkNQ-zv;SWB)=a73?g&h)vgQ6dQ?zw0V=F-RR(Sm2LMTV;x5clEecdBPYH)oq<(2&NLk>43{x7f2BFyE+V;_#IG>0KDv0NpVQMLfl2e)fX6!Kgrl_ zLt*3~P6TJ6vmkyhH}r?Zi*bddDA3IYv5_0w2kqu%qqkX&2`9rVcsSu9VK=wAFPO*s zr>B+4wPN#DXKwm%Vp_GOtFf zj5`fBzT0USjuEj^9S|wGF_39Jm@XYIMNj3MN_+5lYb7GC!cKZC67Q{1>TAm7X z@^KlbPsO|+ZL?Qs^mjC`enzqv8L!|;cxJTgD($pg@uR8t#vdams~>hDIWgVMjthiy zgK@HLR^%^p%v1rVoO0t%8_PZ?yjwD^`<%bWJ|fSM3mkqwOmx` zF9!3RwUF%ztdodXSX{1(V~s)vF&^^4-=&CBI`BIg9>MN6>SY&iTDpq7pxT%>UU7)n z;k|kHCRy%OHII|W1}RtYDVahoO?G0HBolLLL2|rxPe5vi}HWSGpjf#@Y5K~b_|MsIo>PVwsdU+PCgMcsMNMHQ^T zZ@%*Ev3UFk?o;w0i1|DT7#)Z9r7;;f+%YEcIK?8RsN@)uOFSMxQk+ih=#(IawGjHQ zTIAfnzuA0Dy1d+TZdyFVl|N9me9We2(C~FjqKR~1{^TjU>XyTL|18ZlH|LYsgl9?a zTmhXYv|aL$zenmF=4GtrKMH=YP2KIQ%sTAI&G1#Q2Fpem$1OOEdXojBQlykam0~;U z(1Mo3VChs6?kfJuJiYKR@NsE#+02={vB$r71h;;@zCtRk9_rpSnE9B2toig~4qh!> zq@9Pn?bH~6|7YXg74fv}%t@QN_aYmP6?Gb>jS(vLmznzQ_7E4%(W%2E1rYvtVs z%-KrqS%2*o01XV(cE~_$S;?9_5)`0#`iQ4sT`n6~psaNTljq}kp-Affh5XruXa0Z2 z0>kdxsp;hg*vqF>-VAYO4Stj zLZCa1bX}=E1=GD)kB>#0jX?F6@E>p;WkaPXi;Hv`R4SHpe@SESHTwqwV{Ar$Yx~xj zVvnXR)u!3X@9Of;#GGz+d`mqv{Tym)`x4V9!S?KHVhSz&%R~L&Zw+&JRxUQ*asDWg z>1cgz+P8e5XzwP^{!PprB9GvI021esH61RDCbjj;E4cH8BX<%b@9rLKa*;1z=X{{8SJtYwJy=Tl?y z4XY25)%l9EeR9)!hCJG*>}MyCj1SV7lkhKQi?g5cnD zBw2EB04-O5Z0CliUfyKe73|`F65GX(2_P7}k~WI>0a`v62ViCcf1BGKj7HVEaCgMG4*@3 zPjH3sCo2&$pq4vG>bsQp6HFvJLP6}bM}y$k_gVJodZn~$@%Yqe{kU5d_o4e8m9%B- zOBWYRM{LvbQhY(JdYr`)UGVPcUWd~&eTP0>SCxE%8;=});wQrj@I@AAOKItu>Cv}Nk6!(;&_E41D z+aFkJLW6~csrO0gn=AcOxM-tW>Cq7Ps+r<~n0sPz)LJw#w8l@8dU}QaNtXC#Y>_=W zu>m4HQnHex z-vd*f_so-ZToALuGfiNGEYkL!8)V|HKXs^0_IeIm1nIUk3%8$KozdY3i_&I4k zaf!u(4!*=*BuS-SF!Fkg(gL(!O`Ush1@@#jMq42b8ewggyZL*(%i3l0()~8Mp;$%& z$T)>%kZ(I%D{PJLhCHwIfhZasV~b-^$;NHO1!YQuWKF@0D2oCb2CLph~ReU8MC{-M=TAx|4tY&!ow{m>0Ilhqvyn$|&5R zYxI!Rp&1&B`p86Tgb4CLUD_5Wo*kK@ZkSAiUX6~p+O;XDQZ9Nz&t2*~Eeml1#4~`5 zMz~O09rzsXyD2nQ4EVIxW^yT3x2o zb?>J(w({avc@s58wQX}IxQMsTz1**{VZkd15Wu+hLN{}EXp?-gR=)Ky$3|Fq9UxBjd1*-H99X#;RE9gGI<93GzR>A~9AoD>X5(^}9A%bt&R3vtMj`yr z@Jaxinq<4#s8LfB`Qx{G_XYRO%Puuw=E+opv?dD^5OX-U(@WcM3Eriq6dNxnM*tmi zUwZ3-6e?=C3|MC@t)F=PeZ-LW;5dmr?L^4wk!=hN_~(o?rGphNa0-II7-ksTNO)W4 zE&36b7jSaVasAQ&B@8wQZPxDI`1{fw8by4YV2KHOzguDQ_i{Z!q*?O}14r!CKNqTy^gJ2wpp;VKHh^_rIBxadK)PE9&(qJwKCd z!f>pQFWgYeWyQz9$2YgkV*$#I;4f;xT@UQK#c^J&i8fX}7<_%YDCpJ+aua+;?{d@w z6GhJ_w{89l&$uWlNJ$ce_=|u`ly%}xF+|+DBVt0A5=db}GM3yW$Ai$@#q8N1@S~@O z&BZRC^8Z0SvwxxQ{Vy#p?gwuZe_k7ZdS+?k!WHm|2m-dFAWni%>ew0N6 z-S%)=3Pw-XFujK|Aw^V=hG`G7I_zbRIMxP57~Szx&Jvek;}`eHh*L+6ex@B&+_NJ8 zvvxYSPE$WSuJ3G9oSUTZce2pX$HWS6K$8QfvtPhbMgeH`I7z7_lt!(eRvaF)r33wq z3G~H|>zntJ8uV>C2|q-0F!eDk`{s=tisWoYnfjxRzHu%cGPl62c)YMMS+u^G_vkaxT{UvYe~4jl)awo zxJ(k4$Z>r1q4M+RH#bapJ?RfG{aYSYd97y`J&q}sJk{TsEY^;U=V#Smd-}LzTkVwW zO^a%6rE-OQBTRMmz*<6V#rw$#MOlT}HF%|<(yp20DOMN$y+>PwNxvg^sxC{`71Uw zG}YDi1dudr$))0=9DJWGoy?-uwY1yZxQyX+0|gPyQh6kB`mW71eX^c?!9Y%iur~TR z8x%kB&GbCf=_c}{>Lmps?NlRYdHr8x)`tI}7Gc)<3!qHKTQKTms);vLIkpOVg;oCo z+-^-bp<8yL)cv&ScAEb0G!`W~VH{ zI`O=boL(c=N@`99Qm9pr!E9#c4C{tI93ArXhj(G5_)9ml&4F08CM?l%oyX(*gR}W$ zawuT5N>oWOnv+SvBgOb-^W?}_aR?Vfih&e|Q=_Fwdh!95oydESZXMZfwv%E{ODBle zNh_`1gjwhQ@wlz36yKV>AElxHC)3V{=!x}F7GC>r<~AlE_5;u6y%-KA8HwAp=LDf@ zVGsmRT16I*1d}81lF01~K`NCtJ{!J*?%mf6*(bcBK_PX8ndt+vyt8{t8)aiKt+Gi-#LE>KA*wZ?b4OXT(3I}b8U1Ig0? z3MJx@e7c292v*IYE6w0s0RSz*50ccihOguocg+vbUoU%+=44E90iMdv+C-J!v4a39 zI$EM7Bkr;4t+vPB1)CUgLH>3Ep%WvVL?hIhV34usIu8I231y#GGZ7cx6%syMDdagY z-n@Jbv*kNJSfupp+QiRmPcyZcUKHWIs?hz8v%_C=kzNULwyap!<}0aY2_*@`!1Zp3 ziG#Sh(nyyzypsViWMqdYBTPduDN4{AG2J$E*g&kF?2Xk0qF|sn20+(OoSW9IH;;ut z67|?n03;u<6Hfpj5s?w3t}zqkenK+G=S@J{2{4_OvOOz_7nF?Yl&k}QAxY6*^C)m$ z*olQ2dDW#wf z7tKcy-x)B5hsf_EvdZ{Z1qB_3{Jx>hQFdFuhwyJwP3vZU{4Oa4(9<3nS>^t>arDOp zo1?22$N7(1pOAE=d<`29r&Oz87yk|U?k8X6^sXMZO4(8W;^_$+CoRI2MuUyArMC>N zKIhD22Ue^^b4w>zHoEHrJ5Od|AyNC06Z`X5TU-u)!AghhYH@X7`tg9m=}f!Niq#39 z6%IddRY%(DDCY7?T+z?I>ifE=LpRHA_m@Fkq#mArJkcIv@+Rpf^TCz@xG}Ha(Viq6CLVQs({OaJpWECrlff7_m@G zR8o5eT-?Bczsmi|pD^1K7Su=W%{X#d_&Vd+Dv=6ZxLof6MXA6SjRUMK*7 zO2oBOu$`71AbnjmJX(+zwS+f82~ze#`JKJBSe!WPx1inpQRxMiBG!2Agp?L(u$?|N zbS(8;l!orQB5N+{jQY4jt(EtJ4$&z78Fdj}6fQW?9cVk-%^J})EYg+nG@B#}* zAV`jADr&?Dn%ESPx|>Hv4|3zwjH0~-oRkpzr;>jCjL8Ks_JF;li_6P<7xok1{JSY7 zhfa4Gd?MxO=y|Cjg6)VsCG7+;qpjd-GE4)+BU7ghQKD{hLP;Fb&t5<)_`YB+2`1PC@{W?}ul;@c-+W-Ou;AN;smlP!8n&a5N+SaP08jeg4+y zT4iz+r!seNl}WfVv}@n!bapKkcm1glCz`oDF;N~2gGa?Au3yn{54wC%y8X_} zfSbMgwAPa1Ym(Bn$OF{`H`7#Kj5fU~UO)EiIZzZ0wO~!s2J^Fb5RsTHq$&#QhuxNw znrbV*osp9*GaN6yWb(9eC~mxo-I-c?mY9-sSUUbrYK6G*KI^Lt3+ObHOLEVF`58+H zg`gN?Q5=}op30paY66EQMN4mDkvoo+AEYy0c}~b&0**3ohmN39Iz6HWR52MxPu^~e z?T8m(NuXAj&c6>oUHu}4!2;+#6b2rPh@*Lm zsnzRy%c(s?*J%EEx@d@J?uX3vJN{YwNg>LCgb;i?nbmi!wSI5?9yr1PKqR7}bfb1% zyf%Q|RlFI75$E6p4P(*pxDF{S{-X{yoy5hpe`dY8Atc#$EB|&DyUdoy;0&3jn>@jN z@PDTP#c&9E^T0qck+8=APLO0W9t|+p{?)MnKy$E@DGNl%cD`sY&FwP9w>59NxuH!7 zI_2z)d||Err!xFlG8WvF0p0)4D=^1m;POj+QC%}d3aNSkHPSiAVVIhVXTpeZaW;k*K1@kG zwu@2NnI)`uLtbhBN=4#>>iF2?? z4l;ld^Cc7<5uCB}zz>s(EY&YWY9nZR2QtdP?#mwOjH$O_9<_ye76zvD?|A(^Au}+g zH-HBy`ib^_ZJ^8Yy|#rf#J8_3!h1^JCq@RXZjoCJ?^>NTf1O_vK%T$Y1FmV<3)H={ zdK;2P3aFbhn5b$pIXbfEh1H*)_3!&pGZOjCC!pYn@>KUwu8iXiqKso#^Zsdd#dMem z_(#V0?aX(_6phT&Do74cexqs%odS_w2s91Rk7N>eRMFn8e{b4Gax=Xohw7ECZvIaG zJ`~Ww!$tCZXQ(yOWrnb&e59^$#J>1v7WiO|q^_lu{#|8!Sh-(YDcw|=%S5I7A_?-S zP1+}Uz}HVK|DsXGwrd;aQ03A$Rhgl-+;|`l3j$3O@73+~v|n638YK!RQH$nTtz?X~ zccidRC>UEb#S_4?all0aQ5;6@lGN2H&j0(;4J!CT-StI-i1Ibncx`&ir^Vw3*Vjox zl_%8*o~sqt-?p)|Mw<;v!>bX{d~s|jCIkTCH;M?gR*FL>Md^`IPG}H+7vMn=dh_Zd z>gpt%_`{ez){}d4viD!fs?BHR!$$Fx?29L6O(`wh7~`{49MUy=jz z!U0`B^y3675YtAeQw_|O%vvcxuHy2hyNE{oY0jb`ib*J zbm;+*8b}BSL%?q_k)~g~fCIo{DP9C50PiV8Uo$LmW$6qMi zY?I^W|3xs@j{_ia_zp4-4Ci747MeiCk*T!vx325y@DsPL<_7t310JLVOEyZ1 z9~=}xD~>&O5(13IFPJQfP$&gBcxa*Fyd)AV!GB;*<=kZh^KXj?YU5YMfKCm2NpMDf zYtE7WeNBUnDu@Ga6!C&q%Jy`Z+T^Hw;BV}ZH?|1~bx+ypk@ zbE$KDG}4|qX?04M+`chE+Usz5ue=$Y|I&7`TtFIn7PO}L3U*Z|wXAstl$Fo>Jvf2) ze23o^?+f?h2!ZBr{z;AhK?4#6j9LD!G4{Q;rCy*W`D=+L8n|Q5W_-c}@i5 zKO)si@NqXNo0z*NUk_y@g6*gj;r`VRw-4wuFRupqQ#@9CBwYr~er%im!CMIxsB zoKD0UALY<7k=Ht!>-?TgMn zRKm;AAN+T8XtP%ct!!5fRZLb&j{BSCiI($u>el=*7NAK^B6yT%4si30+nsj zW)92YCDtnrVbsB-FV2$f*30kJwar{e|Y`^xTrzZ<*%sT=V#KHowJT?rxSV9mKuzhe6{IIfAF{9^a#-2aOKCS9I|$md&( zA>UwZO0fH>H5RzWH5T}PVU7JaW1cUle2d(Qq>Y4~8L6`uWx*+lGDlll{#@J&L9T_& z`eOG7!?k0$fXa6T1Sz}A*@TeACqFGua)UO7Jl^WA}eC3Yx_p8{@8 z-0>H?KfwJUa70wTpmuy8x3Q(=LP_Np{1%zM&*JsClf*dI3@2=2yYMyF3MPC%0KOjr z5C%j3KK1sE-BM}a$`bTd;rgme|2w>vUi}WRrRTpwZ0R|i#X{Y@t*sfUC7*yNU{H(2 zjDLlH=zoOk^9PgSFyfE4#SyR70yFe2fJ=sdhv0(ALfxc|rQJe3`qC7G&lQuOVZq}_ z(;B{%!~(wq?3=5E!jQ&|8<-J4$ZWB4Yw44}e_`Ple*d(s1;NS+_m^+A7<~T43#%>x zVA!hVn9FvquEHWqzfzd=C56%U7FaVJ*{^+n0EiD51hVj7I1IY*UjS(FZ_)z_29W{5 z000mI1w#PD=Ye22@hMKgFa92X`LHL|i*s_4EtJ>)jUCYpkN-Em(7*fO;dB6T8WHR` z7m=rhqVX6c9*@F8!7wlsfC3|+a0C$|2po&SLWxHyI0A`8fzX^bIJ~V0sZifm#2jUR zYlpL0`~NB@BOy&ot0p0(Nc;li#D6F*(VdKqt2wZ#96(R@_)hAF5T6kd2^O!!f)D~v zSdl{Obj6PC?H_W%6m7=Rf-rEyfwO@PFVV^lZM9YWp)~#pH5Bnh3Vu2S`)hspa;RaVBV>k_ueh?Uc&q>HgA8~hi$4&cmMN!p!>SEu!R! z{s8_kAOQRa|9|801Ni@Rpzy={KL`j1!2az2|Hk9b{{LtHU%LPCU=RcW z#lvw3B#fj-4Fe$%FeDU+CWhmG5D5O?T>qg&2l})A{~M1Vz@Ib~^2z(C>#@Xh)U2EgHe*8hLw!4XJJW*K%+hm4}D*j`J2AY3ef%JI`> zE?s!*#?~?~De6I$Cw85Ko5{<(inma+zv7cSYq)OnF0b{}TRBgj<$O4Q5NnBlREq>V zw%RtuwTnAuRkakm9C?Fzp6(oOe|MBq zKUO`&fm&4(z`Xu{hNM@?;Db95-7`He zXe=OGkjmev_qgRzt2ZM#g?||nSwR#_Bke|4>g}f`52-!pP7v^+)}^;F_dVp*!N}C* z4m0fWX)A1#LlrPIR#ef*Qy)4i)j-F>B2{xmm&A-pp@IGM<_=+|#p_u9}@fOef%U;-4SjYglDPw(&W zx8wKm%((oVQGjvX>7!Efrx;mSWGrz4l!9K8+}9Zyd8r9PUb$2C>F#7a)V$Inbi1ec zYXJhZ$7EpUT+^G4w)0cmOGkV7t;D35YgKDMUh zER(x7ucG6l#yrjsF$8jM_q<1|p+vdQWaDO*Tjbt z%#>M)`HnFYVrtwt%F{!elT3|y{oyRupz$36o7Z{I$?Nz$ZfV&WbYia>^CpUQTxyUg z6k`(0_VwAD*Il`62-qIXq8>#(dPz&dM_@ZWt?Z!&GH-v6G67CX8YwcaLuB+JB|;j^ z8yNwA`)}QF4*?Os8M#F2CQq^o)FA;m%8l*-NqUoD7=FE{M1wXbHAJD~gS!R`UoLr| z?iHSZLvt_cpHuVFZrE@oe{+lDXzOD-rkv9WO_WnT#Q|5G$b54TpSgQof{aOkERIpY z*`3Q15FtQ0Bg+;C0K-Jdfh9N$1-(t`D|X9r<&5{^kR+m61J+CBk4HvdL!j*6Ar{l!F++&d04kGY$}S-|(= z&1CbqP6M7>?l;y=I*QLlD$RLsY$!FGG2K2x#fqNisHB*>5bT~&KI6Ty*#I`z4;P4k zMjkSDo2*zbca?hA>G7(glpc&MOfu8Fv|fRi=Y-hftymdQmX{e1Q6HlA4ZJH8rp*mF z6Um}RZ@8X3k;#`)3d~1O*m$V?y7XBqW@@uB1876he7$wXbxs~txvP74QUeC*Zaj9s z<)2hin1y~yJx%uX%Ho}eD|J+yfCX5Gp#%EfreMkUY?Q&O!dcxSXm+BvRV#^yYCKv$oFd< z;LFJiWKkN5VSQ}N(!YD}`Ey!~ku1;`9JiF^Z#IK;#BdN01C3F+8o)@2Nf(2JtpUH;CT>qGlp(0s9#)`P+M zeYMK33q4tsMjhWLeej)EFRDL(|GH|cHlV*f*%#AWb53O{u`#1Lc%F6my_^>=w_u>X z>Ktu({+#UaMGLPx_WL^AFPvAAmz);8e??cJ;LJ8pKO+;93o@EwhY*`RjRMqNBoCGN zvnWk)q_EOSI#1J#X0V5Duil3eC@zfT91A2&asYruXf(YlW0ECiz0~}F@#uZKlXwZ2 zFLBQ9olfX*?|gVc)+gwgu1D!K+lkFL=ftPuByQXp`{Y4)AIoz4_{61TBbxCMwuSDH1uxWA$Dr6s($d~02}_P=&?7_pwXk4Bl6SDX+UAyQueDrF;1Q! z^5;>FF_U)+_$6#L)ht+#JvZs3RooFnV`O|Yq%K7H{I=|!M`#R-Rj%bz7;ONK;fn2&e0}8-lP6Z@JxVrAND|=i%Nk8OV zeC2>JG@QS6fV~6DWIWhlgd5yk;jQGC(X-AXRLQ`vm6dyR0x)3NuDXFu&0isDv`#s2 zo1+^8=LLobIlYhhTp6E6D0tdN)uK{P+()SeT#=kj;l=8I1pq$y@}H@Ng9$#m2^N0k zHR0ZVf!#CK>bBI@2SiV7Qf=??sv6OZuS^}Bx^M}(Eiw&v$*V)$OaED*u2_l#yT56@ z6^=2p8nixOD!^SqfB#ANvlqzb4=NUZlT1g1EA2Jzm6=utI?U!buyY@e8g{E0OYW;@ zY`s?h7BMV;B+KX1Ug%*I^-%D(*CkY>9Cp;b_-c384!d{B zye3!2DIQRTln=?ltA`3*CpO|m_8o9Pe%d7hedkP1vwD%@_O2|jeBOCh>&YtLvG-Jy z?8A*Thd;o>y)tA+lpatv=29|AeiEyQj2+mf_fj{1P(BlXU^9I$)2Ktl&8B@5u0Gda zp;aw6`T7pe`XH@k!{Qq#ZF}B+%H=q7z{A>h)7UnU)x$dh>D5`S+~tf`?u?adtPnGeK$( zZ&WU^9@yHQu(faVY?a56AvnLn)@UcaS<|%WOa0^L1e7-M)s)Ueuge6yvA;@O zzBkmC^OZPx%3KLNM>aNi{_YE>kB|x-{Vr??U&n{2hhjiG{*Metf7rie0*nrVFCrWo!81d@j_|mAm7LZ@rx%}+oxUhP*EMxVU0Z- zA6}r$!l;$e3miVW>9T9M%4BNA#MS4agMbi|lA8|%__->RpFE^{VrFsh?Tz!fC2#hJ zK~QJdyg2qEHK_N6OLpFsaZn1qHR;+ZKOC?fICMc-R1^c3+Q5uN*kUz-YE)-m*Ak%wtHu9yKaju=rV}92rnehc!?qiYUg%-EZSlJ zt{L|Jj(D~^$4H#GA@|IGYxUeJe=mE}@%Gn9lTf^*Z_T#+A<_IsTt-lM;gj5#>&s13 zns+lkrO$78b?(}&TA#yWQ(|uqB&n93t(nNw7dD{soA|iTe@gJSr~sk5%{4afd0+8- z*$K!tN*v>vc+C#qy6Dgp+wD3XV27frd2Ciu!OX=T?-$ooInqLZir+j-rG;hEsR*?w zz1{YxOSsj{)^64(K<;k3P$+DMJGLC4i`5Vu->-8EV(*2t^v~8U?Jubt5FDs4@!obG zeRJ&oBgMi28?_By>^@Bz}jCYlG z*fwrr+aBcKV`5QSahw&>Xk8#m2s-uFz-Hg6-CY};P8&R(gSpgI3f*7da=X8^zRd5Q zYK^yGyP2|$*zHv3Jg55yM-Maaf5uSliBJ~pO%3rn>O^aQZF@nTOAZ&I;llQOF!hvD z{J!zCTd_yoKHVOl9D05f*4`WLov~3TaWwG!&GDJ!nCbGKiT4#Wci?yKNC%78+<)drMRRy@$UEQfYwPd=_LF7_;$ zUDYjI{^;wp5{=wKw(6F{HE(4EhqnqDX;{ghNDBDa{piu;Hlv&Q<@iRi9Rlai7^oFq z4>AB(t*@;TvPGKW?FVtbEe16Tf=}Gs?>j$?R5##%d4oA*3PfHF3!mrDvj(|)4mNna zH@#>#*s!f-puOFGtT`bHGc~rw&uS*Cl!>)9*l*5_U!kmx^GF1R4Tpa|yCS(@@spGr z8$U?IRu!Q`qSaH+A98Ud!=hor@4R(UIK^5#BdSz1#l2Ely*!??^J`5^ZYjb69|!`ZMjU5kuP*N z=x@7F^|}*s>KvPS$VI-utk*E78&S<7CLWo8nJoSn#oj(|=`ss_qnOFz zWEwZo5b9=b{Vb-QK}1Lbb~$H$tGD-|HaN#|s<1?pcXNR=p%+Yb6P_|fMNmH(N2X(c z)HGuq#9PU;r#de)*1rrg%&d5Z`{PW*SpLy#3C1 z=S9!I%4Q{Sd@>#HyLYWBq)-$2pebm7!(6UuhzRd~B$K7-RIX;oxTl||-Hr(8yvSXfJ*skT= zQwVHbC6lZ;f%)Qa`97KcB!%H21Nzgd#nnebuI_uO49iwKF>aUTjHVtb76RGsi!-Zr zk=lbLvsD@t@};ufa`MzYIhA{VR<5qkb>5$ldrgt==Je>X&79oD@|?Qx^yX;3nksmR zQd-++eRG{zmh9W113bf}$cEe?1aASCQWtB@-kykZ4g?aWDJ5E2Anxe&;gncbNmk*>CU^QCAu zUHwno#^(-kxWpvJk6e@3HyM0N?gc~RbpqbNephO1yHcez`7Ha#17=UsOY|F_?(Emy z|5Bo+Sq#xkP)Q4Gem~}Y)n?xLNl-X_oEQUKo6tvHqWVc7gT5_}>x%Wm_E;Z$lM>1Z z?n|9Eko>WaM`vD<8T_uA0Z>O1n+_CyN@ARRTG8^oXQ%i-{uB4JW!oTekV^=(x+(AZ9Hb) zeB-u*x_(Pw_yuTm>P5W1(90^xdYU1}2lrDBYt}qjA1$>WPgQY$4Ql6)ZjOHU@(wSD z=cm~HdB71<_g6wMgtOPfXp9^-AE_QV}U+%v5EjWrU?7S1jF`F1e8^Rtxs_)Vj7kcK&79 zBhLwTsQZ+q)KJXDLFLUD#_4AG3u@9Qg^?o?%k*8Sc?Xic0AGM1sD!rDsEOF(J{&1#L zxLGRg<`z@OiQRPZ<+}6AiWNcrSM#|yhYY#ey^gxA$@3C=7x<`rYIV}R{ zSYvCjR<6cHpLf0*WcyOxt*gOg6gUu;wKd^=o4tfE_h%ERUZ= zc7mRcI;)1zZyn6V8CT0&9IWcpx5(^GpesHIE8M-kC_*f6^5*lq)qQrSl?4(8k|n)= z6`3w=KRkHg$|!d?T=(6((bndv+;H`oIaQCn4lfJe_l|g0T|4rY-E1PqcTbF^X1Zzb zlp5U3`$l5cs5LFK#Mq$}{w9cMG3$WTrt8*RY?~AZ!gku5#!XM&-Xq1}?n_S_f`0Q< zNGkE{3tK}VnCh{~(`z?UgR?3blpmIV%aAL~yi6(>X^TIxsR3!$b3cD7zva=!ob$rn zukWOgbCvRL6LhB1uA8h?c~E^Ts%Iyif!{p{jMr!%Y|_qcmonJ$B#-T*TfGt2s5W{; zoijA1cODrYD{Bc$bfK~*5IVRx{EA=J-&${DXTpM~QH|Xm*U2)qclYe8Ye5Qs+h(Gb z$;1l!@twy@DQ`h8ZEtqdPJTWC@#yA8%ZkFow_qQPgH>$TGxA_zd#4Y7xEZZ5VM<~4 zXiHFEuf}w`Mu(Yu|Hwq7{K*F6+~x|`^Fg}fMdRocQ~zui7o$8#>l;;q|^LQ7FEDNH5wfb*m{NS#eYD z;K9_(B~@s#H&)eitW{~wT9OYdL@a66I~o&|K2$LGGZ=Ywhgc3e!ZrARKIxt=QIe*& zpLZUG;Ze11ZS$!o=1_H`XN5R+^W8txHmB%WiLDtrQ$IAVsw57fHM1V=A}< z$Qav2CLiqcWTMr0o;^X?FFCD zbIZ|~Pcp#61YSd>F{M`yJ{o$|+d44}BjeF*TBWkAuu>QH zl<*#ajpc;Xh7c!zw!kW@(x^)TlwmWuC$9yzsP|h~UrB2`UEfV{GGv^GtlGt<=E&2V z#uh1KoN7EaVk&ZUgRR+GJghA(elqr9xt4fsAvTBnz{K9Ij254E9ev*0M|VHDjQLbD z)%YNtaNy2KX16DAUiWS8*MAOsEyYZo?2|x8DQU*#|8{;AlVRl=dK~XcG#Hl(@Qxrl=gu=_N~#cbqH@v$uFr7?!bK74A*|o zS7)jiK}UHo?9qHiwYs^r-o2=>Ik`($QXG1JyUY z+N6-t)M_Zs(RFN|YGmY8io>O5TlzXSTwvMLkk3BIV^^Z{IO*l(79sZ2a1PZ?XCH$e zU3BVqr!x$owanqY#=p}MF$)TZmZnM-q6D#bZ9ENyP{*!p3$Dg4!TVFXg;C0J>j!|Wqh`8bfvOE-4$tUW+^;+0S!8@lpC&} zR5tIEZe+0BfAgRlpma*GC;r1m-FCE_rO+XmkgVtZ+8P4;^rwLqw!%3#`c!aQQ{NSx zQXz4P^Us;O3@-4BiyW^%b=<#w?**NEx)k9%v0CJJC`t|PXRJR<2$UG?6GNvdzNe~x zn-)!Aex!zrM0a|YKG~6$>7tk5k^*%0VT$+etT9a^Gt1D zO)S5FVNChWT`i0^wvHzbMi(IRqqwNcb93IDorWKst~ai57C$~P@u93HOS+(Pko8c1 z9=n4>`Kdkk1Er6Qjd1X>N#xveDLu-68NEx!Lx;%sj>0rpVzTdcHf>?rx{WhXh%oow)LGB0$Ua*7a#62g`$7ATF>3eW z6zi9%m(TH@s+QfSR+J6?0B-J~kO>R9YnPmGK0XI<4^0z6zLoN6z$x9DbDvJ-gDnyy zpGu~;NDsb=j^$62ikS$1Dq?%aCTsHqd(=OC5{4RH&!fg~!Y%FHaqO-q9o1p_iFGW% zbJ@u@6gk`P8SxBc>2yS-Mr^xPN*5XGe>f;g0W?oz%*P$KmnT`5A-&~9mbII0!u=YE zcPjpr5Ko_n`R=}hr}B=WtV>FZ5>oEMx9pw|kUTG$Hm=Xy#8ZiQ=(gIjhj-DzsiF^MVNA1G}ELyIg88?G9IF&pGmw z*Xa(L+HSEQ_&_gzog3cJofwm6GF_(LW|>Citv7d3ZFlkw9W~l`$(cbQT%_{+y2H@h zY@5Q4vFcmVrR$}(Ykw4|7l{*oe1NXw{jRdR@|Aoy(Lpz)4s(9k6j#dV(U!$FUKrQe za?^@P&JsyW^5B$nF%{ZT5;TG4B3xvhd+uwbwLWJx-xLa4EHp!|WV&!l$|oxh0N- z%De14869_jUEyZgR1n%W4efSP-HmkVaCkp$>u-4Dh4_Bgm*9)4%r#^RW?dX@%AMc15l2G#j4U zs-)bn3V#w*=9Q>=6El0W6Ye%}bS9yoYV!tg`N-U9Fbm|-JD!lii7c=t*Tz`YuokNn zH#h>RtYhsWai+w0UETAU$5d~{yFb1&NwI(KQ|rlDc_;KzeEGh(=tEMI4P8N8eBsw7 zWNhAlQ-}hps5;9HZ@)$FCCk8@p}At6D$aJUbF>@w;9|)-gTDE0YOCj5q40Qq$&+y_ zOBSAIiJhRW`*D+uBQ?s=~EUcn|mbbz_@yoG~* zMR=yZ+ZeF=L)XwST#QW^!@N!9@H0HMOQi6%59+OTi#B5!>G`Po|sJeOOFa3@zlNZ3$MLI z>ulvFpJnMMIm$s6Zil9{e{isNRC{}+Nu0Q9o0~9$_sxk=RAUUwt=#O0oY)k_x)6R- z-s>-iU^s%QsQktx@i?~gJIA)q>!Oe@oJUy&gTbzU@Kc<^ z@z8mt4ovQg+^xI+o_|u=*-(aaeE5OPijaHm$E*!HY(}$X=i?f$vWN^#%;w-VRLvjG zawJfxZ&7T$9}nI~lT6#?zk_D0+3nzpgONpkso;9@JL97A3Xq)BG*3JqGBl1gGVl4A zk^W(F!#m{Or$*(>tTu%w&VhD+xKISpBqHk)J@`Htn|kuE7sWM{?Rw3tAS`nY>ma5- zA*jg5-l-BP|M=i^`M zk~?o&KW=W6Xlj)CG&ia0Vq$7QN<4X3R4p&x=uplJ^(< z=(rBONqxd_q2Ops+D^QWk`Q#)t!84VbcHNw!5ggQMOU5hrNERNJpt8rf51- zIQF3f;q}{2Kg9r_n30>IMY73yF@X?6gVM@28ei^?%=|jLj-29uT&JknD@_`1XQFI3 zW#%ihwiWMwaf?Ey*E*Jtm2KnshkE*H7)JwfWO>H)ZA|BB{cdUZ;hdn*I9a{OFhiQs)9t!>Dz+wRj_WrG9G^>NI1DE**^(du z(zf9~%yp$PT%(`x6*&M&C;mNwxMwH*rs=XZu{wS!qw^fuQjzEJPkiZ zcFDay_IeOD5&2qKmuIJmj2gSB{Il4Nmr#fbJ)0iP zx;}B&qbP-ri6Le%{K|NM;l{iKQPU)SUb}Lya_l;hA)QSQFW+ed-^>5=;x0)u%jJexA2#n$A13fOBC+8g&hlgu+}zk{bq(MAT8 z*=fRxnu|=i`ugGsn;2qx_V&IJ4W!c=i|h?MEeGElLKQ4Rt$TPVP+$L5b7`g3XiG{Q zM`p8#!t3ksFp=vm^L#ZI2TYR3OPuS680E_*_E^60p1vwyX=Y<)rVdFUzc!E|Zc4bH zab?zjUn_d^jLZ12=jCG#^w%ytv)*5`SM(vWUk~>v_VL|HxtS2u@NQ)B=IYm0%D(*a z0g4?FDu_LO^qs{bTWKh7nF+tz_rR9np5dku?lz!?U-apxcdpb>XGvd?N-*pQPh&6* zJ~{Br@fkw&{-9s;8 z+4R2GZ8}JQN<(f3pP9~BLm$m-dwiS5`H?j0W*OH60}kL3=cvl#A3NkDJuIqBoihS| z;I;m{Cv&`%dE$hPnBTg`w<&PPxAw(zdOOT)(u-4R+r=orVc+odlNR9R0XqJBd}>!} zXJFtL6Ri8XAbd&Lb7GvDPg#%4yx^7-LAO)%J(e;mX=ps(ge-|^(woi4#makz?sNK7V!ETAOA+#D9v{`Z+G`vQ?W(6iy{>@&E{=EXJ zM&NKM{#seRlHQ8By_y%=54huAY@YOO!UN0urL&&;bj{{{jF1vKodFx zv-^V)#+K>8nu6@9hZu|WFeaS=f5YDWqHdy%jGQ`!S4J|T@e&w8wwZHQ=5FnynwGpL z0_);-YO|#m6km)Qp`j3-Znhi6V#oE8EME(;X=Z(x++1;JTzqTI)6F!0hf7mmb(fL7 zqtFQP+t@syqQu8S%X!vE_nO1`w4V|BR-mk= z`XB?7;7IpFnaV4nu@U0TPe0(cnTbX{+ZPN|`V^F-#Ja)MgK~UWVXJ*=!HtxO`SRVrK5#^`s!)2avU!U()La8v0`r!s^1V?s*`J^_JkcpV4E(Q#xy>g zaq*SYf7_FG%)ESN)I6#t&V1%_)frXcB-3HTvTCL_%l98eA^C6yR_8(3b(x)l&-8#OF2@tK3t*l=+&uz;WpVcbrD*(Z5Nv& zOY&j2;pyVB*}T&(V>{C}KDym)T2`C` zrFkzMqIdV?381*ACjTe*q1fDRm&EEjhFn{dU4{2vg%vX;`;>VWU7TkTo>vhm*Wa~A z{^EH|{wRent3Zl>Yv*kL-LiAgygRn?>X^HZ!kh^w!MEFEM4FYS*^E9_wcfpMxiMRo z>|ERop7ojjtx%dreqh{rOM_IOqadYwVy7!z@vxC9w{;Lx@jxjl*7cJdYy z9NqVxSGMdvEY=axsLAw6`mxm~3u)CtC-Ksh&VCm5aI{#>ES+)iI|HU-*o%*DbNm;N;Z~pmI7Lh-Dj`Nrya@PzcEYfkZs4#8YN7&PSu&^O@Z$k(#MmX$-EWT zaQV!CxQvUR`>|BA<1V>9Sf1EqyJg0odluhMzi)P(OjgsYmr&06h))-F~s*^JZkyxBevnkiu4tkS~ zLk@e2H{uh$6pn~-Od>u+C-04$9buU}F{-jVgXzWNT}7utOjx%B9v8f#xgPya+Ylbr z&8&6D1q+`T=w}H%N9KRpAp&7%(PUm7{%}PEKwG(KBBj8B%pXR&yyz%rSa{fY5QVJBV+`oS(AsO<1K{u);R7$QHti@ ztzgJ?uhV^z;Wd2xO#E>fm%`LuKHSJou??0HR5?4t%m_onzBi`m=vs};9CpkDO*I(U z)*n_J=A0B8A#2oPZd~_BgiWRdkoU=dd7GNCit&vrulx#Y>`G(x^9~Lb*;$|6_~_c* zMuwa%H#4a#>e#rnx^@n!x@zk3@NAEdn+4=(Xqx+I>m57e;}LktbE-DGoc5BWsJ;$M z<*h!I?PF(B1~on9J@}GTuBJI}n=XFtG3zUhrhD6)P4{$2=Zqahy(6Uh@?#T!>S+s` zXPGJ53~rFZ1F#yN&L=YxJMTQCxvu-Zq)5lopZD|)mkX_CuVWM^3!CrSq=YdQ-O|b) z9-E|M->iTTv(UZmJ26DxzN=+UsiMtofPVJsokCcbomjVR5~?9?eNh zQxo~X2Zlo5`BBR$xeV4fYg@1bd-2K>Pe~OoQiY2NA>@xeRVY$Da8M5?X@yV)HDn9p61y|K=n&@>_0D-781sf8oUb(epH@X>GXzg=3;l?1GhRy&j9L%qDnn*QOxkp z00u~yJ+EVZg~%kI?`$VcRQat^#Z#E9Q3ecL?8SiCZg$*r+w;B{yA)#_S_+o9V8_%y z!^a16`TOB2npK5ND;BvVU2cZp?l$%|us8O&_plAur!wq+w=ScJvgU$EbOEJM0X_L2 zQ;8E8lz|%ylcWLfk3T_On$U@$p{uBb3lBCqSfR1GZp1CU*S6Q*(s_T0T|-Ku0a7Y} zDjBC+5JkgSj@RyeyKjKi^XgMgD%6%I;lUCV^&LCyYw!BwKF-rAt%d5RG94{wrU~V- z;=1bC_+xp1R_Ea2m~;_ z$rrH$kHxjGxV9v79N^`ZCrq)=BEVHiP*6A)U58oDut0Vu*12y;7y+1Pjac)VRd=LWU&~PtK4&P2#aKe=mnmVZJ zA&`}Sj5;J@B491RH{XAnLNI9)3> zV;oVbmMB9I0-hKJ{Wrb0Cks=_hUf!d9#mOA-006_vn7llh4@-hbdC~pltqDy;`n#V^!N15GZ@wabC90?} zw5$S!Z2*zzxw$*v(rmT_j>qMNC?zw!EiBV4G0sMYa7jca)^pzDdlT5)TGt1WnObLp zx;+l$)Q?Q4zk%88yDwI-c)BFc~`FLd))SmF3c+G#SJ7>q30k;BQer!FQ8a&xUnN)w&LR8 zxT9*CJ#>YtB$GnU7C{s-MhE~FI~KVXBzHFg*22-vP-9vwu8HT=1UgB%2K*)15wPU@ zAAOos#_35;T)GsDqQs;Yb|ex>7bnxUk}Z4u?b>|4v(8ngooY+61!#|dr9o4(HO7)| z2nV?r-(U$#Cbcyc6)`!j0w^iukP?xo4Q6IG3t&gMAo~-J&{0;s9JLO?^5GrJn8{KI zus}h$_1s@;s)KQR9#*r!^V3a~L_JAOE6kfI0{B+cn zX;LZCKI>V3TMH7phQtN9A#67~e~qvWvrLCB>PQ3`!WuFtB=CuY{ zW24JKZA5BqM6cl&?k+t$f02N$)cu(JEzC1o{KA}O^z^3ENkvOh8IJz|%A)CY0jvi0 z1LEE;$iS;II z*D+p-%3d^R>DH)6FO+V5l15xuz?$#XJ7QBnV{-5cfb#1CWZADuN`9B*S{ zI3QZZ)BZel9!&8`(!DX4WiV5D(^S*b)H%5kl|dntx81LxFt(-xNOcystup?X=)9Xb zn=8VO5~c5`!baZJs=$z3ZwNvr8Tqnj&sVJvagLX|McQLMHktElxI%MvU( zu)ta}kF>9bRT*@dteKW$m(MG|lqIFp8--D*gXSGX5(e5$kIL9pdj9~Ux~_?GZl3B` zs_P?_=4H3ze~m}pj(x4aw+Fs5Ec5LD0M%JEanBAxlXX2Yst4ieD?E=aH8CnyMNYtP zsQ#FMk1OhafX{O5<2dOW%%U19nFMmYO7P6nl8lnfrL-o8lHZoxkO+99(@^!4^>qgB zjUzNtNq3#tk*eb4o1I&c=~8cUF_PwKB_?-SEhu@Re@0U=3<`&Bz$V;_+wwTwJYb`G z>fDaFBy8D+UZI1vmE$&yU4_b(Bn$F%pL}8+O!c*WUm%W-6Hzpu42ujP#~}(36jCjF zao+Yj90i9ep`YL@E2VX%r;2DJcvPP@qDd9KNDkUTje%jXHruu?Syb6n{V$fSYezKk z)Y^FAe}tsUG{#Yj6Kgl#02cNr*E@?+796^gnzED(!lN~(nP83Pai%m={{W9j;@h2# zuYPeO(>bLywX(%itb~K{$NK%SdBrO4_hujz`JDJrPtnrcY{m-0@l zD-BzVadWV4`wUm+%6ej(Gs{;sq?y(lxFxNHe`eCsL8(IneoGfmrH|JVwe;DoT$PRF z%F08+B#r^2MT15gYjDGx?6x+r7$q)C6iO*-mNu57T_%-?F;eOjdXF}8huXtz2e5KV zI(jO4e48(d8d_>KLmfOU3}^g7HM=QyaIM$+H#&>Ic#` zf3XL1$ry#{YRu6(S1FQPQ_N{1dWe~Wk2uByqYgp7+k>{pxWwH*PgPe^OPiFCu9ZU} zP-9sM*hpLp_d5g6&g2oNq?)RErCE@~OKU1M*@!m)jmWWG$=_{{W*Ma|R#hgs95j;# zRV^L7#coZ<8+Jb}w>Oxjs;kPg*#%IEe-@#GxOqYrPyy6;wXJRMW4Hps;wrwk_=QjL zLSmQH$|H$FD#vf1Be~lD0KNgdl<_<*RZy}~yfLqmwDy$9tjf1$4e8raJbJ7)!>rDd z58@J0P*aeNr{U$1{K*)CH8JFKW3Vl2Ui@JbE?$!a{{X~_uDW*%uE1(zd*0&Wf8_2I z_pl1{Bs95{P)hJf;Q~uhtI?~{zLE$6{?@(yt$=Y=k=K@{hc;QDo>8KnIe=iXpYalX zK-ky=w$>Ywg05v6Pvz4|1TTHw20p#X``ueDgAbrkEN*PAERt2{kwKezkyDkkxZyie^;up_f9Xw51W|q1D2>Vt2ic+gZTQsf3L?CE~Z37g<(}kWKgBRl&J8v9~?p;gUMK zSAu4cP&iSj*c*$3@;mW_W}c{+ zrmLlN0F?l-RboN2ZZ~6l9U}h#hrbh9cUe|t6stu^T?{b-T~pzqb{dWSM8|vY`da~g z{yx*flybowF{F<%0Z5Zxf2&xL&(8Z}v{~gaf}(2JA+D%+K|vX_Q?MgzaeLc;V{UN* zwL&&&VP{!U#Cp~}TJ-Ovf_DUt2R!=>CoD2l)S7Bmh6v<85c3URcDA{U>HboCTFYT_ zEo=no&!wqabaaXlOB)E)RH@Z+G_c_By~xKYGwNzs>C)>BDy4 zj^lHPhN?<=8c1b2!vM@91!S_26}28OwS~DS*kQcogszmw%cqU}>1BoN=%jh9I9pn`sANcRu&+h;UjRbn{aB^sb$1f5J%P_5*D}fwtR=U=$(i z{J5zglg%vJWO>X^t>ms92~uj?byMZHQb#}2aUe@OG{nu@tII%+6s=}3P+N%NOe zAG=X~i6_|Gj@W`SFrhX28lVPf#nilUfqf`7eiq;X-tBHq1!VO!zGBAhOecULgS4y$Mj19i+m|PtsDYIW;y{v7(2Mtpv6#iV0<}yqx7ViSa zO9m$De=KabzdP&?2My@nG)QXRM>9NS#9EKyw+hOmdk`#r`{7lnj$L%|(dw0oiOCIR z8i%h^pTu_r0bz5s?SPu2&1P1nsLQ6cRMKcyB^a)z7t$WX^Y=JuEb>hv)kjqj%*Ah8c}1;?jr*qe?oyIP8ccz=gf z#!jsUK`XAMi~>lFYybp3{XA&|e1T=m5j4=6Ap{|=!busI$_C-spf=ULg4m7@+^v`8 z5ZBh4C}yRkG8JT+r&6?h>;m5Yq6W)*DJH`YW30+z>G3J)q92Yn@!h9mJ< ze-YdpA8bHDG=iJXfW;yOd&LxGq=A18^`198-uE{4z)5A)(N$CYM3t;0Sqz3fwMNGJ zgKPsZz4jVS$2?)SZ4EpV!Inc)GD#)YM5uLrI-8pgno6WHxMh%Bz~(gb1Y{d(xZDl3ZLzo;e*=Ju zuIu%WGt41FSy@v_F#D=1W~nj8#k4fSH6k{$>ab&RVlg9?b!|WK4~$-&>KCVuK_h?I zNQLY*DzmUqr%2RWf)pP1#P(m1*BWP$C{V!AjYiB1fB_{mn<($SwifvZxo%-a40W`X zIYXp|!YwCQQ+-Yf_vY8_vF&^Xe`_$S>v`akdM=7T37BcAhHS?&iYG5(51A5beStRa zE`7+yDf~d{4u?uXQPNT7l{uYIqo-W9rF5AJGXTNBDePF;03SPI&Ss;ir)laQStm%u zkpQI>KHvdsc0T0Tn;&R;z>!o%SuG#m1}^qi0YR`;Dh0sY?g=*}gRlT0f4aA-JV2<< zss#QNRS6@70nwUM2!RISLDViy?f6Og;w5MCS2wa#^!7`UYb1)w%Aq`w#kgQ2fU@f5 zz_7WnzBCN2^GdwFN35x*yq}BC088eT7qDS+Mw5S@1bKxWWib@AkxuHW(z1x`&8Um0 z7uXBl*0$Ha0)<)7{ZpAPf1LjSud_elu0-DlNfdAZU38MpMyucDu_xt;Ors^~-k+yA zeoaFiG<6Z^mY~KOX+aj=M%AY91KmtNn2GUQf2h@I?8}NrUbJq9m3dH1+lQ^ z``DZS8$jg0J*1;npstansF_cg&@70>1nM-g0c=PCPVHf9f(@_{f7O|j)Y=-E6Ie!B zY-7_Zt-!N^>TR0rdx33+BPNqBaYG_bsYMhVbL`gGrW?g=D~t^l`eK8l)}vSN|D zdGM5p08}dWHw6I!g@y089Q)zVP|(Ri9Ix=S)XEu?$5 zW0^)@O`g=Z#>+g0Q6m!=6A%{qG=aEXtaq>i_uLEtB@;A0YRfDd*vk-*FX1-x7B#nR zfj!OvR-IZo#YEK0Qe<)EAv%V>Zd=oC0PlMN?So|*l(}Vjf6NSVREWz(6fO8uQjiO8 z*!JU(o+Wdhlx&t7Y{g)fmWg6%DcYdxa#)LM0P|~n_OZk+UFS3nB}%f0(0&vF+!1tr zE_b^VbE@XtcL3risG4I+WQJK}mqSyfw=CT1+=kMZFqmokWC)xwf9e@JTQjX{y6P{kM$b`}RvC%72% zr&uf_so|OlTB0PUSmb3=mlka?+hct@6K;Pj1U5%mG)wUBTP@~&OoBoKkPV%#Y)B0D zHvVJ2`^#m*3OveHc2O6Yq`PbgxHmTKx!7-p>6GM826*{;SAwy+!lf9dnUPDPt%RJoVHMDG;^XOz(_Y{4fR z0U%So$lAx){c$OpW^p|O%HAy7t5=Mw*{{YA$)4!zq6&vuNe84SRGm5I9%^JfI zjD%rtyw&(gu-j1slWoc0j5(_`(9JYa%Bt=PKbBuZTz)HlD-nD4?efETp?ZBZu}vv8 zVFZU#fiCtSC?|^&4<^9-SUEAvjv1SE%#YX>0Hi@--yW7PVD1JKplmPpGe`=qc@Yh%4zQ;LbHhn({iXzZY|K9CD(4Q)pM0E>Rs7%pQ`PV-U6PctL=7YiJUy4gW0 zO8y&e+W~W7wa)8QK~)WZ#8XYJ49_b>;JJv6u55J+fYuwI#78&|iki7*rhvv`j68xC zl2?ev7_qYuY_bZM zo#u^GP(lL|kgaNoTZhrGI&JB&<9l#fH1z_aZ-mtrf+ImJr(92@g*t}Ab=Yx!Er57} z9M?3ce~6xCsc(pPRc3PbHzQ$d5I491wy_vfFp?~)pwZ;%Ek!zC?|iYTf7;|RZ7pk( zFZ3g|u+hyd)ih8;RXnmrpL)wG$O_r7q>CMa;OBvuGO&qs+UN@o z1*}N0APxosr4%Y`*(^@KHTH>+niho);z%$O~%SOk&r# z+!23n{NZfzvYHv9gDrQOvYpsZqA}&5PB{8<4Yxbtl@rUFRL@%N9XzwMO`}ex0N+zw z3tXPy057(l!68tPe^OJzi7b#^1d>7=HI(0VzZV>GcAga+Sq+i^=&-T2X&VItKUkVY(d`JAJE_x zg0d8n7@jGbl&YDcOKH(_a=Us-u(2fX$?byFtg=)XBA?2qe`uDV29aiH!#5?Z>1~&5 zcLRJyB{Yfu0G35WHqbXGliR)h_uyXE!-5)8#CL{c>!Z9X$o~*0c$Vi^uRev zQ9(#%WQeA;c|KLtC8F&5^BZB^?}}5bd;x52aLq2FCZjiQJ8_SD>pRqb`PqDz{iKBZML2D8Y#b zx_}nkfN$wH7b-TAFf5oge0}WP8(w;hu(PxZG+FD6< zZ7N&&K^yFS>~$TFI0Q2KiE`)^&p{njFG^vCSBqdyo3J3><9i-?+t`^TXgu_$q-LdQ zG|y)RSJk@<4V!>3ZgB4=uFWIMs{~b*A{|RRfR^(>8WnW|=>bdKNejQe8N-<@8GTF< zR+O34e+g;fP!ZEr$zywfJ8HLKYuI2C%`+Jmb4iywn8UoRG?32BNw?zT{6G`1JMu8< zmadU9DRTN+G==J_FC>!bx@Zk1JbN5e|wX4^>4-j+%GDrs-l?^7}bS=x35Ry zxw@7D#PMy1af2m)#IM6tOEIFoz<`F&m%mllcFuyS-~Wm z-EC`dcqiKd9p<~l(?HS)`^_=xXw@6D~YB66AX>0|gP>Wq@K zwjhG7<#GdyYEsALeZkvffYCCpsg7DHDWO2dIMrPFnt&s7Yj5)IcEm+I^%Z&4f98gI zif2BijTX`!!SxZO?#NHQh0Xaflr!b;9HKR-r=C<%1jLp~aG{#oKpVM5RlWZJ5x6@X z7A~lpqh4O1prd@k3UHH86*C576R9Pwqzy_+lBU8Chg(xz?Rb38MlSd;wtg$lAQNzd%q7 zYO=^E9qTGf&F813lv?hszs%TP!#D z@;jE()oxlGZ$J6jXIFnuc$O zQm?uWG^n?yQD!_IeeQ7gKg{w|0#ar1`H^UjN2(6GnrB1zVB7GEur}?+_QYJ(HPp1} zEh4#yun9HEP|AdaBoHsP>~C)V_(Dn1jDxM}#(VI;!|3EnY`lmob0}iG zWq=nVz!RlN8he1a*M88HRS%d_2Z}hG;L#%dOi)H%`djlm_9wQ(7=~qIY2lH%?NTPg3czWe}i>!Pz- zx(BVu=kp?`t=)!=H)Z@bBwoW;17o%L7*8>%sLd+gu9BUNC0i>Xj-6sfu(8shohlCl zbFn?QaVB$A)xW{=+_s(wRic__4(!r8fWBZXbzO^sLY?k=U@aLXMLjCkQ#B;ANl_7v zEoFGse|>tk1d*hYR9kgj`x||CPG$4tr!bPR(X})AF%rZ6cu(00P5;K7f46nU-f2UZ`rNaI!fOokfg7=FO+)b>x%j z`d~C}k*5Cur8Al=+M23L?~KdSG@g*Mi9}!+f0KcSEU`j_e=uRTtAn`hzWX$*q^qK$K{jES#?ebDT8etv5u#}{0G4)O$e~D7vjM5U z#fleIW}ZDkl)+rNCP4$p%4UIMQzOc<2!IF+3l?=y&QRLq>EJ%;-oEkPyC#lzFyAN92tQ={kAQf2j@4^qelI=n1|X#z%% z)uU@2m~42(hO&34sGd0^c^;+0$febqSvlOBSbB}S3){8vo%l=8)x7~0ZB<)IO${!4 zBr``#42D&wh-)G@)wqxs17o&@e>XVQs;ix9Xw3@5wKRccSkYEMWkLaVcfE-qxd&m$ z9qfG7T{tQk_M19e_cu$o*nh; zWGL3TvX8_K%G>4GZGch(TTmyi%T}T@8BK1Ui1ij?3u-nS+hetdZg89BVx`HiovLJv zDh66cAx4mN`gYip_2<4MYAUNLsVbw)1!Y8ajI*Wx00@y|Izw3L7SeY&uwC%thJuEw zoKGbJRF)^!`UmI?GjxmQ<+qe@KBJxmX!9yFYS6aWG%wZPun*nzmV7*ct` zDk!IsQYqmp3`1Kh>9*GFqzn3#@IJUK^wmmMn!cI5%0)Fj!!O zRa2P@F@(mK_=ZrT#_9mM({ae+*amPYqNSN?soqA7m&*~@jbrf*e_E|?mi?19A^GJbQ&;Eo}`GMxJDht~98z{2=MLxYT$SJ8^yh z!-*>kRVHyeNmWxMf3gM=BfAZ*&2TS%NVT`I0Bwb8>m;X)#7fn+r)F?=02^6J_uSl% zVmsY9b6Mk|YDcM5dWm)U0FbbPK_u);*>7=s-qye(5lvgwErI93EORCp4BVZ2}z3*du1Z4DPx^)!m1q72ZAV|de zSl+_I>~|Nq@5tN%OGQkT62x?erm|>~GD~SB2)pa zsbp2Kur|9`18V?I!%w~vOwHx;o}x;KqW~E|=D&@Vf5byrF{#~3Hm+Ek*xO;rz)QVY zl9DywzxF7?)t|e!L)4=7al8R^|R#rq)Yi>1?Nd(+;upnO8hN8=;swpccIn6px z6WTb1fKt!u3dGv`jrKf?k5&Mc{{X|GqBQTBe^iRXcOof^> zf3hS5u?_3lR1$gZVS90dQbqhowz<|iqr16%7$N9I=w+nf84EzzMug+jrYN6p{Sv!dU>k=(Hg|F7Amr< z*lf%IExzMocOY1sNmSKR3R((Rf|?0wjGk=J+_Z~w6qW#8O~E?uZZ{st*0gzzGeb31 zlDAw^nE*h-+v+#43@^X6fIEx_V626F(w^1;<_ZI+MVm7vtf0jpS z-n7L$dfJ+z5vfCtFMATZ?xb9c+zqgWZ^UI;b2eL-)YroTqf^rvAL4af-4U6GnScG5 zupQfF4*Fj=q^dq`7IuvtD4sf~pnY=%km;5uLOmqdk5!v*U@QpqzGWtL1x#-e(pAw2 z;iwZx+F4mnnWDR!qgjgGZVkf?f39TehHF5fso{=zLI(>d^5j(GNVWB;7CZ}F3*6fg z`JP>tR%Q?UF0L%Trk)2{Y{0@NcrRc}DAXN&(ED1!tZ@w8QJwVUkke<`EQDzC>a%K4 zTAoDNi#%ZY(XV^xV5D*}(dfn#7u2a)V?0Y&^Hsd9v#NTtkZYgD+BH&X*B zVlSu;K?dWHb#TLFV(L$Ut%EMjr57>$JQeox;b24ganu&FM&E=7CrgoH6}KThOO{1Z zLY4Hjam2*=EfnFyCfC%*e@V5+tDXmZHR<|E>uOk2q=D(*P^?M4aeyo?7~JeZ8+IdX z8lN-Fl3A()%!yV>mq`h;a)RLL7U1`}u;%Y_Y__(#wiH#7yon1I8ceMjByd1BzT4Zk zq#cM&Pn}g%!kJuaOE`oNze8nxK!#CssM~G(9(Lap{KqQHJoJ)yf8mX4qt_TP=l}z2 zp(9B;g}o-j@;1MJ1Iv%ThS+z z6p~2SwfD7+xowEKw)hAba=Ou%)5AoX>Sxy&Rb@jW-ABFGbYrZLET;P$7Mhl&B$QN3 z9VBj}%+`&jAVt6mf20l6-_i-=y|D70j;^+EhRbQH&X`-%9`_4tfZP%GSh z>na63TT?+CO#WrTSuJkbwj>fjAd|VZfEEPG!&4-4RaeBSj#mClm5EcETEn>B*S?ZX z#tD}BH2zn?OXfo~<(RZ8?FEPdRNK_HXB^m_$hf)XbXAqrf0AYSEj?_-(@c*V>t@_t zeeH9=xGVv&zQo9?<;!S{aO+sK1eQP*%HBZW0zo5iyD0?R0&GaYO=kTlXw_w_nPead zBnc@cS(npd4w8K&ZaMnmHlr(^RhDUIRRR{4F&J3{x>RWZ1L?U_dy9*En`4!EtwwVT zLvaigh>0nYe}&|^{6LZ}dw00Cf#e@Dr=-a8s#?7Av-o5)NgBu%&zR`oTr6M~I#e5# zQ(#Fpz%}w;$SS7jB54{XcP{d31eFVQ2HTy+%kO&{GbD8rPAcGX@|sO-`kY?sTUi9@ zEN*z`oHjMtT}?a{P}!|#NfL75(Ubr!u-qN)4}SP(e?u!&JVIJkSj9pE43lXRup;Qp zeLG#bwXRMAvE!amm%;oZxu*{$G?G#4fr_ktVk-$7n}g+@j^vx{Ha>3Z%6yYAtA?HB zjzEV5nt*{VTy&XhzPgjvoQYf`Jj%ie(l|Kjy z2HRZvM;|O)d_JqA%d4Tx^L(kKkx=>sRx(Ib3yo)++V=duN%XB%Ov#x~OB6p309!;_ z=&JWyjnAwS8o(R=*tA#lf>7$e$-X@ zJd~1Axt3^J!%5o8z*@u;+?~j{+ZEb+mzH_qcM-9XmAuCMBVF|i_ct~e(zC3T(`FLq zRg}##fRf2EkSLMZ>a$!M-GD4d;Q-&;8J1mJQ-JUg62FgDc-~)E&Yhlk&Rb~`yDqL z%5pjhz8qDASfA6)&9AG!U2>OA%L87mxYRPjv+J@EbFO<f@rEaene@g{d zBrwx!9f0203Kxypi2nekDe|1gfm#a6)_E!{2bNVD$}CQjZuTP9(mq(G{hQ8JL)3ZJ zSzl8dRG8yRdE<#o%!_kiO@)u4jmX(Ydt+$ozLcZu&YP1a`1om`%~qto$g=+cD-|cy z2`su#@|)iFBg)yV9ZS%;!q!k^f3?(8WwNy`T+yQu<{wmQ9FI^s8N%#z@OU^6S7w#e zwDLs@;(Va?^^_4M4mq5!=9CZ@>Lxe4|7h+l1F|fYj zb|%934a~5ob*arJ41xzpqh@qxxL^q@du&&bI3sKB;)5>|Mcu+w|r<_;O5ooeD~wWFZ)-Y^?QfhOd3 zu(&?=!j;s}Q_iLumnz7zu~=Hl=H8G=EvCfp`(X)IYJ1D%7YEW;SK$ZT5o6EDpQiW; zwWxwb_`n;=@_|C9e}BVk+S_hV8*lT&d2<6zs%h8?bsmNU>C^bFe|~V@n=vs@EQ+LS zLIZ2pcPZ(x<9iTowT}Y?qmDX*9CcMJfF+R?G=^=+--B;+b7S(r6Ex47uQ8|0ve?xo zjlS*aw$Ls=03_+#lVipA?7^w2>eWF=ql^}DTKZgVYj(KrfNAiu(ma#PrGyT# zd1A$gw&uhgzGIHo!X!^4>7-*QYokNvdbJw@NVy{9jo1s3^0}9%q^Ihd$qiO(1q?CM z$n7MmEh?5We_0f4qU3F1$pYh;l%`2!Xe28O#~X%{GRX35NG990&!*NE+V}^PPnE@& zO0gtP%(o91W>^Th*?>ErNWJv;xw*1sDiU^vNg6oinLw4{B_vKrP%aN6dz16`YPmf$ zZ53P@gyhu{B!MF(^b@37Co#5CbJ*Un0%7IdRFB~?g6kM5I5h9I2F~43RJY;6E#AzISO?ZLI#2@ zV04l0ZEuzynrd3sB9fxEnwDY!mSh4z;Q==Y7`p>-yk6sboJY!@Y{}=nMM#9WklRR+ zIn)3on}R z!>@HsANdntHU_|Mc_aCb9oN)RwqCK)QmJN_&Wd@CrwMBW0{)^K@d5rT?}FwXDVVN9 zL!0~*ElDj$luRFZ#zMOS4(r9mzs0@*m2GJgQq50DQW`};c-4X+2&&Fmj;rfpK_nfyF#BMhp{8u#Bv%qNJ^je_+c-iC`4fA}7@wl19Ns*41v-xNC8SQP$MW zQ}IlqmC!VkfvwOo-9cahJCSdtu$4?NHcQmrzpCWHm5Gh@=&b zG)D#9g_Mo~UQZjF@D2h!t1~HNl1hxWN=Jn}*!<~&C}_dbcGL*EjtY~o?|cmZ0HgYU zHHsY1Cd{YHGpBDldWt$_j#d0Cf1*c|SY+hc_Wg!0@#HjhbOQ9&~D|Sf;QTe@0kdH`Bg` zvXOfM$+^-dZSR% z*H&fH6aYvCwE`Jzpy}Ke3J&)s;L^9Dx@$1aYx7Qp>NAq&l*f9CD}qFt%PmAQ+yECq z=)W*GU&f)Ya2nv@)cosyOQD1ccmqFLro`Hy&TAz@7jNB=Ivb&2tzl`hz3Lb8N;bCWW&c zuQ6IlS5#i8fS3!Ge{EWnix4b77N8!Urm2Q%gpzn}q;8KfP~;2lZ(=M6u(A2!t#unx zLgXw@PnfVs^s6+H0BqV+8>wOsxF>sd#(mIVXdaV#cpIo=OuWjP7Fvjlqa7!A3JFkj zk$u4@U@f)NCh9z&Hi|mDr#7L@5?8!w1ww0EcXT6IxnuZ-f8ZZ$U^w%|NUfnNV`~&w zN3$Y^H(LvGp>J+5*vQng(q=yl$R(Fr%pwZXBJ9kX_9R~CzoZk+997XpBuv7fw2+|? z=(|Ozw{m#0fo-qGB1|w(O|BstMDO>Jc?VU(0qWCnazU^by_O|GMeE^w3Rgwih7^FiG$e3Vm%}d;DKv!Zhow)>nb$IRZkM1 zGg(K_7tAEs6;MW=NYiTzpKK~=CV|8`hPIAK)lgs3z-cOf#CF^O1-&P@1Qbb1$ZBJr z2~1Kf&m3qjt~Ls7Z7uR0&aUGb@7hZu>YlmCXfq6^f1;&oayj+NzL5lU@@&t&!3tGP z&jVHaV?nK}%csrdoYKsNWhOXcK+)+^KpM%t__n}a!rKejW0NYAFM4@ltA)g{HdG5`s~U&>ZqYDLs=$=NAzA;@4lTU%qnlXwYDo` zw^Zb^e`Wcp%5upe%~GgEQ}Gny>1P+R-`Kbtllt2pB=G~Hv(E_pL5><)rj6l|YUrhb zU6V^ODiF1=X1d#cH#_6l9Szi(pIUV77g5tkNkv^f8#P=s`m{jNs16x#una>3y}`YW z#sYKrJwvRt)UcI$c^&E&p|r_m)Ejcf;XtqhfBYw&apJEXdQzvRyhf;@%d*O)%PQkD z(n1ul^2-)*wj2g6zCDpinP+u!K1vLeQp{E4L46KX7DKr9az8#d##i>TSZjJ8EvzyM zTE(B?Wo2-NYG~C0#+Cp*i8mkm4awgD=A4$IEVDF9YD)R$p#fS-n89By&Yw_IVsw+H zf5!JB{l+$G*{7Q)qMI|OnmVV3N!B^yv5;#lBs}e;YQF^g_Q(2bD>6qd1qzypz9HFL zSU^!xUs1WTkOOc}HW%-uRpzx-^f5=9*NUo|S|5j!W+ZtbP0V_l+Q~ONZUL}11dy1{ zy3#et6CjF=M8vgBvm_dwAIc^;*qy8jf1lNk{0vf}I(mUuRZCFE&!}BDk4W_qds~gS zJB^R!jUu|98X6Vq>eC4Tm?l1HlUq!t)&peKx{dw!8)00(r3X-DH1!#M4K+O^bdOn8 zQXNLP;Q%=WznfA89Gkc!RqcSP$QL zwCQ#5_M;-BHLH-+WNB9F9JZ>79VQAb>r%qx-`}vtYe@&hP-fY_Wdy$#(@638+Vsr9 zfnw|qkbelc9myc4avUG$7mJXv5nDacvB50bTHekyes$EsJA6ozk zAm8yEMRa{_dI__hsHCS#389fAsEVZ`aVkVaX&W_xYpN&&YBsRIZxxw)e?XNEu>Ha+ z$t>g#EDilP0C9Vhu-N0wUNmHMT|d=Q=h<@Wqs@e#Q!KuG5!Mr>++6xB8tZaY6L4*? z-1F}fvJBgzrRn6%%+$G!5lVTf)Jahr1k@UJQ};y~HJv)_EOC=`_egc`TXe2@kxflS zn#ihiIzNY=864EcwkVL?e@Q8HI!FtvUW0H=@D$$;DyHbJxu(cEjzLufwNFJd(MZ)2 zYLToYjbndZ$}OxnTX!mPy7kA}HzuX&YOJrP+L4he5|XB(YsWp_IX8+jZ(vcBy@*nH zQb9go>gtGcoWg9bH>*kf!s#`9W*Vq8mQa#Jq=aIm5;ZXv03O)6e|UG{uU7brTNIzk-&1;;MkH8lPU8C<1jkJDzg&2gmQ7vL$>hUO(npw9)f+5K&Ar%< zy>oq(T!Ugr=Nbn|_#4r=8>IQIGj$zu$M=)guDGIdKm}q4O6lM679zy)jWaFDC^}~* z88%5#1ax#zGngfPe^M^?vl0L-+PhxD);Ab$l~cu0sv0_VtZ53OsPh4ggR!;kz_)Ng zC!K&xEFw6msInR*G|@yAonx}bbd5}OfKUbFZgv;j3(#d5#Z)BFOpOd=AS|RG!Vk9m z^M(|a)wx4fd>hUsNQ^UUb^(C<%LP(w4al(f`R_f`8C3>lf0eCNnW|H>IUZPqy{rd& z?tT7uz)#oaFjZ93l#y7`!lLsBWh49}n=OvVZvOx)##f+1^z^jszY@zZV#dNIUDd^p zy@BM8+usrytra{2%BPf6LmU83(t;W{*m?mwfqkqyZO-iih$Ql7o)k?286*<}<^yoU zlg+QFUe^PAf8ZjcFH1L?p_xc6tIGuGAYYDl2XA|EhfwF`1Zz(Rnj;FvRgn5vZUG|3 z=Jvh)g~kwix|(*D7-Lj+lUNdxK#UK=ZUE zY^LqCyXyDHbkWD1!SM4LDcRG`Xld1S@1&)zru>_Nc{e2PB`7GLG^nh~X)CH%Ei)RE zEX1~@U4s$a-LJL+B5CSr#8V`Okk3wIF~;m&)Sr>C8oxpT>@dqNRBCwZS(r&07hq&< zBWn*(e-{SFx#auYVtw-J$mW+Pqo#_oR*Bb3t(}$=>Xq=Ik_BfU~KiV+DQHg8ka!K&Mq`+)EKxo)-7uQR@NT(c@-4(KxA}G ztp>j>VToChnA``u0Bw8QRgJA}ut;guCI0}re|L)Uw9Dp1F8l%-$6=>d`|-y3j;_u= zA}JuJjUrgs`craEyWihPzWeYwwgJSmc~G3PTQP8|!b!tfLuz9h+ep6m(tCWZhhA1$ zQ<6mlG=*n?g!8m8Pc4b`u>jlv0KWTL*exYMq{(V&7J(_>F=)K$8%~u))OB(0PWu}H zf6eh9Boj?eYN+P;bcum8fuS|&EpISwuF7n@>}_%dfk`S?&sk!r5Wby~DM*S3U=TK; za#ZqcFJs?p2cEV{N*I=k7^x-mBx%4Y<{RCYQiG^&I0DD8zUFBpiYi(vXD~F%(i!D8 zEX<$;^xQ7O;YEoAUgL5|DCqA@YGPn+f6`rBTN?{(2?Ez3Ud67BxBvnOSJqS2)IDZf zQBxf(vNgq1`lABcbuiekI}dA+Hp6IPoYS;l#A1dTi6a-Pm(i#x3dC}KH?tl1`eA7R zuRu`0%+gdCJ6lmF0Iqq^-7THFRCPsOS6@?G95crpbfITZ_kTWL zopGUt#enWXv7}sy-Y< zH@r=z+BI%T_Sjf0fg}s@i9EYDr>4whcY2mkP(>m}Om0~efJOJPQoqc8cnS`Bz9{r$ zQPDvgL)Qk;JJ`l}RjB4^icY&?0em3QC>|on5;}=Gg{E zT}Mv}PXv=s0|&h{PVtMI0p_a!RI?GR8(eG-0xq}3EP@z`sL$Z74=PV0Q-926&=}k( zAnt8{#bABK#wxYA(r0;LNpos>%w{1}l2lvF z)RQXdj-zrJ_f9RoBXsKwb66AW}C{FJrQT3BH_Ul%6&9$3a(oKZw06nW3qn z@~KiFzFLIRS%YZx7FN<&FaX$-d*Zp)A80=e`hjU`rsxWqoZ^>MYkxgeU}S|$hFu{J zlW}!oEO~}_QPbzx_;2*%ahajD^l|aJhd@v zE2OcG!9xJn)oc2#k22*xDD`ZarA}MX-y4&PsbN|Q3Tl^@o*}iEyO(Ar%)1*7!9}kp z7usv7G8Utv&0vAF$bYRl_*B<3mef{VV^Az^OKvZxl1TyRT8{|)JBC`E;)|zgX(y~r zQOg4?YL<4pn8-1!l0fwf5PwX^syrO(Ltj&v^tCaDS~{OGE9o9OYdGAaLUudfbvv7q zNU+4`MSY-pW34Btf_A3LC~2V{OU)nv%;1eOH~?zgkOs!gZhtn#@iPc1XNp#pSP~*& z(v?&;7rv_vTMe#w7Qj|0d{EE2TcI#}@@;;-MNee>L(tJLZgH6jjG=*buEn(^xxc4I#3cj}K-k!{D0Cx8oJ+?DN6Q+A{OUK~o(`X`re~M2<;gXI&sU0FBgaNE=w- zEi_b7)@5<$P}NE0Bgpj8M#`lo)9h`l@GvEIxv;*(#6wp#Y_&4i5553IR!$IXNjKPS zdwZK5ueKxCT~Xon47Bu}CkA;>QVg!LiuR5z2*8tRSbwVxH~cIb;9I!F4OfiWg=SXS zZgY?(X+<m+Qtq-Jys6xa=}EK2-^_5>-vnYv@eOy4Y8T%#kC;U<|a z5X-5wd4G*?3DSdqQk#Yyha_)okHxOJhcSiWpv;~~%W9P6EYa#vjHCu#?WB@71dsijBrcVmKRZn{=qzP|La|B*8>um|9>kt8p8c14u_*Jb*QDzt4w-~T zx|ymZ-Q`6BMDM#H2mb)T-`f*COXD9>^)5?S1AnDfa4S}7TB=wmL&+Lx(Co!h>KTXz z;9AxjV;=D4XohbETW0B#&qryZoJLzi>kD9c+?5BByLKcH_C+eaODJJ9RT05+Bi_am z+HR!s6zBv1EE#MV98|n&$msg3!er!`x#p zZ+{Z}POJ|OMVaQ=tu#fYSt;4wk#@VCy(&lo=jH8&uz07|=)c74A@< zNZnaj7q=QUdP%bj18W-wEtS-=vn4x5U~9`1Y@~qKRlUjD!@co$Ws5U0WO-!NHFB*w zGRPFf>i4sw7B*(}wb-7+fViyWk&4zzGk;Eq43a@2n5;3zPN@Zg`E1fSG8QL*0N~&xvy7^ux1lmhRbwo&W)aHKsNBL} za@v`>8X1khJ@>`u!w(EAbIJzkI4G**mo}DICaG5`6jX*V?Do^8Ln7N#e1HpWSbvh~ zytk+7>$-IwrjB7z12mMC8thbsR2Mq}RFD|&^0xTdItII^yfv)qDhh1QSTco#W)fD_ zF`7KGC}B2NLHCSV!yo`DvXBcL2b}Zo53}xw>IJ6G(w>f*Kw6dvBw3)>8jGp1Wl~8= z8BXU_`-|vN$D8$sP1JSo@d_$4(|`5U)V1qInr4j1T|}V9!$~OxJ;H-{K3Ax4fiLK+5_{tn%!-5Uo)a9&=MH9&E<{IA~x&2p^2 zKIw^;Ph6Q=t!PAx&eW@Qa7XhC9qqrQe6dKPug&^KI&Q9{ny#ve<@>oKl<0Z2Y%EQ{ zDlTp>aH7}?UsZTD;)hFD!+((G6?AcC=Hg0ebVnTOPUFoC$mP_n?m+BpLB$*XsOpZ1 zmaihM$>pT0AP0_FL}HoEh?Pb3+^X0c?gI7$8&*ljSP)T; z;lCE9`P+km)|n?t_|KPA^u!sww7LFVJo;(ohs*eg*6NKauAsoPIDgrgrrZN;26fc6 zGWes@xm=l%nzuR0qeY5JjVn~q1l8mhP#L2tgGQz{xwesgSAPmRhboeWyDiJIT6~yO zwG|}a5jc6Os#Hi+dV;W41QknK_qgtGdK&D^-ow56j_O{A={#?}m25w|uZ zacpZo8qLzZThuh!hJR}DMVeDoC3N0w0KqHya-eBcsR7iu0nu3M18b7NH(6+5qN^17 zkV<;!OnT3%OUO#GCrPlIiTMum;3~ z!LTFd3RXo?0GgtxsYw-GEJ1a#*+A?`H@7DD?|fLR^U7ADq^6*nD8hx0@CFTK9%6=J zvs?>y8-H66oi*plQzWt^Zmk2WN)e8c!j{>+M`aeU-FF+~nVAb)9BCChQ%VX4j!;4n zcO|TBb~m=ow*KIy8&yvwMRBNCmRL)P0}xylBme-pBYT^Ha5>)xN-3vGIOB+;ykx@6 zLotl)sYR`EcI-ctj6)?X)N;J)&XGo}P>@20Hh-{FVZDW}-G|c)N0`Y-7P3teg1|zm zx<%COaj^>9j>q}{cUhSlnI_57f+(egGNEY>xpemjR?J5B{f&kpC@E=XsCH(Q$*@&; zi^(Bl&6M1bE^dEKu)btzFs+`%0(JwA0O7n; z(2pXSE9$Btf}hTnU+&f^1=*EABW=jOfL#2K7CNwaxFg z+w1`0v=tG;m6=t3Q%~lzXf-9+l;`m+_kX)s0xe_ju)scS$~mc_r4+~n3T1H^EZWEx zE>56U=r6G3@vy{gQqBBAOv~Z~rb>d6GojMW=4{$ck77W#ZN~P(O-oS~JarBXl=W=r zUNPlH&bGFz4Zyk)a4t{2_y@`{PcufKPxu}NR(6u)Yb0_kwy-0AsklCA;PYsyB!8r- zor{xBi3!}92@A0dKqS}!u>*T>wrK>hW~eHjS|nIj!J%zTmMj?TZf?fxZN}W;Jh>P6 zoK&^W)lx#mqF^D9tt4T#ra5+JGBx!S8@m zG>iCbaluPd=SmPoB##T)GCvv+;(z{;?O+YN03Sf$zhYM_qpGZ+6;r_^B1kV#9VNy*q8`1G&I5&Z(uVFv(3j#R@8<#rBI@mMm9K;U3n%eSiDnAu1rL z(JZuam5j)I&~%e=y5H-)zxLx3IgMp}wMGPnNS$Pn<}tpg)j_aJTI=c-)u{3;F&Ub^ zZED}aqQDUde*J~RNQ)6>uae#jBLbTw;D=LNEnY3$dTgSzSEC~Q? zK1UJR=*=nOmUdAj@Wy3{k$=h!A#6hx*j#@#j>7xmQi=@1YFR0>TFkPFDI)@*Vx^Pf zByy(KmD@-P#V$1Zt_7C(hR!H*{KnaBL9WhI;o_%`O3H?42bKv?B9BaDD_mWbud%-S z1uBBSCGiI-gEjvEqca(yqmVTfVp3?TT_;g!S|$=3HnL5&G4r+XPJf~59LJ%b_?V-Xvt)O#+Z>5o2~x<4XjW3 zTXL;MDb2F_YI>L>rWM1=&?PKU18FL^J8HkG_qiiYw-^l9GW$Sfgeu99n%aqNQ{~ZD zii?76>(C33PaJM;PJj4uLE?{HOj0bP!koroA|q3oVVW4_QEf^Ks;lZDg^h)chNHRD zBZ8iWp{J4>dLc5pODC2~85CVs%VtZ4y^f%|xFFjQ{WDe7Y1CP)ok@|UZbUN|j*hO6 zF12 zvm1GfW+KAik*7)H8@K-eP4h0V%pjF<9N?$dks(LdymSU*(Uw>Mvf!_Dn+jDYr4AY}LLFpQA;=dnT zowO>66-_f39uymT;Juh#g~%rNJYNB{RCr_I-fdW!bq-UNW(k!*YJ8fa@{n70KBC9e zSL6?KYgw*S&^;STox_>Y^mbb{O+AxTS4w>sXzwC|BPPvj_Y6k+0gLpuZ<*y;{Jl$A znSaXlQ&S1%XsZJ=YCwFciVzk1X*M98EC%-5dN-)sHx!wDQ%5~a(Jb+WyB*^)0#j?U zi?^h2W;PZ!2W?A~M3E|LI<%^&2V!lg?lm{H#C~I*)8v&pE5>6S z@+_K7EiAPOCBV35MREnL&98oW#~|t~ihr`ZFv_x;$SU(XHYrpdH(dq)00;qqB~KuM zZO1&BifAL9Ss?RGLa-Aez^sY@YnC7qe-Q0rEwQjT1hOuxf`STn>Awy0$c;qoBQsFN zOYsGmGqVYbdP^*9ZZ5{#9X1&sjvWD+_1zrZR~LoV**^ZT-l~qnvQ_^72%CTN`cz$)VW1qDwx9D|DuW>4aK z^(685X`0hz8COxxtzlCXP{ld|$A5PPlvom|gzEDETY@hO;@3xbk5SZhU0c+YGF4^p zJ)_Jr#6*cBEWSx`rqQ2LDN|u`N`eOP*(Zn<9|;_JwM^61#ZJ()Z4lArX=0!Y+QitG z_8#D1E7kos`~#`Fm#4F8iixTzB`{4kjReR`dVpba3Y9k4?`}DB)0G+CYkwH&+_x)g ze7u-ewh%sE9CSzTF0Jhb&yk~s!9m?%UFtOH1dlFhn?C4E){0fbRz z6!|87Nt#fGnB+NFghfz>Pk$gA%7OHN-CsKu>>}v z2^J~_0_T_biP2RZN1FJfp5!#uxxE!JrpgT+I;#1KSfT}1*6af!E7-T=P!D_GNoBPC zDd7%l)}A}(6j>yFLsd0aU6tl=nq^gVP>A%m4I`FN{#n#O(~mm%W0_^W6_?jn^-DoC z*biQOj)^I0Xl&EM z(#AJ~@QneRP01`a)wS#b$I%(2RQ*p;RW%%+1ny*|r<4L>q;sibZM{cBAPi4q&eqR7 zQRrOLqk4hrGTNB49+=Li4@dAaiRs{X3{`cjG-Z)Zh1*t@y??zejSnKFsmx@{o>K&i z0cho#o|)r_#boj%V{JjCS2oqX(2J6GsP!Iol+fj!L(-j9)2l6IY>oUXy_^py>N**R z)DSk_%q(yjTnPC^ctxl%v_@HDoG_Xum0Wpo?h3KLKZtHM04@fV;5K_pUE)_xL)5wa z6iHK6Q5#DnT7OmKm9(=|DiQ{=;1;n1M0*ubF_tIC=GoOenaxThv#4fim68~4zO!Y>}jVIh~zM@v&tSy<~dlpIWXwZvaYwFU(oi-rX#GJ;Hsye$r z&N7O{mYMT9snnY&3Feb=a4e)SAaXVq!8|;!n<(lUe1FO2ib{WqQ7s&^x;&8!w=2Dg zT_la~Yhvj^;KgrI=6Q4UerqKzT~kP}QCnXTjz)5toJ9(f&Z6Ilek<4$xW%KX{>wdC zOH=3M(^Xc`(^0I0GoFRvh^QKa5CO6D{{TJiN#u)wz4&e7S4U=9)VXg^<_}LxP^?@; z%N)EI$m|8rt0jwH_TckK0-nxmMw!Ql}j)eR$YM? zus*I&hiNi8thX?Q<;&zV1uL!Gd1MequUON_1Xvq>Skk;a>I~O2@eUfC)YQ?2gW(WN z_XO$Co}vJ?t?DCgb{Gx5mY!Uk(^W`i6S9RyeSb3`AIoUS05G}RRks{r(r0o~Q&Z7a zW|dTrO&^pIZlHk2a5Q_8KP(!TEto(gYKDnm41hB+4Auc}vD)M5&&u(FDoWRqS@T*r z2yH7I^8i2qa5WE2?`^Oa*=#(=tr1Nu>Jk=kz^;-Ab+v}}E8g4t09)oO1WJ&^j3lUH z>VFgO2z67<#rHNO-;#FY4B6{4{Mt%&jgcW|V4`0%Myj{cuW~d1#0I&(-#jClG?l9X zr;bQujr4)6Dmz;1-r((j>|hYh8d|!vlT1oH$Bh+tWl&hKbMJCC7ZyL3Bhb&8N~>Kq z%IO@6Ztgw9VeUS7kf*Dv%QGl*IDanu=<=X;WOzqrJ{MbvO)nS9F7Bh`q2rb3{kru&<% z*Oo1A+uYn?o@t#=T#ryBN(p+>vJIn|tqu@PAZQOp6MkN?B3C0f1sc6k-c}m*vjGoH-S8nps3_C-{a{ z5!4V-_PG2&s*p=tgX;R=)b!Nx$dOmaD?1}c8okDsxd&Ojt}I8Vx6c4-EYRij>r+cb zFP~mx($dT3$si0as=xvij^_8U9_J4;oc@lZDHQcINGg^@m{g^iqJLX8kPZpEZY^|O?d`(vom0}y6mTHbPfYQ&(YAx= z1l-(Mk}d8{?O<#%BY&CFO-GQ7x{@djIG9hVLiw_=J8NTNbpHS-J6`_)QC?P-DdAdr zV3WlnOXdO^_a2hyzbX;7dj{&8|| zYkGmbuk;P8>vGC@6jOP1DGRzY24&ovg3N9mY~1omJPu?d%c!z>ouka5H12{kR%R?B zQX0tGELag^et*P&_gN}Ppohw+Owl~Kl!+EVLP+OP7L022>98zC!ARxMz zu7BwWgcCuU)e5++@buL3%%v_q=(Vi3a&|k7Hy0x2B7dL6$~d8Vs(zE|>8fHx5P#VK zf<-3dQQQDoYyh{Y-qsim9tt-~VpR!>7es$8Z>Zm%1-B%Pw(c>^zMm-)D&_)33vl*3 zq>FX(*b+30j!z_Q++v|8h^>`nEt;=~-B%aHp;|98nTDhUYSOxO3$@8>0CyHQu&dN~ zrJli-8Gq=yXFs5*^OAX3<+9X9(U%&dl|`&dUc_J2Y!2;!+^OM;Skz9?#~LE6tYi%% z5LhY#4*iXf8gU6vMFwE*)b$S(A?*NrD#WC6&u{@dTW-MPJ;{7y$ulRZ$hyjo4xG#& z^2~9GT4)PMw~y8@r0=NgbdW=Ddt5S(qv`z0Dt}1mv$}e@RJ5*%SdOo-`_&RMg2a!) z1&Idtz5(2oO-9fMe@WLSxS|@N1SnE$qf=Pe5^uL*h4R`M^J>ak%Bp5DQb{zhp^U$l zTm^YZzv2pP81QdyNWp6=W9iC9&nB1d0uxgbLh=wbSm}@G6qC3Xz3q(lgU3vcKD({y zw|{A<@_RK*H8n_JSpfk;%oVQ@3>;a3Cr~7hGS zQ&oUa(UJH`+-^>u%X~)W{yB6kH7whbNX zDk^7=yD_XYzJQSlRB9o8E9v$;9q+ZQalm8^CR*~W0$8=8vV~nH`zbfn;eiS``f-d} zv&XsRshX1{={e-go>lU`5;a&&qyu63zsa&c7rNTAI(+viY?_(|l7D5J zF_XxiL1rQ#6<$SRP$(sDsmV7Nz*am->0IBU>uGb0z2TK>qToR_*aU0XUVJvaPV3J6vVpWkRMDrx4kPtu`g@xE^Qh060wjcHM9Z}(BWh`BN zlT-D6S(up?s+p@QLPan-Y#nsGY=1SW4lW7wpPetJyg|(=BF<0Cr%STV1b=7zMZIV~Xl;@O`%TH32@%b@kum_UTBY~x7Us1DwjG>00 zR>aTZ!t}mjn6&wyQ!>=d^tG9Bu2_)7s>=GJL{=9Bu>|sM!&?=Om(OeRtbe65C#-sW znzBo7+165gVy+k$Ok53vIp24_RyBjl+^4IHxd23bz9(#4rDs)9J>jLgiE zej^K)lS&P39Dvv;D{z45&lU3ykfnpGI(X%DuuoYcQ^MpXV;w$^RH`3dn9Cgivl7G} z);O|I{h@LSyy~|uQI{G@xPRH`VwtS;bqyg6c3BlrIo#ZlV{k>yuOPdoa+HHN=`N%hgasvMre82mNU0!CGHWSS zR%2x$oAC?S8{j>EQ<>+I(>k7+%QNhkHIGzllUA|?j@m-Y44V>MGJlIafUqZ4*BG}} z_*2x4Uh!oe7oTJaFlwnX>Y5o)Q<#8qbw1Lks52{H@mqV`P%^%`tLeOsT4^e2^BAhy zQBy$0pkZ}k3nZ$1$9Uu_)+?gwE(pFJ&*O)S8RaESJ!N%$eFSDX-BIV1q%k5wfs2xK z+ejLZa4%p04?}ow(SLcbf}R;?T~(cQr9_=u)p=)@DrKidj*0*oK<2*&LPTro)Y5rn zT#}r;Do7xMO%*hhbJCQSbf?n# zMYZm%p`z=GzMJTII+m7_JdT;6Un)cq`4PG#a<$wU8Ie?!^pWef;0#Jp^gr2~qN?cn zlOpPD_Bbn}YJZ_JNfN=WnkgiXBRJOAbVsla-$?{8!SlMG3HsMFpQt)xp%bYjl4C5Q zTA7sj3gEF=BXJ@1y~}6-HT5>w3JnKBny(P5dJ{A0^~@?38kM9q2qT7h9X!~ZeM`!l z1}(ak9f+oMui0Nark_6Q{;Z~E_&_Qkq=`(G8cdpG9)DQW3k|p@Vh?<5nXlRpKFg-1 zi>11EAj``{)O6Lg6)>cc(?%_?%+%}zVCpJejhe#uH%`0lpPbWEWtm=06(q7=3W}*8 zSdg?}Gt&z-_d6L?KypryEN~fRFHLyImQ4LI(tRV>?Msz(*Zn8!%c^;d>bOHo-Qo7GLFNAdg5aBl}Qz zofA*g8U21)GgMT>>rGb-gwdjnDH2Nqqcm(uxFpKkF$4+lz%b4Yg>_I^&b@8Vk9}mI27*IM2&ps z1&M1}g~p?8Taq?U3jWA>zf4zC^*@YnB_x%VF-P!GRfcE~xFS}PGDF|e3ZqzZOK>vE zFBE!$v#6+gnwDAw%QDrg8X*DGep`*^A%*5(9#C-%PyM*P%l+}K2HrY=ec%i zG=Ec~wLB0krg&*&Wees+uc#pwE~)_1Sn0L{(W{hrW#P4TMqlvt(a8-f$w^jfS_NcK zE|G>T&Af<9Y23&yeJ!U9uko88InR|JT4&j=ODLMumDi@22@vX2F6#06tOk^}t-Bm! zeOcnaRA&L9m#OD*B+pX{NofkrW9AjV3V+IXUJra`SxKwwZmXf`XtTMp$fl@{niN>c z(4>K=UcfQi*xzgg@2k3#r{Ab2s*Y)>>DaKQ2^vist&WKA!m(h$n*qrHUvzqJsd~ez zYoKher)QFq7WLH3zD#L*7WIA-Eqf}0cN=3<>0buAAEyzes>z_tYGWcHK43*eihsd{ z*--u3k@6QByYr0;sgjkJDkx!OhA2R@pwLMm3o!Zfeh4D~*CfpTI;KrztcE3N1d6V&ra_0<)mA2~`$ zW{e+81zq&>Ymjbvz3y=jB`}m1b~3faI;yEJYx;n<7Tj9>@C@jdx`wIehK;14gen$~ z(uAq7YmZjrasa;<7iMgahjls9LH#wv$miD zvjN4&*R}BDH;zM@!d_;ljK*8com*6jZzq^})YrH?+S{BYD`;oT>3@WX(L%BYPbrmH zEs3%*7gqILADzg(u!dn-nYvX;Uen7Ov-wXf>dvTdWGr`M>Ngh_zqPOlW>EhC4yA!3 zsDiFwHda#05oQF&;eaY_&A7PU&Ag6FO(tVVBy{q|qZpq-vlRbka$hMKm`9SyG(WFV9BQwxfRLzvjJ%1$A0LeW<`79B|m<7~D znCcg?2HRjGr;Zq1C7}FH!N79XjYgXm8}|h5^V-(Jl++o9XF*?>($vQ(rAZ}{Nu5=U zWCimQ8QWmtUO zmInO&MUVct2Y(q<(@iw6sgfvOD9cAGi#3hzpx;k71Rd;sjqGzm@yR1j?o~Yvt}rYB z+fm!H-oyU@QNAkQ3t1gQ3hH_1iIFTKDrJqAxYTX`0I9gOi6MA6m^t%LPfu1wL@hnx zmu)eIDlS1i><#b7^a+=f;pwBKjzx)N^4zqLu~uI(ZGZJ5>+`k8dl$jss0K zWlX{$P^bWng_%e#=>Uyp!rwdLe9tgh=7wm31zm7O<;be38v2TX>NhG6Zb>`a723v_ zNa;Z=o>WXVMK1cm7xgg~76Wg7cEB--Wu=X(Bp$T7NYg6FbzPNo+!8PI8g56+4-FMv za??}F%6|wzu%yT?`csm{d$+2`VRLKW2|V-E#Z^^38qR9a%F1PC)QZ;yfYWnfe^(oG z#uccJS{#}gC#j{of+So0iXNL7TtOGdd%+^eoF@}u29e+fXa!)JTLR|zzkS2bQVWU_iscO)nWV{LkQwWQOh>mMkpvAjybRAYq4aJt#@8^17-uU zvA|Phpz#WtoYhoj`8Hs}MunbOC#H9hp}P`{$$wEi4lEC`CF%^?n5}kg(T$W*$Q6vV z^?wUgh-^iqU>iYZ2h>4L)Le0k3(B&J&xQ{7A{Zl{Z@gBp9%$OYn*p#p9xMsPw<_qI zyELALf>_d6CW=^Oh)k;@wx(MbP!A^H_Z(ZC1y-9q%X7Iz^tqKCMO5;dg0#{JLmeTU zMxgq5)Ib0Y?RzN1YUWfkO&uC$F%N~PXn(4jMGDI$@29ap$~{8Y+*omG>aPNN2c;vU zr-!B}o&g!Xp`wzisekEd z>++b&no0_%hFL&hOi{AxMJ=ol+8J&}t6Y(3&b%zh>MNuE7t=XXJd|@uL0wSwI$m^; zBqd{66}?Ot5_k$ti8jPVH`%`;>P+I2kE*i_k1MLKtF=$S($rI=1Mq+(l4gtoP22AE zU4IuOhI><<^xZ8CQ1q8u=BpFPjel2@W>t3+LcxfST7W$yt$+X>R=Fa}HPbyOO*1ZS zoOpwfY0RnK85yb}ouOGhNROCp4P!++x0@Vy zE{QU=*f6&Jh4;Srb(?q%;e?r0MKwNIS4Av^Bw1di%!OBB8>!NY2w%nQPk(Yw0;|&> zXWoOUVGd!{Q|2}CO!7@nO<5dJ#)%%O4V9VIRZgPC!1Rvh!o`;KPl5dr5UrTe3d#fK zK-G24B9UP0AVzY!t^wE+usmA%+&&f4%hXAh_;Q{N8Z3(3KVG_EmG|}l>O5b_wm5ZY3 z05GtyYbhq5Cr#&znoE_?8ai{T%*+T<+kb)(5(_rg1LtE%r|^cqHjb}1>faF;FwABs{#_j!!whPRC8^jD z36Fj*tSks1oCloq)1G89wp*CRR}N)H%Jp$bEp|3=#Dlp8L1VBt+qNbo%-=bpSSw~o zYGRH!8F=E$zrOfmVX6WX{M^YavdI&id~Bp zWn!TEg4i2?8L3smj-N8iz5;nF7FuX;8zqtI0MRJk#N4IE!1|5G18(RZv&cGDmo%X3 zXef@o&w%+r93bU@D$W1&M6j_B7`Xr~G6^f(J zfrwy2F*g=qReyqQeL#(|<9fu7TP``}`zs7W8psA{{rI!R)VthNhn0e_cvwfG*w z@SE)cl4SicnRPUjGgIa@6>(F+G@%(LrduK%0qv+Fi`c1VD}8{{9qBItX=+FDOy|Rz z3M{&XmYQFTqFQsN%NYhn((I%Ws>Iw9P4~nPQsjLF;eS)*)VXasEj@K)uM`xwk{I*? z!bh4@sf#+O3N9?j2Ad2&>aVn?h&i`U<`ns^M=8oH=e}CbQW>I-7ZJQ{G@mhpfXvOO zcU>aG8UA4}U-ida)Aa2xOTkMlp{9Zskk=(S@<}Y-q$=S@XV_NaS4&Q{iO~ zR%b^p>VJ&GGo~?6WywJWO<74x5eyN?2$7EDu@=3{8w_F|82DwJ}YZljIvaZ1ms-76LjsPIPt;yh`sGBs3nR7X0b2Ld)I>@gLi3=uzo{{ObwEkNRN9K85 zSASGxRbrx@WSwDHU`1Oz2_39}f=K`}Ew~mv?_jEQ^i)u-WO?3iQ$-XoNbs0hWfC$- z%BWNuChFsLABY|Zx#;@%2z;Er3FV$>BBx4;6stokI++L{*y^>o-~rnk7lxfHlz5Go zbeBkYfzs6UR5@i_^UGUErU`Qje=0VLl7IVZV6)rN4?31q^do*h#!ZI-N!r`!-2$s`z$_1@ho`)uc0O3L^yA zT$L8~!%X+9vtYU11o;4`sggE|Lx1I%#UzO(m?CKw(f~tyl6NGYb-tA7T<4|px{QxA zT+Upl_;Sk>?U7AcEldJWEPxTCRM5fHHTT>Tz{URnt-KWJo~WX$ntanWV$nuv^GR~b ze5lel3l&sy)S09QVReABfHae*VDYPFH5IQ(6zub8Ede^oX$u>L3$qQyyMOcBY-n8} zmS+?lSD5s@MqMOTk=8Ou^uy2cw7P;9d)yERYxW~yd-~RYu$E6*&{AZ5HISrTRT3nV zXHdMA5={)tdkRNoFs;;uBpGfPl~IWtpX@E;hfc+rKTY_DP15wrY5pFPlA?8rsz!{7 z<#&zz#dRX;3a}xL_6Fe!vwyTS$Ys#7phQ_rWm`c|OWb!oj{MtVFw&)SO4>T6hN)wj zshVjfshL+qirDU@hObtV+hX-y_Ib>6*NZii z!+xNhsv$=6x_&I#X{6inH=K1DKg4vX*ja74_BTy+w@LM$LqnW&-fKo_q;#f^nvO(B z;gq7f#-M?y*~j7Jaer)D$4yI99B=${Gm6Ltkm!DQ8P9B z-?D+e<8!v$?K_guWjT~}0;@k1l>tk|t?sPhNeXs0<8gZ)JM9YtK@73Ym_OeHjYP4a z3#4{m@f#gm4eU3*_-Rp?{6=R4briAGu%F8!X#zrQ)*-KL3r}lI0qk7;l=tl8aADRwbO7oGWWk0o_Sqdw1{tj0@G})pJIw-df2b zD2i2(vjrAaYyKT4(zkP20uL+&05L$$zqf*g0?QBu06##$zqd`60%0!&02M&$zqhW% z0^Se>01ZI$zqgFm0yG1E5t)^B07%(Ad)CtT*(_A$F)}hTA~Fv9=7%&~tfyhCb>F;B z7NJ=t({PcQAPr5lTqS89#ChM`9jAl)ROIl`yqe9TNtl|4i}1?)rEgxW)79C=45<1z zd7P|Ttyghq^0O$Oo3nsgT3iO3%#4%#EKI|xS%wC0&Fl4eW2WJMMYu>-)ZC%@VUwRF zaq%i^odp+E*v!)~$jvHE=4r58qTMXt&`{HGMlHsZu+=&J;pq>DU%l;|<@xG(Fu1(D z?60`Fev-}y)Z}0lt}^`Z@b>N7Uk$ol6U0+<8Jd{7OU*pVTS06R%qQjMW zkyVNWEt%$7Fgd3IO_Jqu9Y^`5XR`I=3?SnH5Jc(CS+ZWBZ}ZDK%0n{=R>7D$NM1av!*eyjBc?|FVStRThLD9_9yn(@MaBrX-#h{6ESS=~@_d11 zrru{sN{hySh*+Cr%gw8A9=KtXPqH_1F?s9yqEW7-Ml*w{2#!W!DY0#e?2wB%s`gr_#J zH8)j4k;vktAwlBNMn)CTHi>4y!FI|Rl$h5+L}ZNKC}v#}9Yf=jvydR5jbQOsix4{_ zNH?v2Q|^kODV4u4pP4_H6JD4<1F%A0G9obZC_h_|`&4}}9tU}rTntjwIpk$L$>}GYM`JiC7#8TB3H#A_f)`fW?Lku~U_* z;N2LMhhReg5jJ3gyc9c{qWxPMmv%i{gcG8F6kf%37R;$oNN?9!IPLMO1+7}$vt&VY zBIwg_=p~k!Uk8{dZJ9LunVOc-VL?}6Ld!yQ$wXO;u_y!aNX%KdSeZ1+2>ECqqi3cQ z=-hya5;A@kPlN328~UyUmwLY>0-sE!Jtl#6bhHXtxC+vM$sq711Jcm|vv4BFO#6d> zjz}&*#0@ka+pCqQkqjxA3bIohyn8~L5*>c}5;aVRa5Ku=)YcO8aw;tZSw=K@Dqm>h z=d{(5@kNxZGoTI;&1f38b8N)WtXJleaL(B6CJh6PX?$^Vi48$#D1F-S*}Y7%e6eYb z13-o_dbyz;PNX^`l0aCIh6EQrhH2SkzJLYMB%+ZAG!z4K$ zq*Ue)1FzXbyg58N{QB_D;q5LT5~q7^Px{S2590Zn(0Q-KMFh*>SK=bj0csq7dNmGh zjl(CwMKnG9b`iwEJctL=)!_D*U;o>eNB?&8$3J!j-w39FD<6vVK%l3i>+&q3i83(* zCKAR@7GZ$HKh9{W)?5f=ssdL5A_h(CM@M)xSud!XY`e}PI>*tT;yaywjLpBS6Ks@T zD_aH39Yll)g#WYy&upO#{0qQuw+$DsrZQeYh-U4AKXu!PkgwC`T7}T;&(EWn?r*XH!s{kY)SXLB&m?qe0%x>E6 zLZPVjHGz_LKh|L>>qGl0jFa{JjAuVx)3<2J1u_gOTT|^zjEg8QQ3s5N5dP5oOih{d zFkDGnQQTUBPT-U$$k5dOoxlK!Ei6rrBl3qQ-x}sx%y(~IJSSMJHnQ0q6SvMVtbeV%2Xed+t}iyff^h29b3Jr9W5~xID!P(ahxXjyaxcO>@XlQ-wk*GqQc*U+C;G#`-y{h)nd7glV12Ein|R4|S0sXQ zGTj&!ZsQ9`Kq~i%v>^0nEc|^$;=*MNCi1>n&lk**m~~8>YzjQ-o97{DzDfgTuT*W% z$}t-fDyRXP%b04>fAI@}qTerRMg9_|)cIp55MtBe({l=`0*lcF4*p1#W%T2lZx6p3 zf$?JTNM<^V7GStUyzXn!{4y~I2RN<6?BKwxSYmZ8ngzrm(+SQ4=GjahjEQM9FNs4Y z&VYXs^#wMLMk8kL@w{)w0S=-WgnVh6-%rCCpN2y{m%I$2hu@&25;)pdvuhzUdr1I(afUNeCaKZ16ma*H?pAC+6?q^HVv_q1Z@8 zV4wj6#HaMA`M(j2lFLlX@p+y@&jQ{z4VdqptrvZ}PoFuFkq=SxXW};M5~F4C-QmQw z3HfHxd@Wo*e?)t$upl(aX%n(S1V13z2(xTQhsj!)V8Vu|ez+2@?AWau9jj;j{E3+h z7exILs?CW4vf6?q;9#96lXNkI-X-+G`-B;qHE`~mkzuh$q#Wpcau#?+O!u}sNqzLL zg{h@9RT78^RnP=DvZ5U0BI`-EAQ(Tb*3?0Jme5$!fA%-@fuy)Foh{ao1+s7%5v@w% zR=F${lxI|;I@knW$B8fr866t}!zwD4I<2jQZwpXT)1^`-Nh>BRU-t!qPM(20)J|t9PMRSf>q55{YHJ z98m1j5d@08X4#ZWpVPjiiqI9}&-` zkIa2@$2{P}$Q+wlz*sIM_Pa<3@F`07%olI_tmS=Y-iPm{NN7HkmlA@00)@L<&z`6G z@W8FpqHRmNmw;kF9q-zuA&vY6@cEbrThw6k8Pq6r%kDIbW??5GwvF_B-F)jIrR=GY= zf$N$mx6rWiLiwOQ1fD~&u)szje{=x3E^xKOx}tQxX4YIuxv3mA+pmqfeeO*uqV0~r z$*rJjFvU)To}1Q}ctIfFMU<_H2b)GSy@90J<(JT8msgb=vCG1{dUD41jW}k&X|l%a zaI3Bh)rl#a1?g67m?xzJ6!(f){$ioI5ux%~{OKd>)5lbqTF=Q zxjqGz;QAdq`mXNU(j=G%<`%JhA@qn&Dak{YG3-!J9zY1QCj+DZs59!B(dh_!JWJ!> z;#GIlL}cl(!x+1L9(;vpe`e9IB|iUUd8dtTH8oify;}tjQlogc%JQ0a@Qj5{GrBwr zV-{Se&|lKu(Im*i5$>{~*0o$1@mZJ5SxuuXn3Gt;OkRE_Of^B#o=a^Nff)_L446th z1z*Cjseo}xdTOCjP0y=PyCB%x%~8{sm4(2B2pS}DLk(r_-81bMf3%F7wv&G4;Ch)5 z`#2Ax_*T1Cb85xE`{wA*?MlTrNn+@vpO@>siI!m>+TJsWhjd;Zm83@vD3K z5&WVn$6c=$LBRo4m(or1x=A)q@tO%XPH*?*woc8PSbbU^8vYHw?H*sQ^Kp&pYo_4Dz|Lb<)g?3LmWcI@iAL|zp6FE_v`@)m zo$Ha#%}5FC%sA+VL(T~UxHtMEtUO(%YI?YotSEtWLez5hV?0Ru)F-QUZlEo!h{va>18w} z5+1TO6)k1ge_K0;6y+7X!Q$O$-9B!~Zm?Q6Sg86M)s{!!H!f1+>Xcx)-qb{kFPta?p^vy5bo7qPm*<u z<3e`Vf9O{6wyaIT@{I0{oGdT!FE{{I`a=b^7B#IdTmi$K;(t{IMI86hy<>Zo01@ZT zy*OI%(--$n-*y#1`>^eFTe08oZ)L)odZ1lU?t@;qUg-HI{W!4Ufh5XB=xml)%7qE=evie+!GfeV$I(58#I*iPQxos>tk2Y)K# z`R_r~wG|XUkw&+%ysWftEn*m!q)bSC={VjtZw+Ge&0tIve?P@FhudUibUZ!m| ze@pajN+eYnVHHN}wmZh!?g%Qdz^rP6{4!~os?koP>ZB*q84X8_m&*DjYD8H%I%`=| zGU|^^=apz=Vv4j z_r2A|HEhf4%jbflX?u!9@dQ2v zRhXBdM%*Cuq{X#_WoZieO-JEQ)0-?2h>{7;@PAc~dqBdgLS2E6Jh!5w8$$8Me~ae& zX_^(WXPZRNx&k&g#GBeeK7`I?vMybfF2fW;E$vp`wZVdUw;VR0y2;f*c!ooln>hth zxXu`?Y?DJ&V=*}TrF4(A0w>IBaXR&5IZf>Ko6`Tj%KQsG@V&+& zG5#e%F-=w};4MaY30FK^gz-Fwf9e&x6~rdavz}h!sSyPqCrnS7O)fY>Sq9wy5^GFN zT%#{%2MQh?`#E$N$0Aj7B(QlwIpzfD=pHVK@O8xM!=Cdm=vA2Z;dwx3cqdPeO?#Y- z|DoLp==+iUnt3y!EV3pnO@h#c0TAB3o@Ji({e~($g*6B=> z_1Hn?qetxFW%J;D#BH92S7PUcymHr1t;~o09_Tlm&o`Qj_wT0D^8VdyhNct4)L`+u zSMQpsUpL#EO|Uj6{ZdQ~e=SSn=t#IwgB4JbGz~VzO&7=qlLzK@R*UPUuM!>LFVyyW zNDbn0^R#`k3b3<&<6oUnfBR8z4&x*!9By3sZ4GrxBePBa_YIKi?i{7I{x5>!Zv~@| zMANCVXRw!IIgvQrImsexoQ)Ij&NP9)W13u8I}(v@h*QnCTImov!MB93J#LFKfUn{n zaFJN9%Ww0)il&q0u0jz&hg%l9L+Nb2#Hoyv%Wr5%&EUVP;QabYe=;#*P%k!-a9Ff5 z)59O0Ip;r-k&GILt=6!Mp~eb@k<%A3XQVc)#f>Jjb3|N5Fj9OTEu&n|PnyA3x4}w5 zo`e6869*lyf9VVw@bM`u&v5_GwvXc2LLA(su;ae@n)GfBY*}X6fN22(FlK+bNjMcd zz@iILZi&R%!o_Kjf3d|wwgTG#QyQ^=b+ofpGU1s~qhj{Pv3AAkqzL|KJ%JU4 zx{Zt;g1&aTd|5eqnVcu{D2{;HvvrLNHk!-duWxWJ4MsiPTK;JZ_*3ACY&Di=-61mkqA~A zEHV<~Gs#&Iyy=@4N#=Ai?EJ&W2sRQmS{N6yUt&bNjLwq2Gz{?|vQDRR-%VAQ%-oDX z6Gx`QqK8^PsKrU3Cf~TGB*8fqGyksWiIsrnml2KXe^B~q8HYk7ny^=RC`8uKS-1*K zhbG8do;wwaVDC!RV3lD-1LTWQCdAgQ=_1M^cC=XM>_-;m4SStoVrRWe)Nd<@%Kg10nU$?)kC_c&n9{IF1a`0nrPVz@LcNk+&%?@`Eif=G zPqR0QW&=Ug-G(3j#go|2qPf=A?a_2})!Nuy-J8Wj2?4QEH;ZBrRf}%x89~z;4MsXK zy_YE_?cMfYGie=RrEU z@mw97e!oA0?I0g?)|DIfb6yYtT!Iqhf6T-jzrGpVS^C?M_xQg7aVvoMh5^wRAFtgr zBpHkE_2yD?M(l^+FQ}uKAhQjgShjm6f1z?SlZN>^jU~zk&EUG@p^7bT-dEMdcVD&} zpw-PDs?8g1w_@pF_4*BV4QzsFu7m(FHsLHxVs*@reaJ?jB{jEo6Vd95h*hMk;AOwE za#Gi>-AO1bLJ()l%Kim?H_lrlyNJxw5n0)KHq(X>UkhGCdpAlWn*{85H6y&Re=;uO z95LJo@*U7BhjaC-ca&+i=Txzr4dA$g(XDH2M$uNA1na!5AChr4Swpyv77|UxqR_2d zc1?G26ljlK1GG6+KgZ!*?vx^S-(%i8fh}mlW;O;yR|@GPFX*W%plrynSXmNoqks?FcXk65XAksLtkA^K`Zqw{*2$qZwXGV}2LR z7>Ao-<{d#6ZY=AMm>=z%f7hV|d=gev4edh>;c_8|qTFfj1e%@2nnhbnE7dnPh!#Zc z3+v$0SFO_Cid{gcre@0(tId`>EfeTMH|(p=JhL%RyqqC$O0io9moS&2A9=L6c1Lxt zVD)~xFvz;8kSz6XeBhMW3^zP)7Sp6I_TP9ST#$$KWuai%H5cjqeE9Ic2-vvA@6aXWD>RQMSFVqvV8e_HMGrwLUhGQkf7m%>u7 z7@;|amr~Ch(>AsyR{PDgBM+D0+;Hu6Put^&1?@NPL%V0bQ0iTjW@sbqX=! z1tD{vC`V@_EwI@EtR`qtJE00(;{+F$^FH4#_f_kE@17~$(Aw)BD2qj5|^oD z{$M`TrA61^$2ZrlO_dGJ>hqON;V`OcSG0_`FG{=O8cd?S`EUX{`8uSPm>wTLD%||M zD=7(A5D=N^D5AX+moQp=nNQD%?HFbW$`-}P=BVF4f4c3#t$Y_%v}uv|#w|7-Z#8;S zz=_S~ir^vDyTE2t5nA03ucG{x-U8m~6TcBZ&|lR4xju^I++VN2GSG6`~460teYnEi}l!;s2^v8?^1{qsSsx^!)^5nKP071hhK?0$l5wbHUc%( zhBbG$e=MMvN3iFMUg}}$E5f zR5Uh4dX)TnM#z=2X-iJP<_9`j&f2xhG_EBVS<}S4$ItgZOU-nzL_)o#^(w#9x<#$x z7OdPCbTpH^)*GJtY!}HFvkyA$leV+(>Mw&;f6)aO6-+6I5UBT3u2XH^eAv~nLqa^@ zCSPtbv4iwBu>ozwi<#JkbMxx^Lb6h&8DnA3Mj6}ajjx;(**S-Iq=8?s|2Rwc9IcEa z{IWBVi(bGu6UZ%?AaR>zUOr)M+^UHUPAhPUX1NPr@&H@wvD)8j6szz`PZYJ>QA)wy zf3S>>Ht>hNue3oz85XS4T8YI-BscYX;B)w1k!ZPQ-#iLASZsoOHkLcXlp`PESf+E0 z1`dA{1Ow{Xn>`s5M+|0DC5}IimRs!RJ!#kwsul%lS+20v+BGRYobeJ3^W|zdjjVC> zlPK*k&*|M1p@Rrd^|M7)DKR;tR~`M-e+3euh}lFTyRoN4`_UDT+Xs#VjJ9S;`8FF= ze}J9OxV`RgA85~%o2d2fcgJmd?TI@!K9sM}X^jeRI6ks3rxGGLKQ{aCsDcJ`X!Ncd z$&Evkw+|Cn*5DNCt!rZ_mHfx>OiY-au3m1~*F{`}n|NYX8*6T*K0n&hvu0nWf6g;D z8fTjx4aF>0BQH;3kLL5@f8%anC}|eco3$uT|kJT&lrWj&9%vTf&2A z1MiLT__~Hp(f7L##jq4Y`tkG_d+04M3jBh7cYhKuh>UgoQJo&E$@$x&f5BpbO9S&b z_}a*r1a29ae7nK8Zakz094j=O66bcS}@VTB2niH2K=vRnkXwkW}23zr$|tjq^H z+BwLAS-E#_zz17zM?0S*f3$ay#*uZUAu@46DF`;2~8e?f5t{hKXy;5~ZJ zgW~c1`{Mbt&+N0Ws-zMlaW%Ar&?CltSgRXaTTk?0!H1GpVfSsVMAeZ$59H6WDde^$ z!3lTNu7KT}g;uJF!4~FHp2ly>_uQb_-4GY>;H%taI+q7nydgBwl_tp8>s$vRVr!ga z0q4_}Y?U(*jaVUPe6gBYpTDzzecX&`ZNe?hkBm%F}bcB9*0-{!DT z>EH8$2oQ@BJGD&N3$V4d7Vw(z?6|2Nt*=bS?oP|YtS(t|*J?P>-0L{Z?aAxc*gD}7 zFYjbz73dk2F-l@;0=WDX23ezf_wJ22E?iXTQCMrs5;8|o2sIX`Gx=7fF0w^!teKX7 zCI?Ki%O=Z9f3(J8XQs)sT%f6$NcLOeauzGd-W$Z0+^b+jhjos(yZq^o{A%Dz3|s{b zTCja}`cXCv9(1^yZmH$Ix|=-c{ITomxF87s)92tH!SKw`S;69kS_v!SIMV!kHUWl2Kj%pK``5(PeJ)3!M*d@)C9TZ;!s(b~9 zahyfon0mZAPQUBx(xZ}pjpwDr5meQ$wI?EaYc%V4L*z(}&W=4@dyz4`n7f6VaCCS% zQXvMZyKsczcmONg!qv3oDuY&Jj*~B9rJ9ld~U|7WnJ{4?&!eb!{IK; z%5YW@mU_XlX^N1a!R0x@q-gx`eg~~~3(JFixdMYsjOHJ`HwV<^z=BocerRLM_E z0@HFT6~o7+m0&s5DF_f!>7}X+z_}=sbe>2mYVRfj&itD%s`cLGyk{=n6NHIYe_Cv{ zT2~QTudL82eGV@m&DbZO>0<->qWJz!;kj`v!A!6aYX zYb{nZn9eHUD+TpX7Jo%yYKbKbe}g4v<65;wD?RpkBs9gLXiWg@(JH8EPfiYwR+!x5 zYuV~v0nlfQ7vkR8vVgO&j6>Gbdah8MjgI5>^lVcBQoUq0FP+;qc7xbq*q}ol+9fUp z%$aN?b=ah@x6MTq5Kr*8+oP^$PV5`uXDecqb9L+BYd3J1?8&(da;4kie+2@49H6IM z?nsh6?SE>|^_`oy{pr+vI&GUz&FP)nJ#%#Xtroa<-?Z=CrWfrx_b+4XXk9utxn}oc zYQAHR?t}UCokU~$fS@*bbW$xai4Ofnc(pas7D*Zmndg_I%I;U6buWx6@P4u|C?#@lT z!Xd@c=$utrQebice~ZlS-w`W?UTPE>zW@%*SKnW!vAZG?e5WuLbnJxx>8Rn;Jq{SU zl?CD17iL+dy8UE2ZF=pLqUG1WhH299zC30RrghJMzRts;kQsG5H2RP40_e@@@hnN+ zy4S?)^98*`f9MzoX|-Op=IJpN^6QI5XcR1l+JC);T5$3-+)>yIyWMazT)P+EIcvR` zb=RwP05nct%G8>v4ZES?3Ve3LrnEcv_l=}5MJmlVHm?$l(Kv?rhl2wSe}aLu?Z&spT}c&FpzO-gcfG!ZEO8mtx)+q~3c!T$!FMbUot$P5#_EDp|T z)%kLOe|3198Qf z`BM{kQR<{pYL`?j0h}_gJD$)qs5W z=FJaK3BUgKanJny#dF${$X-h=?Y6xVyBkcCf1kF&q9`_ee6w=ZHmRTpN>`U9WB8Ru|pMWv#eK@j9?&ZzyUeDoVI=0 z_@d8;hc{m*h#`t!Pni2Oq`kzc>D^+>W-JC>$onUcUq5~L;@QhT4^Lh`{Ql|i@xzm+ ze;N-!1F5NM;ra+K zI>~l$FondBChP2=m=_PnpB%N#u6P@3e;cVxwKI+FnGyR7j1Xm3f>GJalUWqA-2u~Z z4MfFk9}0GRe7V>4VPtQ7!Ye|aW#Hx^VSXgkn%uDiJz;M|yk@3upbV?}q;?!X(xAqC zqV6UvU9{0~+jxYl!KDfGO^*IsmBV=ZUfT&_ z8ZvlT$ltT~T<`8(GgDT-0uZCLC+wtQ)@=ruKOR?Yx?c602D$~H9M4)eI_wXL+e*}}sdWkG4NV=hsal3uny!fc#iaRQYIBICujF?!C zI-|RHN2=Hz;aW)I$=E#eeiBtLqh*vLn<>%QXd*JNO@^2^Ip_n1pLe*wYaFUQfY}?Y zWw5e0SDN7-MgTO?sq-TWbA2oRBJv*dL8&2;G<>r_*U$s;&s>tjbHMW3e|`{;t&wjB z$+lsV$5yZ40l;sp5tL^{B6`~}K7$hks(N8$dvfoSoZFiro3$`K+bn(KVSqD=&k(#F zw)1I%NO&hpnnoTh&i&}7HqIAqMLGF=_I;tL@+2&dj3v{MYfka3@IofW*jhd)ARO$( zhb2JXz7{e(aLCtr$Q{Pfe*`zVa&eZ11x&5A3@WQ73l51xXjOt3NGa(OJ*za%55rjo z14NXjvnnUKhR=0bA-Sp_=_Ml(lu=7!d}ca*@jh0?qrG;Q*o=bwIN@0H&v+y$o?9dV zNE}|^QM%+H?s>+Ebgqx=#g{!i@cRzq#NSf1k@&qAVkD0*r|` z7;WHtf;eYH!9=Yw1dV=0f9Fu1T2R~}oa=M)07BIf96htX#DhS(JvMhgZ3pch{eRr< zwI}Vj$V{0Fr3!g4G!{FQvX8eLexEm*n~auLi(|G(LR6)mcuw%GhHcLkjdFG-mb;VP zYFsKe)WI8>5cS&mf9#mwbuB^eujA;K+Je;U+F=(BaK-9UrBGaD;ZOo}`pczX__)C( zTDhchJuN6HHTtGN1Q$#bCr(=v>OgeCVr$jf2?bVn`<>HSN4$T7M!HmVSSsqKxxa57 zYcOP}2%sDjhqXw|-jYkCiOBM5Ea``ti&H9T+7kpUH37Auf5D7<>=8$WQtbYo+50ZJ zWOdgwKi;-Tgx6j*kWng~ozh;DPd#apg5 z8)&+u`6z-Kf0pg9(6URPvp*`d9nMfu;bKj*G?IS#Yo8qhwOiz(u=_S(jfd)XR#7fy z&fFMVOhIY5*5$fBS_<3=*dJJD2qVEE329Dlpco z!#p_z1B{qV(1@~sk%w-8P^KA4;>3Pv1x%#+wGG13i*DVY6M9BE7{*u7Yhw>QhNy>o zuh+!*zFK5qTqu^UYo;5lUPIun3vvltfST7Kf&r?q#iL*oV&FS^t{tJi>Bn{#S zfACNTR`g>~xS6}LfOxOeDcV66dE_1#ZI1D~uh$ax1f3y9C(+ zbmHjtZ35-1AKpBB@$5gIZdIYZdG+KKdkb(ZY7UD54H~gr>r`%7?X8QLku#XwP+Q1f z4trt1j?*Bgh6#~vo{^zwf1DCIiFJ#ke)f$A+|GPcOJxi>r~aOr1YwVYtZr zj8&raWQ>rWlY)B@UjV)sWPuI>4l#!`;)hRw9c=E)7MFeD+*BR@nUQ=2nZ?#pK0t|F zyk^l-nikcI7uMot5qtJwz#GQ0f1Wng+dL6t8mj`45=9OLeQ%3vEJXe08jfO^<6HH< zzJAQWlKe=r2=I~6uj0~F>OW`rBr$@#YI0w(=*>}|=yCtQ9%@{*zN zn=fv=^@!MouMSM9_PA8-&h^!{wOv(BUq9{}v;=2ZI;j6uGjC{pa$V~;2T%jDsRmQ; zDEXlB5KV63OZ&XuIwq|1aa&r~KyY`0w1c+t3n zsx<0tYwGf%vc&w|x-qM|3cX3J4oF@PEo)UH0Ka93HyE9_l6AAs%9yP7SWi9|DGm2Ojno}xUR|7@ zQkO51^vvU-6nr8iTdczA$*bqjo?NF0LYd)D9Z}97Ch(lZf59$O|B1sTAlQ(*7IeOK zFtz6_Bi4VR?L_YZFknGJa zpgm2`BAZMu$!a^T*Ya{RYOY3Sj$cG@0XZZJ-3aDuaj}@?|DM=r(WMCUpwV9MnF5@h zErWL+#2#LI;-6JulvYO6_AUfWruTIsqM4^$g*^trPjG4=&&? z2j3-$e~D8QLSK-ANw*Mni6qC3+Mri_~Au7@Tj%Q_FAz848ua#kYc~9 zYLhng!T(qXlk;1K|FnN&cgg&wy*&!nw+#NbqpJ!f!DRjwb8ut*Jwx>#R6)jKHYhyRa9-l>vDJ}4>5lm2~l9fZsR|g?(e{3hiU(6oY-77$g2f63xd;2U5 zovnuD#aY=^mS#4ETX1&9QDRkTDaGt+sKTQVgNtRV-!ZOiU^#AxH+#7?_0bV0WR)N| zRVmAJLYJ9RI-Ay!>JB+NzEwbuy1)WDe;|`|8n{4Af-m+uL9nc^2g*Fb%@wZg5!$H* zTMNCl7>T-dWIA&0YqB50iGXN4qEYjGAs~7gwo`kBf&m-=qkY z3~IuIu+7ZTl2`R+w)p%&dW)gtJ}Y6mR0%f2Hu7Fw1=aSAE3X_ur&9tT|upR3hG_y@z1Dtd7BE zJiiEIzR|O)Iaiu>`~{SkxZAFST8j%n?%ovzn;lXiZC1v*WwaGDWSKO)`)q3KFrVD4 zjU5QJzfZiYHRc6=oe*&(&d{^)6)A-Y2{JFaNCIm67DR9uar$R!dSdP2f5jf(yLnH; zLzGuOxnnaK@=bB!zfDb`BNpCp4{AMaq4~j;(mJJO3ukK%DxuB!T{W2)d--9wanpX4 ztTe$?8fFtZ^y#2uzFSN2#5g=4UiLLotGRX0uiAKh#PP}C_V60%Z8XL6$e)G=n}S`4 zv$yvA5g5(`f$Z)uT9}F7e@oA_b)Y2|e?*Mrb%jLYm&c8t9yUdTMHAlCl=l`5Ty1d$ z9mN0X>k?^S@gFM^ufxr5$!wI+JTi!sbxU=F=+k#p2q(t{ipizY!-cxk6SXNahM zaWmE>DU-B%IF4NHvtX*(SGfVBhIKGL3TLxW(gyf8J(_l&8p)ys8(<(h1@2mdW^-Yt z#O@}~Ck?WOj$i^5f9{Kpxee}(^;871;2N<5B=W!|@!_7-mE%ASWNsESVaI^?4J)=K z3E_Wx3^ub)jTLSqQ zE~EKbK6I#}2gtKmPrdVFU#44r?V#G+INa6$RXsE%n23@gpJqe55*9N!u;_2?skv$M zZadVh9r_JwHi4uZltB+&8-=OhT#TJ#kS4*m=HIq$cTd~4ZQHiHr)}HDv@vblwrzXb z_Rie<-;LPVf7pnd->T}#U!BZzPM)m#P-WNI{(i(vo*}br36~WjmgZlew*B%O33C;h z2Ge}ZXv($9;JMI~uXk93Vm%RVS5ZlaV}t=U0Q}eGm2QdfCgAEpO{AB@!89L%z@{`Y zB-t&x&?CJw)TYo<)B{YM0k2+8Nt~vYC~EcfP_3oke$AWsD9Wf-VLQJ*7`RI{z5x_`sbmf)yq4Xa7yn(7i zW}U3`fAG`Ee(#1dE!+CSi)Ep+3h#%f2^60lKA+L`Iv=dKgJ8{O9$u&u1cN#0n;}%9+4BH|>|EQXt;f36eZTO2jTRI-a*99kU_F7wNelUJs-PA%T?$>SWr>0-Mgatgd zsks__(2;Dj*{doi)eWig?ndp-50*7&dKcQv;@_i;th zqc?b24(a>Y>E0;uHK9l*eyggE%bV(ye}UFJB%}vg!H3rkIL3 zApTi1^hK!7%5O&tXj=BeT8#Dh;x%c7j;JSqfpra_X$LpyKkKcbyU}ShU;upI6i=bm zR3F?hLAb`z=y;K-(CnX9z-qAgf467c4~zF&sfI?>tiq>u%}&!tXu$@a{p>9hY?M`u zmeTvdqTBucb}?|m4_#F0ZT^k7)$Oxb(cS6%K7F}9L4T@$WJ+6QF^AAX4JRq9tRHh( zytAAZNKZt=N_?7Ib&{U}LER>6!eZsG7g0Zp@bIu4u5Ka2N#u~( zkHRLt3=1*kJ7p6j7!ZvU*&+voA;B@9VkbaHa~in64EgfV3@*XT%j3y?w~rp;vF4>S zSA~y*rgv&>zag=eYJ#u0veJuSEKpTt+^v2Pz8&SFK?0Lg&Dp1Hm*mwHo!*jdJ8XG~ z5PX?%?=mQP!ufHRuYr>vf2PTxXsm-n6YM5|)8@exaoMBy$SYbOkLlN)Dh82wjIF2C zK^e%)%|U02V*{P9ngt2NzthJoUN%%ikL|sq>r?I<-LCS8`PjP7xMDdd!+T+P6+S_X zV_Lv-)1Nm#C;6o{He*ocptWcknGa~k0vI@y+=ne?$mw5he6coQe^|O>>9Wd<$$?=G z(T6{BAvmY05(gor@xONeq1}Ad>E)$K^Vl%iky)S#MNG-c)_y-$x|qL|gY04knyKPN z&$jRf5u~qeRZhM$ql=AQyyb(d$3;-rTgVH&i@08V#j~$3aF|Grx-gGT0EmmBa)h)V z!e$P`v>pxj@kmSuf4OWUkbUN=2aWM?;fmi97Q$`FkAR5q9)08enEKd7(EqsI=^h*t ztGNF$>W8M<#`PjBo!l0GnSdD7A9<%GbFty!8r$dPHbGz_h6iV`9BlJB+AddW>9BLb(L%Nyv0Bo=-3}nusyfwl$er`NE>ptwG${Ce~p9DoSBFahXG0S#5?JN zN~$wBij@smgXZ%uq~D3!g<}B(c2@+;qHB=%vLoqzhc87;??eVn<*)~KDKTB_*?lA*oll5)~VudRk<~<2-mBVPDoFr zSNmu|mqOZtq#Z&#Xu3Z3KH4=*C7rEp>b%UAr#5B^8IZheL!lpf2Q5(1WpYe<976iT z4XNI@w$sSd4OFTX(s!#hLEZ~@z&`|Sz1f*8K4FjtkiNAI?Q?0cV343z!dH(_MRrd1 z!YJ}1f2>|)xP+4h5j1}+uWY}QsS`-BcVG-V8#9oX@tyMUCk7HiS}RZ6NME7+#zzHh zr<06$rIzl5_9Q+iF%5CnllJV=mhBuXt!}qK}b~+ zqlw$5+0>h9XmbC`nVHM%U3YJsZKCe=6lQ;pRw*(|sL=V)+QNhk-gzd=Dyu#CvlXWN@CQ1n& zf1)g=WrNM-LMg{I#MB=kd=Jq+XC7$YtM}rc4w;pAT6rK51V3b6=r2gp&TPip30V< zKPZR4H;6091QeG0^``W@?}iNm3tMfJ=dPMM19Qssg|&2g!nknpJsc1v@To{(WG%G% zfNd3duPuGOXmY~_O=^Vn=}$|E>M*7WxR0!WcQ2Kn{FSqqjDAG0=IxT@dY*aXe?_nH z&yzwsmbJl5+K%aBe-qE^K{Pyju^GMXBk+6CWJ*zC(G#!3t6O5Yc(d4s z2JBbVhR@_r2epZ+hnsI2jQ-RXJPRn!<_HUhPt>@W?MmO1cTsUK*(!CKV82{_8h-DB z3e%)a_p`tkzSfuSnz+aN7PY%yd}(o1^s{HwYUGzu_Rh#@j00Q@&RT-gf6AWIdjuqO z3bkZJQK#(JSz8G}i&9CW(PH&SzuLd$K=b)Lzh`UYPcl7!?@yJRbQQFX^4ae?ZvbaN zn7>V-?8!@21r-+kYL1;*`~8+tYsecA*X;aEmk>L`$2|<)rZcsnxp( zAV5D<00mi~zZb89&=tk5!5|yWZ6hP-V;Q<);4pywf&Aa0RdS{EVyQ zMSU=o1wO+pOQy+sQelcajRX{9am=J7Eyr+4djLjc;3Dm*C{jYQiZn}upnow)*OcEg zNJ64hjXow8L^9j%j42#pRxo?=Q-5w=6V{Yw&ZCMwUhF3284^8AUZ3x5rs1)b`DH|Y z?S#LJAYNp?`R65XQuyppk*7U&V94wQ_;uhjdh%jGC94WYb64)ZlJv`2%dN6ZlV4<) zt`O7sg~N2|!@>_Jj(v$7hku-FhJ?uhbKDDBGP8^zXe$kUj7UT=lM@xo>kLlw(meB8 zhu%=hCKvi4cfXlV)CBLsYPd8>q|GT9;ny?+tdL{=K^ zw3bZw4TkIjc7!(lUVnyM9YOPkOs6~?)!t9B(d$s;XW!FLLRIG)`Fh z4QEC$ji!ta^=@LhxQ_kL+{Dw6bE;{{D>^zNqI5pkJzHol2D1t97K_hcr9zqfy;v2V zS5b-)uSGdco4)04euugaYVe{F_{NMssO#+nqo#5pMR(IoSYDujws zt#A>~GYMf<34ZOzeA_etWy@@W6fKpi_G`!MwO;vAsn|n zLDH+$fs%T`bIpwI5%MO8CaPZu3Np+iuAoz%#C%dehXHNd21J)YFsyMxcJaey-8kQ~ zIJxp-neAqUsDGjEu`C1@hf265Mx~`48bV-@=FHSkDN0!&zj8@oMh0gi@fXqwtlaoN zrJUf!8D$5rfkuF9+F>tBzcDis`6*@SA}-XZSg=1(`Craf`hH7-)&~#&xvit|Kn(x* zLe&o0(TlMA9O(WEE<5dR;g1sH=WQ(htvxFW#EDU*RDX*!=GJZ4Fu_SVs&a3xlgey_ z-K05p>F<8Plfdc~jdoi!rSBjT6NM(a&CLoOXpJ~>Yq!oP6@8TWa=hw+b~sm6QM6JHFNxL|kdt&I9@LiBiS{qN&?Ebn%#B&S za*7_3?9Q9WT-PhPLKq@}=cn3AuV?u`&^vguEr0LJeU+{LHu~D7TjkMtER-8fDRoaf zpY_M-eFP$7O4p9|+H+k>;ztZCU$EETs5_+D(U-qR=Dg!fpy&&cB~d8+1GP)=Gz`+R z4`jsC*rT$$S9qMvl-)n z>X?z=RH^cQXYOe(z`#1c#!72sfQkFZ+Pob!yIu~qcQ|M@NX&dRYdvb!dn()_rLM(h zuT5rL>eMd_wq^x+B_B%5xL||uStOC@3V)aTlsOK*i+&k2*RrQGcF`#)>g^-8SOgMUn^b;>2NY5r!3V+Hy zoN#HF5c3{M3aNE&eDfD-n|$|@f?ZF(#ugjtR~fxp$I*N$TID{EsxS-(?((QD*Wt~@ zqVw6AN0S_GD-~^wb}x1O!qK{&E1i}=k%S$327&yoWp0I5i7(D26c}jG1g)t%4PO_7 zY*)Gc{S!2?GtuOa!~}KaS*o`!JAczk7fzWbB#H!-rf3NAsi1o{tukyEL=T zzhfgawWMAW>{ny*sIEwyyq`x)7yxz=vQo(I@^Ml>kr{a~$YQ-FOJu3O&zktxWsG~e z5N>}I145^XGT76ufiC2XK2?4ic%ppVSn1NQf#3ZweXxql$9;%iZxkkS?SIDBxA|_2=0cuo>u{D9PXKblESgtT z!%lzTB2(khg(gni_p#ep-+#ej4!X{)8tzJpDO3`XaU>dG2;-O(f&bmtUuzsqdjwgX z{qg2-(?Op`>N!1kaU6g%kxv9`o20Jy6!}em(i-Yp zy8#&TVJ%Dt*l181=|cn?f_N}D*7(N?@9%>%9RrP}R=DtqTn%^|7k`R=zuX<}waAB& z+v^d-okf+}cY8e^7Afph8>6cCYN64|=F^^^xaR(zf==MOI}|KcX>CotRl{qaR=3YY zU-Yi_*gqmzw)oa9>qGbzL?N4B_J0zpD>RUN`6>;RHg{xfmWzobo}$8IB_tTHFBRP;d@ll+VsMOk(!lG2 zJ#LL_1hFUS^3984nj~=OKg_WtK@nks_^x&S!_yj-XZIXIZ5~2=-V|vY&@jg3X`x0V z*-`p<$w&E7FtKsd-i!H7I6IZg^G;2zlB9`?!sJIrKn3e|_iCx~_(H6k9 zAC(wPyuizV*GBj(gE?QBnX2YZzw}+H{b21DmwSJ8+U)UlUUw$(MJF0Zrx--}?*1HJ zJn3Y=6Mw01hvE=TPH<&+j+Q6|i#-+o!f7>L(ct5iu-Oyo^a;@GP23wr_dtAO#W@*r|})gH3z6iZaC zO4BuxEO#=|gv0Ct%gakC=2U;GMlmK|Ekc%O8-E`zN$Y&0+v;R19ObqgFYv=545;Rs zg;L3!QS8(I1E>r)Vw9AyB(FfG>5iM4fOVXfTks5iv?w#?U{ht&IU4)l$3*z$=&Cx> zKg1x7@T)UHwKsfaxkMA4z{N#>TadIJEShCFVvP{$qL2ZAe@_h~8*xosMG8_nO6(+! ztbdmGM{O5F!?_@$Bk2X!Ya0K|64(Q5T1nKqjar#my@A(=IN|=>pnaso_`F;&WkA}x)Y<%ugtFHo+(Dg5C&24)_V8Hy=6{@& z6e2gc9b8i{+n8oyHH)s`-lDNwvaS$z)BYCl!vLR#0#9Hfjkspksp`FQ`(fnTj*LKtx06 zzK}ZMK6N^Zq{rF8YcfB})Z-fStAEdt=tdt{JpmW#{I!^5=%*F~j(}j)IS>o{0b<6x zh4E=y!C6Jd13Gw4)pCnrE8!X2;a)uqi_W=2N5p%2yfKxN0^9N-m37&x+hg+|xXUf) zJOS!-y&I3by(8sq))serA`kk<-CZ0$izZAxL%^7Sj1b8RCc^XLuV{f;qknt77HhUJ z*iPYUpk_}Vje5A1-P?*Z5=Z{GTR6$B&!fhPiBFzPgZCD#7CC|jaYo0ExC}azr>ub{ ziNF+Pq&yi^ukeC zO1%Qj#6CdnWMnz<@+{0QUTeXMoSpbA z6%rfVrsDKn%4rv=4Yfm?HBm3+g(+F5(s}0x4T!#CMxQ^eVrpP)Ch3WqF1|sC zsu9u5B5G~iv$MuDcoeHJ0T4UD9L?13**=OBasv#AohQ^{ZH;*pLi?c<2xWo95|ECS zGF`T`$lR4HKH+!QtACcjWdgYStIP*mYm5tC3^JIsGNOVqN8(t|>DHq;arKq1Tq?m{ z#3`Or{#PIAFYcfg)F!Mvs!^3-EVQ#Eo;)=!0b*lTMzSvRi2}7~9&w{7!!wJIFR7ZB zp6CXqJVbu$9{JeL>MP?`aa`_w)P|PJCiy%nIt#L`V4byf(to*bq6N8?8zon(*W~Hg zbXhT1yG=%MP<-+H-7?NH)$si@v92+g<_M9mQaIhGuLnd|Mh>P$mUOXhD+eTR86NQV=t1-hOQy);=GZy5*ZFy%9)OK877`SnU++R8T2MY7;|TJ)g=T zufl7i4j5_D^H-j|14j%k!W<$5n7U_g40ENq1jbqj_zGI#rv(aGiAq}OMG84u$zfxx z3@W@vm^$@~Mq~_?K?@dpkY==%Rf~$gigKZtZ`1-W0e_|NAi@D#DJxJ&(@?3^s{RId zMnz2q?$=F5wYfxvMwVvkHEPl^pz6sZVdh8Bv~&`aGD|><>>oRhK2JiyG=1oE^#{dE zbXMNKmzIW>lJ?h1Nk$ADB8?y|CcKCBe`8|=moqa}0yWAXQdN>@c$K}`TQm%0 z6u zV2P9T6l0S!L8Ij6MJDK)0jA_xOsR5uQ!h{en{Z65Ryp_V6cyRHEC$4`d+N@}^3b2Q zNtexMVkxINp4a(d!!{-cFBUI}j^mNVpO;Pfpns5A5=|yN#y!uNalCTQ-|2ui?1|tG za3RYRzaYe%lqf_V=}~VLA!N+UU<4qpR6?ele!XmmOJII7CzjH1kW$1~`I|%E;le+T zBaWZpei|=*Sd+X1(S+thR$S^G)lMl|4p^V^yE^WQA23gXbV_s+P%d+ej&l-p*q3Dq z!GBH7*o*AEC(CZ+J!c2HPclmx1*}RgycX(=*k{Y_)(6#!g|Fj!{Tg9-{{E9$$HoEO z_aslOz?7g?D|Snq{ZGN+MKt5I#j2_aL0pOw5_CdZxVl=$0hO%)El)~S?8TWbC7ji$ zUTy=@h>qZRiIRw0`TH1m#(UXj!bKqU`EUKs0 zZ7&vji4S)&cN|%jNlWwF*veISg@cABce#RoKJVK+#0*?<=Sl+QK8ExSys$U&%r36B z9SA5dyRgew5t^wX87}s9k7oBLmew_^(;n<g$89e4{9Z z<1T>$|3#3 zOVq^Ag-ZqzAB^WTl}eH%1#6>Yz6>EH(1U00PinpAtOGd3XO$-&N#^1(p$?YiJ$C&b#+xys~iy2st;_^fLlW9S1N{p~oT$@6i) zBCDOZY1hynj<^@$;ylCe{sY)0h(N|Z5bAG=Pcdd0gTOE+RqvUxnSUvY0SNX%7}(vj zsU>OY6cu)X0nk#wFbwY5Apd^o1#|)x(Mj{y%~MV}rb5{g4%QYEmrghvHQGI67_!f6 zEQOMO&w~ZlKYEs>U48VjJ9Zp1CA0#j`lwkWFo4NN7_7ufk4aBTO^%D1RrEgBRBG9} zN^D;TmewoCEX9JsGJi8M0RciCG(9bPP;xzvQ~p&Kp`zyI;bI<%8&;XZM!hiaJsoeM{JG>6FjaxI+ zJhb_ryOSL6@Hz~@-ZvcRDTu$Hdo~aUVB~uSbOtnD4|PP)rDhJp9k(-`&4f7#S$cnT5-_dmo;Ow=G2f89*5z}DGVGkp)ei}c6-!%R3}_y5JH z{0}j>FwyhXFmC0*j{2TmrF$MBhy33EFj+5{@~0p{IR8To`Ma2!;4jIFD%(MW{qyHhs`=>_=YIJO#?H=LDd`psA`hUcY>VM6RG}h-SoIm_Ox7H^X zZ_oDjP7aC)&X98OwUYEur0iV>52ewKnZ3{W78-waY&QgwKMatj7d%WRPCoF&Q@K@3 z`|Aluu!|ewEs2wg#QZamwJ$|Zs0zQ zsU3(<QGH(5q8v(&nATu6pCtx{t$yzwp_Fm`obLmIi;k3(WG@b%cPO?+DNc z(0|%$Ak;~WW%!F1t@6~YB*kflRXs$+I*z|C5r@jLF=V(1Z{$F({C@CWY0~-qq zA)Sb_zVc6dCsV*@eBt~&p0#||9~$O&KS_63)tmWvPSnrxbqtmC40l0Z!`QudTu{s9 z5|cVvLm<=*mYAh{<|5*C^{X=HwS?Xfm4B1Eq;ntwN_DD%J>b7PVIU&F_ID`g9!MSO z|If4&Ac8l4_=3NA{=Z{6^sjC4?5N_<^B=bd?CG~PL+*Hx$FKsA_?~sr;XtT}+S644 zTG$c+LU4|qDyP>O-1W47m^xofon7F+Q)q2$X}so;p(xVA9IY*_xqn(bLf@0_rhhxj zHa3HG1hl#VzqkwR{GYk|cgNa=|Ic{-l3%|NDXr-Kf!k(y_6~;;jG_TpA)vnd zz(RisKLA^Q2|FP1V+l70sFbS2n)&20(lImC3$lNRX0}JCZ2mFu{1F%Tft~&zJ-6yM z_CTXTemuHjVGZs95s?Ki?SJVr5r0Vbf&G8{KOHK5+DFc|N~1c(gW~n^jEz(cQ8N7N zKzW0xbOhwD{NHABD6;c^IZN% z0R&*L0mrcoK>2rZEbDhU_ifPKp@|qLfan`E#WG?}^>1iSx+gmd-3<aXgWwosV|2G#1M?%JB=kvWl`qM=>{OD%01R8`EcZ&U5*NdRwgqY^`{7LBzFkB?$!{NK5Ws2%a~4n{K)|0_7YI=u2pM}47-_>+W0=iE zn{=mi_vICT;1ryLaZCBpjE|fZj~V z{=|dsG`RMHej+L7?#ITV^$m}TtjD{s)AO6O*l4iklQ;3I%72+wx~0Jy4I*{*<;tDb zvpsPwSDvTKa%inGwX&s8kBt_%k#n4uDT9~vD|4Ea#x3R%+XW|(cdU(ZsuV`uGnYrj zsAsK;D&n6D1Y{-d@di3{+s3nL-u8{Mh6?5WU(nl+${O);f zqmF_L^ri#_<$p8hp94O;J*C%_kZYB99Y#1S(kS`H-i!O%w&v+ zoA+EdBRUvDAa0;|g2U<%b0forkv!vO zVnse=rbP_N3cxsFfuaKk&=1o!;y>@{Mt!UoyWE-yht|yro$tDWgM&qW`bj{_LFF7r z7x0mB=zqA^xA@vw}!kf`&49q9pJ0 zI(6}-rty0!C7(-2x2dFEOv8V=AULNGO&ZL*XswMRZqQROTHMv38Kl+T_LA$;*JVHm zx*^V`h3v}a#X?#Sc1UWh_xvCX&Oik@ltlXHn>&4BC+zHfm3o|{OC33V+x zyAkk3dScaxcpX%W5lr(wzRd{ypFz74`fZ|DV%aPo{C-YL{eNaaR4&llXB0mz4wrYQtDwe(X~kK zHn7!a=C}j*xL2|b@gN}6*azL=@-J6UFDfje+N$}#WE%2hAUxymxgY;T-24_t2!*TL znec6Ft-%p5tkqpOY74OIP-8ovpJ zs`nUa4i#~h-cS)Xs&8v+Y9p#Ev^`lw^-(2i%FUoC=L{Pa7j1}8Oj1UV?@vVYF5 z%RK>K+Otyh<4fL?=BzFfUpjaLH%=Aa^}*r_R%179o2;AJqS85u|}VKgnxgFN126Wyusi0lS-D+!~np^iAmT9cpen0i||ZL zg8QjK65tK47SEN(0U``7OeTyk?5X78*k>jfH1sDs8rGnnpwgg0 zDF8YN>&{S%Oq!w-X%KO%Q@%A8D(Xh+h=mLVcCUW7pfmfU^JloT|FjfHC=OQ1CvP4G#z(_+2x)dO;N1g z2Keop`S} z--AH8KnbK#sKn>8NJU+123N|2LldS=4WFF`8;v>ePXDxm|F|5vj(-JKEu%w?rP`RR zJ{7T_t&H|b7^TOKQ6|KIMju!w^N2<3|1CWC2EdR2cU=7K?E4?1`oWQ^DqOW!{Y*6( zAZD`r>|H+uhGlc~0J)?vd4GjVhoTWm9SNq=s7=4RrO4Uwl#g-l zC!(&+x|P*wYn8Jx3jQLNQT}g)9;z?b(@*$QMvy;rQ!irU$ElZd|38G;CFZDE#1hK? z{VuldZvkf=6@(9BfEAhZkz+&_KSPUecm2g!2MJCp*5$kQ^M83qzm_eR;d)S=tWnaY z^2T5B4u8;G!h#xRd(efvJj;;**;B26H-^V?R1OyxCzoJZx4|Nf^Y$%CaRQ+knB22f z`M+Mg*>5nwc3r4Dj2cSTt)IGT$-}G{P|pGjv*(5NVwyzMHH{_~#G#$l5Rh_9GTVT- zGD;+P{~*YHtbbAm(wd1m4X6Zai$J7pgrv+1Hb?W)&_)1x#<9$W6My&UnBlLzc?Wkq+C#(43^kc;S3^QOpbaj&_=uVfT>`wV56iBNHl_; z>WrHUnP`WnNF1T^{8A2a&ZoNQXjfLeZ{ER=9?!75?SB!487|qdbb_~zgPh(Vp!nfH zY{_CC6uEqD;VFy)CvoU-(umAMWo?m+MH9y-5v_C9yNdN<(;Idk_{aQ$JwW0|33TqF z{}R=YkbX9tUXZ0~kNIGPMDguAf3Pp47Rgtq$MCFmJOBqk_`e+$f{N!bD<(+Ck1I!B zDKzL_-G5_U>)~ooRGh3IkF|=s7C8!R6~n=fz^11b6i?ikDktP9g}mS@krVdw(cy;=F}Zcx?J1FURPw0}#%NUG zD^jCmosyt|CG#kR^GGy5)imSUc1k-p_X)^=B&AU`rKcoK{Ghc$B+}T6p90LaU_Vn< zJ6(IP-|d+`zx&&Qd0qa{LmCe|#_pwy3d>BeB}CD5+LP#LEs`2Lfag9{(8PN0O?$r2 z6MyppMQ3eZfT`aWPTr4E( z_kZ@SG=aNkN6AgmQj|q6%^vPYs!hv8EPnxz37h%S3lWaM+PK>8>2{H)%uJI{>$C#i74+1DyL01q}|!{ z^lG0@g4c-Y)J~MPGEN>H(qruf-+x^*f9NR3)sR7L&*{M$0B`J?q8>gi}aTP4DhhHypwxYtUD`Ruw56+B?bOLi6G$=}mU2bPYB_<AInu2HdP!iM98eWF0`Q)?~6 zE;9OaQSA7<`!NYnJs30u*)WcL`og4VPvAg>rNSwamw;-;7J4^2iJa%?NIwX!2>QwU~>Lvr-kt2ih#c+SAVYOyP>OlW`+FG z{*~T7XFC{FJC}21w@%rrvivxisf(k}HgjGnV)QrWFAN@@XqpYOd^E~JEYtjayu_eh^rK%*M&#Q3R|uYeN^ z0o)sJd^RrL-1F(~#O{=v+6t2{E~a?+B4?o)oF@iTaeWSW+kad!0(mI+-OXV;#Y5x{ z1~ld5QX8aqmg-`#y6$|?N_n;z zNbk9hBI>ci!a+=oaBK}>|Jf}D8wF)Pj7$TXwE_$MKIjBi6JeeWk!{P zJfz+nv&Mw#TYS9@m#>?Q-f5vfJ)Z;D2WNp{Mc#joj1D}|T3`0kx26^Bd)m8Gvju34 z3~;p8WqBVa+5_U1SLqHjCfi)sQ99-s8c@p(Lc_+OaDV4&{y=@~Ziv33a@rTf!yI%7lu`^BZtMt`gjKOcw`0Dj_Q-54*nz$(paFi(RFbkgU zxU%E58-Er{7t!mb#JFbfhPEZ%K)##(9XN8C)jCzGf4xmQI5n%Mtw&;qjhLKW$#4or zJr~FvUAViO?mKH%QXlOWH6YPQXL0Qh)mPHd+p-zfvWXEe8cwLE($@#o6|#zet*sQL z5zP4r1BxRkZo9tL0Yq{_zt7vLO=KR`H`MYV27g>I>BR*Hh-7S~ z!+);|@A*I52{igE(%lGp-&@$j$>lMAU7GSZQVEK&9}3@og-huwkpw59O=%g52zUUALNZ;^2!0Mj()S;~6Y^Qkfg#i8HaatI7L0DY3DgTC)aB)a23=F&FIAr(Q|E?))b+1<-Dw zG~b>ee=gs7=>qUl*7$OD^LygA-+6I-8&4L(hu^!<(;)0m%avScy^vejavBrUkUM@k zX)ux$$y}GExD&%U#d#r^NcnbOx_@?4xJ+}yZolc86-I4Lae4o$)qC#ni4yFir}|pS z>56yBUwM$Kz9qXyXmN8tt<^C0p*<6?x+XOET(}I()rv?HV`tr-`tIEK>u$;IaW6esxNk>4J<81?25Yb!em-`D@_H7N(40Pg z*0)?_j7uyGhsJuX7MU;9e3Wb1a5-=CYP645h&!KY*qwYG5InViiGQ0-m2aM&?aT2{ zPyiq4pI>Hv))0^OKC>lN;_CM`5bc?T!VdwW?eI1Ma=l<3Rpjncm7r;Y9B|S9t?Nn* zeNhxtoTy3-v7~g=s>0h@)5%?-G9A0at7$TG0x)z$)QB0d{LQK*vfDsJ$|qejR!83# zaz@G(0Ja?Q*!3toE`OZ{OZ>Hu(lFwCeHx|t3m|K>OGW=K({g!f#ZNm0!C zfq+5>P(y5+mpS4s2WVZi5p@5ISH`T$N(gwzKwfNqB-aS=OWL20m9KQClSPUpxXQL}MDR95E)XLy>tG zpuEJZXT%uT@tpU8=jDK%YZUDl^775hvyummG+{!V_J8S&mRL=P96Lk_FSiJk@}#XX ze2^gKp=y|`bceaP3JDh&-c;6O+>Rmwx`-O%4g|VYGwWqElrk-(NGpfKE5}jE#j;;~ zc4XZh3v*E!F$+-g0H{;J_$?ljHw-um!K5t0T^b2idN{O__-rd{!Q`U3sM_eL6aqEK zhC!avVSiv4Fuj&yWFM5v-8rIQILl6D76a6nDb9$a=-CCUk8DWPE1}+=XqeprCPTUO z1zMgHErgqi-RiS)UB*xT)ShOTw@9Yv)R@3JHynb&)L#@OqAJ5kAV2U_V-aJ1;9nBG z31ICyn$o-9OIcX>UX`!xCe4G(Ft?>PwNF5zp6d|tw2&UjiuptpBk?l)T zV1HZi9j052x-IruR%S4qL5RW`7ZqpI*VY)Pa(2;zLDa^zhE~;*5s`QY_0fDxaG=U! z4)t4wS4QvJ}I?q6&~r)FQ3q` z=g-FD&_(k@#?4zy3p;CEy3Si5&L;e+I-0c{5uhXH%}=-<(LjaVizN(x!sqHEWHEa? zGPqQ*e!BZW`0+1Ke=Z*!9^M{P+*D^KFTVGLR$Z7;dvpC_+t=-16@1U+LpV$@hJQY} zeNWmOyPj$Au^6yv*N) zBX-?y;c%p-8pxl+LJ;_MMC0vYrBV}XUHSmuVtG;P!&e=5g^fT^O$TYYuXeOh?dHsN z&&OguL!c+*&D@Hw?z^0(oAHgHzJKWqpB>sC<*UM8OnxczxV{#EEbHc)Dt1G{&aBK) zSt+|*gEP3cpWK<4nzh3(wW?HnoE;xBkV>`DXVz8bd(gRGT%R7G{78nwclj*? zx)d-T3;s(8X#o-pjFeNr*5MeueTu=pFKQ^d`zHdDlDaIbiEwT`(|m;uO@Hfxl#~!Z z@L8+(W=q${LBWQkmTbW_bPe;&6W{C;F5v+TLp(Ey(Mrg)o%|z{?vk7k1~jkgWOOUS zHidOdW~iNGrtM8?r)D}54mQu$k2QP*Gx(;0CTRpuqb6EF$JnCgRX1XeI4m}Y#bn5N z!|~;oRoG*3)8H}kB+WEGmVc()Rb4WWdv;8+uKWfAYUuSnb?hmf0QTDO6;;4so)DTo zTJjcQGRL&!K5GQ?#+jDv`!%;EA2x+!TY2XFoA=r^Gj`0b5D_yZEbuUfDG(EI5}^*1 z^8oOywJn2lwpMWqQ;GYJ)YWleGmT!BqcLFvOK51AvXBI+JXgaU$bT)oS7Hi$LW|}> zM3L>H2WT^7j>Yg@=M3>5U&t^?^}V|0WqyZT&gCc(sLrXjNiQ?^XP1YLzP{XEw^m*1 zC(dxnzRK(2uuX=VK-ou2bxO*X#AULwylS>Vbn#IVHzjy0j@~pgKelL+Fq$ zF^+#z-qQ==WFbr3-hb)&zEOdRrVWW{2iJ{^x?Bp%#4>U|`l2Y!R4trm+b+geY0>xu zbA9$0W*6DHPcwv-`dV7Vxllazlwq@){Cj_|%zn z*P~roOmTDAkWE9OXtJ6fuKQLY)37N~_x*T1oz13qS1+~aMufG8*4Xq|a4BS|(>2s? zZO?wl;4L7A9e;jbY+hAk?H}hC*H5$44mh5SnpxqnvKh@q%!?qalA46LL!N5)g)2cW zJhG7EDJOd^wkz%}zn=JgtQxZ`>dndAk zvi&rw?D!u$@E|(2!vT1ll~!$2+c*&Z9Q}%&;Y-pvI}qAy>zVeB0td`-Etdc@H=Pd1 zwi4@NOMgO=3AtZ?E6ItSkkaWFf>*13d3N_%?d|Qs8MOt|z{OPRidrrO&{WnI&bYm# zDNvCEYJgc9%PQa&bS(@>LCAH%4P>&aoX*VKF*^(`Wvy4%xGc=4^X&(vWnO14CV!KhKG90yU5sAUHVO774FGL^jVS5$ zPFTvRfM4VN_$@iEwPwP?jbS?Jp;vNaTy*2xzmlGiWL&39xJ9hcMAAEm-^YjQ@coFK zp?~#`_+30YB%gc|dM!VQ592pv?9ypf_tEWZLe>zs0j_o4#0RL9GD}Bf%pQ4=zRu~p z^zbb?;hJSu>SfX!7P4YP40;&gA}62VD5uK0T&kscC`aUGe2F<1)~aMUEM-Q^LK;iX zudlD*xRio{*VtAV8Z;dt*8ifj*x@pnoC$X?py>CtcJfTO###dvD+K@(KBR%k5d{E8K)AoRxz__f1GoL!12HQF06IX$zqc+1 z1lJJ-04hM$zqbV#1YRx$02e^$zqeyX1k4cy02e^$zqc<~1Udt^Zd(KlD+K@+KZ451#Uj*ZU(>yL5h0E zh;)lRG8inbzS)0yZbh^nd%<&ZLYygalO7Kl>Hs+8RC0UQZI15NMnQsIa^ly2Lh<*7 z>@2k_xLzl_(zPUaAxH$IQYm=B$>eTj;|-IMxoO}(huS1RVusL19!n}oZnKroQRYgH z?Z`iQi5)rsE1REL)o!h$)1mOh_AQ%ejm%n-ytCjBnpPvsZJgw`OMJ(;Cd78ybm4)t z(ojV05|*|&a1Y)aPz)Y=cg;6{Yupk(N1n(?lZkwo{Iy3DP4YVeG7fnDA;AOIvDVh; z80Ec`$Wv$t#O)vecJ@61;~3yp=Cw0NWejZ;f-*s3A0kCP2h4Ce9XKPWrDfad3FkG3 z+dPP-$b3V+Sy$WwSCz(aPX{36n(StM3nN+8q5ECPYI0Gw${q=b2`iqPHM^@GMGyGB(GKhqpx?2ORrjpuW*>ZuI><65q*a6{O!BC(UigKRb0e z=ms;$|v#TrWK2<2W21K^Q5Ji`MdK%Ooqx5ad&YQy4hQY5xEHGZU`!%<%t6%V}aDY8K zV~pdrGg_==Yj)Fau7qhEVnvQJALrb8z-^W&f~ZedB!o4b&?}{1U4gNMop2ttT4NPI0cxD1IayxeZZ=Eo`^KR z5KpIC+`RBc&OEprFm_-`+qy-q9 zk%99cq073LS=6pmP`$nvmmru2n!z^U5*VBUPSqcXUUS7uYjvvW(4A*YNX>_s(BWnt zayU{zaCzsPA5nozac=|{BKfrBo=-h)*iA9rFdKI!)y~|zZU>K)^PVXP&21x8iqc#5 zjNHh79H^DSU>K`61ZVh(VbAHB-0=P4>6*NnrMQAa8s%bW_k|#_AOcG5+v)w%bJsR> zyBIn9TOG26E67;RN!)-iIXEL7c>_JGHe~x1=aC7L1j$(?bs_Lc#zKMuoc?&oIL$CS zO?OaF6x&+CCywS4S+manN?NE0X@Og%^16^b}r)mkFV zH!uL4gS(JH2RZ#}IR*WVgwUaq=F(7iMH3yWtfU0(k|-LS;bo1CxQK>3$Ztf3a(;Tlu>?!wwJ=$zBrsAvfe+aW>7>@#>c|$t*Vz;GZEQIM`U6h6|EHf>dOj#1?l8 zE8l66!z7wvf+n}OXxr@!JlKqcki2YB{6vO491&dpoqH9svuSo$zIof2 z7JLkopH#c}Y2ZyW$=P)SL#Du%r$@7dP5r1%vtgb|aW?45+;F=aa_!o85%RvZ;s#qA zeM%VZggvdwELW?P7$`$NX-3WjBWECQo; zcVT`|tOzHn6zF5g(IUoWX zBg^072Cw1$8%@`A+m#Tj81AiOE*oP5heZQ+2&=JZbR~{@cOd6EyD)OL`+` z`*xWx*fj+#>RMNVe=uX2n}*#U!-V;WCvA1(;irPEuW!6aJ}2GJsoeR$Z@PF@mEsI= zra(yxg$1xl)!nRS(WeZraSUQt^J8)q$=uuUz;JhPxNtyWe@d+@+d`v1Gr{yf2x(T@ z-l4Bq>)N)rJ)~Y__PUO!S~+8Q$}$UVkt&~%{P{vvK_}*F`n%z6$HOfPPtoKa6Y!>s zf2i9Fh{cVBBH@`7XDpGZ1q(1AF~I4;#dwE=AH*=(-PzCKhtzdbZe(p|>{gLSb2AV^ zY>#Y`I6^a&e^}I@1i9q(S5}c|c9D37$4tM}ukBsDhVW?t3Fb0>oIYipb9*-g}vU0h7lG;Hk0GET+c8=Qp<04zXN9M>h{=Jg^~b0l7m)TRfbTS~>eWvTWsKe?jBlhW3|jed3P`!{MzvT7oe=lctpv z_YQDZd#O+vwu}NNDM*{%lA3@Z-OCN;19~F>)>zXy4_3hkuO(`!ULzywo!1&2g^K4O^ zuK?o%(EL$<;mtclhC2%gV7rw={XDW+_8kBhmDj zG;Iz7G;KD|Nm2{V9j%JT46*<|e_~JuI2>e-eR!%vUC?#NS5`N6trRmAhU(pxLy~@N zt`6iJ0B}J)IRsbDnr^GFO!pT$t7}?4nxSYVk|QjIfdd;*;AC<=alky+UGVEt(IeR| znr?L)V$AIkQEwT*`JGVw&H)RZppl&PGs^l_pInuepOr&2GTmOBd1(emByK- z-r0>p&grb8w?+*cG>T((U8Trn1%S(8jz>e&ILh#!h_wr=NVNSi67J#{pUya07G+i{ zSxNibvF-Pc2*quuJ=s5If5`8h{{XU7EwpQFBvr&9DlBQ7C_#(@K*l)!RYvyfLHj{B zR$$0L$P<7E7{?3in!}d%?XQ;7D5Zi_P-RpIv64?5j1o!D9F7fEz0^J+!+SlahbQoy zcDBqSWxAE5m`Su253#VPk@B1$&b862k*Z}${{Ul8hLhW^sqrU^e-X)|B z#E`ovI~qw+J=i=F1ClewK(7|i^*@O|1o02Uty@y@{r0zMl%N;P2#|j5LH(0+zvVdJdQ$t#`MfB9Jpq#&GN5Z;E5Yd+y* zS+)Il3YcZ=1m zmD~-4n$QCwfj}Vi011RpOegdfAoL!334~%oumAUEw#?p1r#+Aklz;pI`))Maoq2EO zmG8WHZ?$dCRDL=8(aVpUIQ6_cC#-h;{M3)8ta{~Hf5pFcxi_cJI&!0Z?)l?$3(mUZ z{l6dkr)zJxdaqMnDgNb0pMLSLkGH(MaO+#w|Esd;S?esgZenK5t)Dx1^|jA<|6uRI z{XbfJlQ-ut`q_!wTygS!C+#=0GFU0wh9rAyr395npl%H4l@{rc-4ZOtQF{chF(WK0Eq@xeG7**}vu= zG-7tX_ofH+G5Kqs+;yL;=id3^qno|(@F!;-fAHwTuTQ?_(D!?nytQ$Uef+L_x9_pu z-{i#;=dUsR(CfcE=BFF}^Wq7eK;pk#_=BylU$(|0H@_nublz*f>4x>V-VM{n9{kd# z-ntjBz3t+;C+_5}zTHm$dHni!H-2l`xqBVa-g)7nLjL%1(@wc3duDN|FuU`Z54A_+ zfBk;4$qy$Z~NH4v*p=8 z8TF}k=BHOr+QCR|e9D76&He3{E6@IifBD3fU(S4M^3A__^UZtKTe8ozJ8#+Vniqrz z(+lqT{DKo)SX~R(KkM@DTmMylL%!mroj3aPjn`(5?f7E2n_hkR$2)Fz{5cmM_L269 z)4RrgZ~b%Rq3K(0_~5!_{~7bc8RoH@Z0>z{>4UpJp^V&d?8>(5BW6o+PZv4Ks?nalcw$r{#hn@TMlxz0eV$9sn zS6{+icj6xEjW0dE^p;<4H1D>pdp28R$^1*!`h1-w+s|Kb=U*(@_u)%-x%RrN?md(r z_JikF9CpV4&Hrqlt3OFE-S2}df4;nS&3`<&-YKgrxL4wa9ciri=&_&AoP5kh-Lq56 zURrVPsM~&ccgHzB+pMxe;rd7Z(Eay^-rnZf_7jw4UmpL{pFEQjJ~`oz!_S_%(mxM) zZ^kg;^W%T|#=rix_W>Vm^U0q1wQhauXCr>59=_4cS4ZzO_f&D`ogX>+e}g?%UB2df zPapW3Gr9fVdB;v?o!?Jgf6ZM-XNJ9U(F;HNV*Z^+{^9AXuX<_maX)!t=1F&s+IQR` zcb#?Wi;G@3?6I4Njriy!;O?nA|GWFW8(&-R=3gwCv9GAE{McVs>fD$cbN6uL-Fsg9 z?OtCVd(g&jmd^Wli>;RLe|wTLW0z&~KAAD|?$H!Q&m9H*T z-~Hz?=4ONEU`ows;}jjq1USZTwZuX(fNJ@YhJU~BIDf8H5K?X!HZ% z;~D=v;O7tQu$pn#UT1u$ypjIT&u)D9(l4ee*PiqFjxVl!-RqgvfA-W}n;tdQJ8b_& zM|^eBC2#&@*Ha1yeKcxu?{CG48+s@GY055ZzxSTl_0WSUSW@nnzx-;0Pwx}we6{^9 z*9=>F!BbyNy?)*ofB#tT*cCoqd-t)Qulq&&y}w?*i;VwU4~I&h7Szsjr8f_VfFMf4zV5Jr5rE=L_a6I(or`y&n6?tFK&h zz)636V1D+wzsx=Jwz*vS+%sq2VK3~s>)*rQTlCITe@=b)fBc5`ueZvkKYQoN7f$~D zZ69y8*~@3;OVXKz>0b{!wD+I~yRO^smZcXjxO0!i!^ZK-m)GwqPTX-*@6rETI&6b? z)ko%ZO?vGZPrah!*+rMnUcTGJ)i%Ct#jU>{wxlH7{lTR#o&WhW$GMj;fBNfTw~V?u zv%rz|d-JEte^u>gZ#?#*1ODhdcfGr1&nrEvo;7A>;ouie-SUd7zFhujUb$@3O^;Q# z{^}oBKfC!a+CNo4&FEjXA3u56{Y&SMPtDljidUxm7#=75bl8I5zIOBSosWNb+S&W> zTlmu{r*Cr0@PD7zbJpZD$`|%N|K6S_9J2p<=Y4kYe=lz+E)!lk|F+prp0@s(=WY4d zxz8MbM)|T!d(XM&=-2Oh`t>i?ef^_F%h&q+!2M77<&x5Nzg}VFk?-HR&B-fF7;*K_ z_t|004x{hc?(ws>`(oX9Kbot)cU$g~PyYGTi*L2>f67g-b1ScO}b<_fAvlvSY^ptr=0wD*T=`ccAT}&j+2&VFWLU*hivLxv*Y4*o-J=ZLp=Bv{h*ch zShVcVmo0c?#HGL7=$W^#-u}8%%oDD7<@K%KpZp`?kYSfy+s@W%M{hj))8gXSe|PNp!=3LQv2%LoRX!13|LXQnE_wOG zqhH$X)st4A{Hv|EIoH^8tH0d(!XXRpSh~^p4JST3zvqZIz)I;p>dI#>x~#JH(a-<* z!;3CxzvZem?pt@A(~jBj2cyQV^y)9~Tk*_KH#q3QkM@7>ptDwA_Q)6R%cEv=-2CI` ze@7oS-~0KG)*kcdyvv+(E>f@B@by#Qc>aoy55IEG9+!-JymHAE-u9FK`rM{H3$~tm zQ19~9rcN%Eestgm&#!XjwJ-lpTb{Z3^B2!J;jc?hxPESBi(OaP?!95dhFv!LoRc2; zziaQj<-OBy74Fz1_^s?yq<6f3d}R$6bH$o_pUo;l(G8ylT|Q_MffJO+0be zw^!Krsb8pKPW!e|cd0 z*QTv@?bh?1U!QT}hQFCUA+?sb`7Wh=)I05xr;ohrhntA&4Zq=q zPo8^dtJ#Mg@bAZeb;RQn@71R*Tef=TulpYUy9bv{yLR=1_BmL1b^IlsV*F{@I_J*1 z{jpbGF1>&KoTsjN3n7u)f*QUDsapGvlaJesl7; z{IO!`o||60W6te=e_M9vDUYpk!IL|6&wFvhz5aUKDl?w>^0@6w2V}RLddAGFR$SXW z?x$Cb_~iCiZu;?!w~qexUH@6W*!|aI)3|Ke7s1y^v2bdhb|hIyZoizIonV9 z=RFJ0{o^11_Q%amdnR?uN>xAMTn_788oY08fGk9u~MhcC6h{!3feq^S|z{ii(i;_kQK_t?`19R1o=`|R@i?T0^n$XlOJf4SwkV>Y+;%Fn#UVrcWa)a|P-Db0MZd!NcXSX@)pLee|^`Y+h z&*xrmf4gJJw%c!c{0`GT`n~!1^f%s@w7GY@G;NdnRyyQI+nlq$^Y#VnY<$PfUtXph zxO~)l7p%ADtXNOWh4RUNgiW{NO(uukxdt zv{NP>{o7yOIOB-(@1MKU#fP4_;!4kdaQp|^f7A9gZq6Qj)tYU<{B?sUq1P=cf^kOzp&!ke?Rn3@6A7Ig^s1q zA9+#gf|LJt>}r+G*Luf&ru?tV_(ivEbMd3^j~5qw^2?>?pSjXLJ0Ee+ueTj$o%+_a zn-1Ilj!EOr$-Oc9jkPbh^Y)+bu=yDWf6nS&@7l#%AGg7_M=liTE4-=jk3!(pL^%F>pV4P{<-5H zJngK#pFjPg=RexzgJp+~`nvcWK5mzH=AAn7_h{Xq^KOzy0Rlojd!}S;hQH`#myp><=D%>dlqMfAaZWnH^7ga1Z5A zo$IdloV(!;2efs+JA1$BTW+hrf3~~&!0?W*|9Qy4BPUP&;r`Fho%{T|4_*128#mc~ z=lA}8+oi=v?7Zr(akcZ>yW85Y-cU5(xqo!m+dtV*J@efISNrW1o9uq&A)OmsJYw_R zwmD;?U#xP)0bA~L+3W`vZaZqp&!=u({NT60zkHt&yFGZ%7SEN=U1O_jf6gkdwD{%! zyXTb51CJf?So@BLo&5FIFJJK56KC|ivFt+S{lmsQX70_umbyioe$`Pkl>86Jjoj^} zA1^l^y7j%IjyeCGjI_S*F(iN`S?~#LE znKFOHb93C$2Nup9cEdW`Zuz?>9$EXn8~KHMytC4&ugV|f51zf(g?GJu%$B)Br(Hbt zd&VFv;A&0y+<$!a4K6Q=t$9=f^sO7^8zrTF-hmRcJwbE5ncvZkThzC6}(W?e>4$U|UfC>h+_2rvoZ}7954vSC zLouMC{j<0&4-;?eYMbDevu2u`;dte?_O_x~?uEu4(%4a^e~q@|StYkvb_;pM2d?lN$#=o4KcXK9}FXTD+Do%yNuBJ}`;C%u9#fjzIEHJ8&_h$w# z@Q`5eoI=sb1DS!q!6(6Z(+f#w`f{s~&$zaeFS}+on<0VZ`u{7 z+a~X#f4}DoWrmNUBIvXY#^Ntj{0-MgcX?qEn8~Eb^^gl^%i=t zIzzg7UVg3+ZIDPB-&@SSsvmI-3Wgz1jW!56qp1>`a3l(|B{6(Vwzl37kdW?4%{X4n0abzt3`k0GF$u-BgbQ11;f2k!6qh54 z1EJGd5GJ6uU^UN+)G%rz$3z`uT14KIUHI7sDdgrYP+*Zp4)#Jqo(M^JBJj2JZq>h|wH)22719gy4AHsWL3lHSSDBpwY8hzl5P1()P0aF)1Qfez5^^Gm8W5CPmQU4w zEysx6D_OVVagz!_LY4tqWcXN?=~!mjjTtu?(*kYa>s>Vaxk3&sD0FqWV%D^Pf2>>{ z_e?@wNJ)61C*+0Rn!Fg3br!e|Hv{~Ya$!EpjmWOwl~_LDXAKV+=8{BS}U~{+rYkDwPT)E?2-YyD$l>l0)ZgZl&OH zX3;6RW>PySkpvQzWD>~l*DQ5_q!=350XL{m^oiLluB386>BeKQNvkM-bZ>8+9ykmm;!L1)Nl#Bmxp7E#XSn zT63k>0TSd0VjDwsA_wM#f9K}F#z6>^IkW<&s)4wHEF=SP{C+J*_Zql?93f`GQ6jM? zoj{JGFJ%+KlXol!I1%(fyQP6xfh;AoF{RcNG&QjTnSsv03-$a1Or(B*%+G@tz#id{OVmCp zIYlR%1wH}u7#|Yk7J34M^Z=4LT1riHqH4dUZ6STg9IXi5jIidweMbX{uCy|&5=7Vn z;FBJ@aAqFttwwrae~Ofl{!$CmKjwi&=)%}S2@xQV!?9uf4+<(=;9LPWKkV>4!kwiO z?CZotPvRFSs+f%Ev|noyops2R2wkA!l&S}3K{4xEX1YLRPtgR671ulwGp6UFo0j-^ zE0IH{7|HBZzhBD{eqe`8iNJ^PWyTel$J(yJ`62});8PM%e^&-F6zH~Sq`#($$u@%D zujTkr$6r%KMix;KM9^F&AOejlcx(fGHC0Kr2mF35NAnuKni8QAd!dL&RZA=$6YV^Q z$o`P-z7j5k3fgxj8tAgAdO|knt;vR(E}Ig86cg=G^-_g^o2go*wmdoQInwbmGkw*G%nwBhP#qZae`G5_*{< z`N?n+7eeQgoecZ^T9fE8-z@?x@ahQ0AQGdLfx`T#gGPF4x|B>8`2AXr%ym4q2&9RbU&~RvjxVN05Jhm7Lk zGc$FleJz!}D+a8f;i7pGPEoM^xi!YGqQvI)dWLdQI#hm@D)+N@obUi2tl5lUuzPZ^`g`WIvA4AgN;*+YCO-UrEl1*keJ7Q<%w^+s(!84^ z(f=e!B{>9B$7%9OsMfnN8td0`q^{>Qe?{29cp4$%mBY{lX94*&G9&YabmIHENhG&u z)UzIA{aTLrwY(%28~_WEJ)jLF3c(7 zRMSWhXds6u@gp%rqZ&XXFg`6dI`GWE=mc5T&)1UsGGnm5ma5h}@};~{eclYp*P^zC zQ;tV-I17pnC_(Dg<29-<;sz~iYDg#byOg2!yR--n8pEK?G)B>k6b(H)wp|P6S)rkf zBtI1m%xt7%CX2~9==W>d?ox)ze=*Y{aA=(GetBlmg(=|4Y5Zy!Y_0HyrJMKyQxZil z*S(Dw>(`oOXI(T+>O*#N5ig{!6W+~p!x&x)h*EdXDm#O?k)6b)koANd&|8=Ttk=Y%L6YckFIUdyU?6e5A7)KOqJogwCNb}f(GMBQaf&QIR_jYisU(2zehJU9; z0LIipsYHXLiSNwi9ij(vXyy?Rn*7~BPfit+$$7tD%aOdcC#N$`b>JzOWrArea7q@r zxd%3Kj>d-hP6eLKG|+iVf9@c(rg>T0d6OgOZ9L+2QRFSjI!149o9M*BddDz`;CPI( zsCrVYczoiwcHin>RifN6)NgQ9#-ySpCw;-Cb(;GTW>Nvh+GgwwoS^h|vw6gwIC;pO zxCpJ9%J|y_+bvL@(iz5%A=sQ}cE!T`%Z>Enw1noB)@1Wa9x5+Rf9LyoabaRVCr8=f z!2$#|AHq|?L3HC%e)vRHk{vkv{aTawtS_fCw{B3hg}U3Vl+dSZq?@Mc30Jn>!Yi9~ z)AR_Rm`Hs9-%=tGEUcGcM?$obu9{x=22-qG%Tat_S51#_ie2d~Xkx4SAr{MfaNim>%H|)5BEUfx+U}X{dm~ zOK-%-ZYU8zD8NM<=$#o#GUe*`Ydv1n^v)#qXpQ zm^kS4idsrilh~6&O7z3G2IZAa4FxKGYO+V|6pHatcOZpH(}%IX%V4rBN<;0J=}ef6 z4`Z`#SEcL&9VGRAJbO!H{4gQK*S$&;>({gmB@LM$7Qag4qt;PuyT4oLNoEvA&^Y%C zXelSLSHpg-e@XM0`xW62SWhE{GuTerEeCg6{dkN%Xdk2G$sxKoh>K1MTs9!ImIU() z`?V%RSeMO+z=%mCHEofA+z9hg6N|WR7^~o4w#k{~7INdfHX%WwhgiRsBl^Hzn<21b zhyXwZUz@vm8a0*=YN9kWNuy8HMA2elzm_9@9S_c6f0KUr_sUrFH4NBue29pidirP0 z4fE-MzKKp7!hS8shZ;Ve5n&eNNor|?EYN$#)GG~h-GFR$vl+ztwH$eCx^6}UQA`iw zCIUz(7Mw;?lKOF}K}-ep@uKTa4RPLtgfa0#(_`YB#*LcJo5<8Vq!*WHaUZBAUUk&lnzmKMp>oTN2x}T2xBkPpFU)#S@}r7r zNVk_b?jn(2Cf2?29qZScL}&dnBf=lZ1z5-Pe@KGS0(!)*P2^KN+HNo>kx85YNlb!i zhy7ZUt`D_d0^mlQ0$~WCYm_G&wKfRD)f%WcLh& zrr$GY79VQ4Y%(8V5)_leH6Dc61->zhg@y)pz?yh{g9bWoa-x&xuwU!(q^9F0Flibi z)xa=;y}uSRB+Q~51p|<~Uva~>YOOQ$e-8TCt%Gyy)P!*YLYTH|P>vlN@ZkBgvYaAJ z*6nlpo-}QvFvK36%*V3O`p2at7LvI&O7gwLkP@0~t)XF%LP}AR9R>OQnzplqA@cKN zo~b~MCqC?XXQ+JWK`%Y~q}jiiJCr2bOY(^OM4q3T@GL@Y@>xVv*&woPU{DQQf9Y(J zQVRYt^}Y#e7(2A9l>Nfgjr0XoIUy6|7G?s^G9kh|g1|{~@DtXHGQkCJV5B6FPD>I# zANFfG5)bI{$$SKN_yuL6HEyPf(><6}rHOnyO-M#@zhBEyyq<3t!5w>(TjA81MspT% zCgMc|E=B{Lx%x?wWBpo=>b0D?f4-0g!b|k1AxgbK0>hZ7?0B|?26=H>qU>p5zm{V_ zO)pM}zznQ-?B_B^=$i6@gt?&E%-~63lC?nu3@35hbTJve=l5$pCiHXLX2amjw3Cgj zeStZ8^J{urNN)IbxGd^v%j`e%Duh)!1b&9VcPUbrAg9Ed2^*{SL$@MJfBccqi(y@= zvX5;!**2U&=r=KwVBFAoVv6rS^w73q5eb>vLeIBjQg+_a%ONmN1X?gCd>Y2Gpk=0hwWFx zxkZk8dAC?}%E(XntpqNtf9W~?Zx~=zW{w*Xk1Ly{9;X~^{>dm!!bb8?l7!u-lkf7H zWi$A>{%U55&P#Y-AVG2ZsGn(3;>UpsAz+~>YJF!g$${UdVXQ9Mf6PpD=b`cms?tRL>dZq>2Eh-ZLHv-qWF=+Pe=lpt_NzV*DVoP1 z_#rfiA7p$=eSWb0YVd=PCZY^tB2ohWvqSTG@i1n*65jC2KIEabx4V*~*SfW_J8WV}k0HvQ6tUVEZZ_V`&FL-Gl71Ls4@YmVJv`wNp+o;Sic%Vim@p9 zBs0((I0KPS#sa@z9leKP3iZeT?d-w$xcIs!u(5vi^c_aL)SrObunA}>L#SJmiS1Ws z4;cOIr)uMGnBfHD(h~VpVZSsL>k8U5^}Y7=OffrO#GbyHBp`qi^5jDGe}wFxxLFcQ_2QNQ*M+pnG;u3}CE zAWO5XI|6wm*#=?1dR$QYDY9CVXq-JEBuGXU>sN0th<*109ThWrzo9YX#}qaRe1)iA zFn+y;IU)Ale+o?>zmP;1g#D_IpP_$WgS6S>SJfmtIqX+s{NW}L`|jkXkH3C-O}1Z+ z^%L&lp&Y${8O9T6vZ!AzO*}h#bI!@7of2CF6~BgYXWdz6o|C2Sj8~|XEGHPI|C=wh zrqU~uRh)6Mp2D;MTC z`95tp-(oxp38#W56Txi!N44e5O388ZsdS;o^<1lWbgANbPIeB^E9;a}Rw3up!9UV) z=9zhq2~w6DKwa_MP`7@8X13oDP*-HAi|zbqXMve3W}W`TBp)-xlo~-y^_vq@O=K5` z{f2^=e`=hV^1#GoFYJhYi79@Aeg7?JnQ+^yL$~cCQsxU@(aeobXDd!m!A|9!a-5c> zLZOW3mF;aggtfX$6xWKRsvao{8j%TK$~YKOing9l*S?>0W5i_EB_#H{^gYszogc^I zPGgFvZLFCG#&Br|PtXcE;ErWxvkN&gT)S;xfBule37t=Jm&!;f^e`qKEJ_E{?*bqt z7B*KrT#+z)$C^Vv@`=@FzOIiC#tsdAP0B{SW()Wjmao+gr0MI)A$`25XqL&vf1T>zOQ}uy1YxZ}(YgnMv3@N_`2oXPRVj+) z9$P5k1qQFmwJlH%s7&&p^#;j-`eCj>*}9HqtY6D$M?$zV&YcrvKP%PNGiQ+NCel5p@T0ya@#N^yd`obrCAeGg?-_3I=9NTwt- zPgFw2Xm$yi=Fz-w3~V2t(tk(fR)%qkg)Al;Al?&nqr;CJi?(!H5qUM^IHDlghA!Er zu3NH}F|3SiS*m5iPpWEL#4qzaf3t^NtDv4JM?6sr<4bMZv1jFH<)@>oX?GMdnU3_r zE^ZvWtmH1__BTO06&Ce6WsjRx;d#lj=_jd0(I=yO(62^Ff^>0a<&jfn3D^R(!ZEig zhNPJmZ)#~L4P6x3$XH6o7S)U;%9?4XEk`yL(P^6<&=5#X!$EO5$Q``if5EFWFwjxy zQp6O`^P_mNi|3;i=M%ij$fG_105ISr+JePWPCka<3w4GObxSsqV238BAIt4AwhbFM z4H~PGb+}BakmGvXa@I@}Rt*8uHQv$EqM5dBUDnfvk`XhCrt`7{qn6UPno(g2vR#|$ zVuz?Nj9Y*s;m!fq^!gtB&s$*z2qZ8AQW%MMHVJX<&L&0WS zkZl!&5UfX0kt7`ks0gMeO17e_hL9E=M=;a8o`B6Vo=JblZ0B%2Xzda=3jUTk_!VLG ze5$;lJS)!?U29I(=^&)-*gn-;&SjxB}X^rSCU zO2b=2Li8hCIFde;W7`KHUD)zX=;t>kB4DViBMOF1goK2G?FJAKsB%DLyw7U$+y%4R zW<%?&GR}_Me~H5u{1x0@_zfP_)!7Mu_g08~-w9s6y`U31;alL1v8Pc#pu_8RvRU%j zQQ>1_A7MLves8&4^x|)zJ$vVNvR@hCZ9OH^np6F4^rNb8^Zk|JC52K?C;KJ(zW(dM zr)&QO5qnOebtveOFppeOP9A`Tjj(lf?m9yJ9oc&IDH#?Gi$Z?fCJQ>)Ovr`C* z?L1;ksbnsMPdmuRT*-7j6b91?%tjK}!8-T2T*V`i3_b2VC(oI99L|~eRj!cXJkY`U z9&c6~jGukXv`W70=A7|(*x&vw9!efb*v|RCe-OE}WbNE#OdZkNAaGnResRCJ!^Pd* zo#O6Vv{-R>my5egaf-VacZwIMNP$vx`+whLKkkQ3b~oAd_vOqaCpph#o-=tSbIwet zZ1J_HF@9@*y_w}yBpWXqxvsBzj>}mdy_8aB+z71NiKU(%Urf+xxXk!EI4k>y6NwB* zf2DE*_7x+$fn627Z=##x2?pP0tiJyW$72Y4ZDBA^*7P{Yg5;0KRa4HrMkoJVRTqK3 zN?P)Ky<3S?nNdwEC7&}_V^^0N!@>3@ayf7z&0kDXd>1xfDa;=X-^*4uE|4MvV&CYz zobbK1;Y(>o*#b5=cKz|%O+&+=dl#TFf4%cxgpVz3Z=ueG7KE}#T~ToY5r{NfKY-?sb!P=Ism7o9!* zhNHmWeBqfY8<7+6m?G`$D!El$?~kD!aBrmRgg+Bb&Nqt1MT(x32E2mSvF^?J2ZUbLF^;W6 zzS~=nH~Mpu{J~kNJMdut3{zwM_VTBSUxneyQq%Su4%4YhLlVO*;%x0=b5zx$9X>Ng z;0Z`vi}ZR9UBP_E|0hyGm$IGfeN4sS7+Cd0r_En0qv3n8X zRPm97jevhUN>70gRy!S4O~?k$B9q2BrNSXEPsI?MXPNF0k5PUHtpM!vqL+UxS- z8@m}b5^473-X-%LY&y*XReN1%M!qV4&blD|w42MPr?mOFmLFLO_PTb`f12I~+FC3u zF)8M@5oV>FLpo-y8aY!$*M1 z+_ab9?#tk6+=+)y=JOcjND2H&!HHW^d6Njs?8#|R(MnjaHoaV48J%tUy=zD@d-6tH zM}pYhkT|TYrk&vV&wJ${1_?i7^ZZi4e^0CF_KVEW;c5F$ z37*(OAKlI>jRyw|UWynk?v#<6r@tizv1hT|5BhwF4l=AUAHAPrSpMMQnd7Yr(HC80 z6i*%zH!Vf_w^lsdcWz4AAM};JR~(2Dj_|Z*IqU~?jBTVD$9v#z-nqF@R8Li)L^VyE z$0~&6RAj03M4LaRe=#4+>J-C6MvZrkY0=(2Ah>5VGSxns5&myYmG~A4pi57UOue<^ zcOoozEXCFK@EGypLh;uzO2HNXq@<1Ssm!%-gINm7^{6bCBgU@8jbm1AAd}<7Z_*M zp~=cKI9ZAn-f4de%h}=kUIyhNmz_bWP}7w|;DVAK)0CL#?id1wRwHwmId}5BzDgoF zLXFFx=OFkPe`<**v8x*PmBTSCXjeJ^D6IDpAG-F_6N#cf52OH7myi;lxTlUVBhGxe z)r2^DZDc-p=bZ!yLL=P5H)_5RyBiWkA$`;ExOAT5e^WNHL6Xl^14PY)Q{+qm*hc~7 ztZXwC-}s8hv2*})Qm~fqPZ|~Tx`cB&z?7T*t5t~5u7%LtlS||YBbBCH2*d3j8eW0C z)D!jcWbxk;_l=$7TA+;;oN`7Yu7+1Ie2{p__C ztaqLX_Fn-(9{3FLs{w@`_yGip!K;o@Wq;aGrQo_6;fl1@>Iz=>wW&a)$zRaH~pA0^Rj z-dlbr7adbLAa+-&qf$z*4Q53MI z7wLay^P3c2bFosl?DO-TzftCaS_SRp%p7H*`vXE2&>ocU2(IFgM(8F3YNJJaseDK5 z_@lQ1*35bwedgsVEDMXY=3y5EoEcfvlJo%6{!yak3WJ_5P3ni=Xe9zLbr~^)$9I5W zeZVu_Xnp!0V3^Pf3N7Ek9HnV4j-yC9#d4L?oIdw!ocovHwT?r^+i8#!>?ku*=--B3 zxx**=8@GDRSy0#A!cfMdcu266Up_FB$B zM!4`tQ3T)m7u;6oHnt6=J%hwOyVhT2d9$%&Q@!Rit1(=uCN_CJ7UE@Axh8i}kc2Qt zsVX-qo~vW0CvGA)e+ysApROkBtO>-n8IeDs8Sc!Fxl?U(hqhOR49 zb0HG*#TnxTU8!INRX2=kc{+TiY14(hISP$}v^#+gNWX+^I9`1EzC3bJQ1+5!#`#|k zQ{{Q&DPLD5z|&<1{E`yVlc3_^S|n zvn%gyY*8Mfx=Z#XiC%B%w`#wU)pVH5C5;1EJ83a-XGf|2BjO-X{8Zf8s@6r}di5nT z?Nl-%)1ET$rL5n{D0O&z+$Tmo7r&&ShP7H<%+Sr$+}4r;|D*1E$02GGL#OeS9>q9C z^%icE&gjZ?M9HW2r_vee%95VjZi(7(MBUut?Ei{LrCni;M@o23is>F8FQwsXnSO4S z5`99Sx00R%JmEKF&|6nEdOmz6%)PAn$Z;V;m8;p9l!*rB_diN>CuR6I4D=@61X`>W zPEa~23gpwbGtH_oHv*jz`Ht4zE=##qwXn9v@0GzJ^ca{e5G=U0TOZ2a*|D(9Ow*te z8k`U}Qz?>p(1e2$)+6iPoXZ_W8%iioH=7j%X75FT5u;(i6^b3;!qyh=ASl^V7DTwNQx1%Y3OXfHlk1z{?XFY`gi^IMmByJi!BVHeR^ked)mfSJL` z7ng^cTsI2Qq|sjolZ%pwr#MxSQ)unLXXbleEJ=LynSh)%Upbt2w0f)DV*bf` zj4}jVdrceQK5{K>Xx2K-HR8^6!ID?I@&!LKN9261p6PYZFoxe4F~)va@hV z-*41{a3bmT!m!7HbnCob(nQ@vdDzY!UpX zfOR>S{p<%)R>}v02TtI^&uG~7$b3pfq?q!rl&k2YSK8-ysmsVjOco4SXVj%Z$r)=v z$SV-@3ahxf}3e-uH?xWSxj?WRFqidITjwCEM$4$<%GzF4eAl>u_*RxzM&? zOll)yA#shW*Rz6AvNfuGg;3RY1=_>L6yPhS6IQOGV~Hkg>yFF$ z0dHVo`p~)3i=tAz_Bpf`q@G%E!;Pp#kZh!6n>ZmWv`SCNG-&f?2s^Ihajha~Dv}(H zeJvLmA++ba@+1k~Ah#wvy|U)lWkId$-~C=R*n6Zj$O+$X$(N%}&kfI9ED8V-`L!18 z%pM~O0$GNc4Btj~;SgF)hu|(BMw_$E)_PVhpA&v~B)u;VQg5Giw_9sIA?_!JRL);@ z@!`6qsgU(nNz{&Q{<^=*$6RS-2P#M>1cRR*X5#lt?IudAPp2A=*~=#=6CE95v4}mK zkib{4`Yn&I7CsxikUX~AeKUcoYJ0`wh#wye(|gntYoP_}y$x@tqx}+nbkXFDp2=|T*zRL}y>a4W7t*#C> zzwFSx^m%n6DO9$+RqCHCV!?Qoy)IQ+uAT52_a~~g(Vq6<*m*ISmRo1g6HKA}Y#0k4 zwICS6pPDL*FBR3Yjli%y;D9r2Kg$|HOq$Clvg z5$Rf_GeMaFk~^1UktGcf*?Ze&2ZrUhin~l3U<=V?rWFGZgW$k1$If5p2njp$y+kJ( zn+;HAop?FpUbUdj730!}i9_a7PfqXu*!gMOKgc}3oy9Lf)D z(Gi~uQ)tf@dWIU`RRFnn(b%+8gbJu^^OCQpbDi9+AnWLLO3Vhw&N}`Xq<>?CP<|E+ zqLK%Xa&=6eS|#u*%NC%VU(}JbpqpSX?P1^-y0K%UyX(RTxgau?{^rqn-bLqHY7hgr z5`p}0rnKU=*ltaM7YJ1@+J%bLeMHiOr zlx&rhP+Qg8@~lm#Va=U?vYd47)-E~bt&EkjdYZP~h7A@1rVd>G~H({90I_*HD@C*1YmA9D}d; z1|?V3(+3zJY{M{^N2umZ2YPSCMUZbrNyZ?Q*P`%?krFozSo!Q!6m`0WQH7j9khvo(u-|9%37lVF(s3M49Ksl#5eq>`gEDamSiI$wV1TZ`HB6B zkL@;*tl?}~Vok8~zL*VGVFf6`m%BWv&kmAX`;s_HiA1A!N{gP3N%|v~EtdGDl^vU#p)u6Cg#>*+-TD z#_yyNr&z;?c#JwQM^zTCbFInYF4C(w?NiXDLL69JqClcuL_AK7pp~L~(QQzQQy>=S z6W2zOo-(F_9`PL^h&-xJmUL1$sLPY^V8!Wg7(4XJS~KpG#@Kl-kL>Hv3tkEv_fAp7 zzas+cA5I%&(M32&n{)M}6Ae&-cgl~YgK^~Kh_#hwmPdLu#Eh0Ky%6WiNx3NMsJx01 zjgUw`Z)eYg(g$2zSYAc8TQd1K|0@HKC^mB9>5e5oi@(6&WP?KK?@+}^sFVJo zMoqi*o?hn+boCNUIM32sF#}|#%A1tR*~bh;EKm)wp0LfCr%1;IttW+f_zq13*cAys zdSgO==d;_abU>Caf_QLel?3oCMR7}Oz^IJTTjMH{S6o;>Dd^p)Po%!B2M_&@;?|}? zggXebu^~OR8w6)0{mUlsVn_zEK6MS{%fgA9#7d2~VG$9hn!pg4=LKQ<2ruKMx)s z+m4gQ6U(UZ8__=ibqr#g|B--U8)vA#b^@ep1<8ZVh^?B9_9w6}h}_C_$xtU1IgQdm z)k}gEZy##)^c#M?(wFi8ZZrfw75pE`BK+=oL&PoavCTmzZD`DjQY7=jlt~cJh?jzy zdtm^Tx%KPQ=0r-F-62V#7k)s!jz5kJAYAJ7^FOPbKaTcAvBAB}kXGuv`(z{fl|#@Lx3DLQXPu zJezP7`Y~Ears1{nONU@|wNh>}=?HB#^n8eSi+0d3fh6D+cBQEMw^^e$x+Apl^6_ge zLJ+20q?kkj%ESpr!w8)*A>ls3G7lzW&3Gp%mQJH7JZ7$iy&68n^}_+G11OU@8)9;3 z&8<#ctc&K+?bQf#OUDx8Z@QYx2~Pe(QW2qqX%Uu)`=lpb6H~m}4>mNxgY?v8ZcTG^ z5t*K}4>I8SCwp1I7bJ}bU8-qOCj6vz9xIfztGyMDJC}Zn$toJ|3+gDuK2QCD8FrZ; zeGSp^ZyFI&_e+0%8sp4jP!Og%)|R3mgnBABFaAw;b4drV6o?-9%urGqc&thiZ+Wgi zcvtKbtl$K3SB#Yg1>n8Z4g~bJb#PA$vVrVzg&ZPf~(; zT8sd0>y_3@((b=nqF zFAQm5?KTZ{_^cYzcx&SPmSB$g{ve*gX7|v_{2^$g@Lxh_miH}d6M&!SDl2KdW@BE^l*Nh?FY3^FlV+fJ9pO(DcQOFaZPG}0NYSg5^4><8UV2wo zHn6S$W=8wK-JClZ%L?Xv%?A13Y7PwhgT)wLGb%X<2OBs36ouel17X>q?d7c1j$_s4 zijw`0XmTMsgRsUqx)@d+E|n!a$A1fb~fLF#PIHE>;gr8wbX%S zVl;`B2P2WMs+1n_`Vi(YCgV67T6c^;pOrlLNvHA<*0nybo3YL%;o2Khl+mtSR&eXQN)PY^7y^h=B9z?#w z&gwY)h8!ZyHd&1qOKwdW-~m#XWuM?t=Bl4;)zU40+G!;J*}OpWYGukj!ui)!T$8C~ zzFMFTZHw%SXp*@Zwyk7%^Rq{Sg%>LbYv9Ms>rr)0-F7H1i_s)!QW#ghMAG71WpTc( z84IRrMDM2PJ^jc(4yuxUieZkZ-@C;}&j{4PSghZJ;*M=Bxo6cMx_v;qH6!ZRqAe?` z%Hy!g7vl0tKHm+RXKn@iI;OFQz*A0Se!hOQUi5L37sfsBe%y;{-=(b3tHXw7e^ejU zQUtkIo;b7P!1ROTQ5%LYpFth!uPqJ3AHvBE`;yC*MbSqb-gLM^ zEy|0!uDi6e$ICi;yT#r(I$Ot*}k9cva? ztX~Ee+}u;BW|{6R{leh`*To|cV&Ht&N@v&>sHLL7cP&WGRz?84u>liHC5^(meL;@T#iVC+xgMT3q&at*NAn0Vsy zs$y;=z}xw~X2~P;@GDLKyFBZ!&t()X7i$J-cHZHgOQwVG7tFAA+QZtSus44u=9+4oBo+|I$NT?`jkcd$tV^;ll!P;uFE(0D9QEr(-H?0?J+f ztj;LZ-zOEA4afS#gu0+^p)Q>=Ui8}qjC$+pIl^B0b>YahQVTrDS zQ+l~Kn@z-~K<`nXR*VVYpyRpM`oSEtwV7hJx?8hIofH2J5dGGi>rAv|09XHqpdF99 z2A(`VAjJ-(D6WI__Sv^apHO#2H18zuuX~c)KHmx^d#qQ)LSKDVm(h8_zmnlwBijAr zt0J}*eHEkE;;`1HYf&t#-j6RTI&l`4UC4(jAkE2nXm6^I!)fHYRZ=EL%({ca69&Uc zX_8aFct0Nd(c%P|z(<*0qkYcC3vEpeB03t%Tl@v6&HyZp6c!Yb1>N*cI5E_cdSmgI z-l*gPuQA1%{sTMCh22HAB1-Vf(YoD#a8wEM252$7kEg58ZxL={edng_emzmk#+szO9kyG94X|BCN+Kvkkvm?Ns%~giZ@?2rS%lhDahzaYx z)l*CuI@ajKHVF<;+l*+eq&$!ChTkpLrcZ25loV*4ZH=$ zghGcPQiz_Y*V&7GlLI(YPL9FFE#8NwwFri}tVmk^H z_BfZfbmh8cln=>Y{yBQ(`Z%ApE<->84jLL_icp3c3tmvElJ3m(74tj#ky0gCZ(-*S z<8k=VPh3!cjC-8y*?Cbk9Ocv^VT)s9SXMOJvcXRW2hGdO{0vsF9M)&238Z7g&Ni~# zi{ghl&h{SmP;N8EhbTK!gm*yRVjjvEB<#M|Q7YO06@}Vf79*7dS}ruY`U2q0KH0S_ z+I2iczJtU*?N}b0&}Wd)l=SS5`niEBz%6|Fk8GjQL^aG3lFw+rvvR11243YCUxCX9 zv9XmbLDq)&Uu=i3=^;Am#bm?Igu7EHBqJbW_R>9p|FH5m5AHzzN?|TvAVOE zxQL4Y=t|?_KrSMIw5(VWg#f_xIY;`j^iAZultu{u2shGH@W}3VIn+FA_F=cwN)4TJ zz??J?ny|#o0?iOvUP3x~ET?M-QXxT<5W48Fq4bdHAxmC4$~Mr@n800?*1x7C{a2Ol z=qjMOii0oo%n(yy2vYEo^p8SZyd*MSniOa}SC*_F?N~KqsyF|l8$OW!eryz-Y3o>i zm_`zxYN#C?WucWmVVL$?TyNnqzx6awUixm~Po+X13%ZW$<0M6YXx}M+_L4`D>b8?m7c9)`m*GS02WN}MkTFgfdUWrDu|p|P#>F>8Aj7u z+DtnF$fP2YUH_v;t25yDHu|-YGZNthWVD?AVYDccI;U*BRQ5x0uYkeB_=Rbkpj@@; z7mgtx)>&h{peNE zGwSBm$;NLOuuer|t(Z@WqT%~9x9j;09XKC95K%-%h#_NnS4$v)xwt3vZIm;=Vq6$z z={XPv)A}C@EuHA@o&)p*x;zA3lZ4Cw7gN?#$XWo}OOc8sc=s0KdsdE7-|2xi_%rR9 zX2E#W!B#r?8+2O7h|MZhiA*=eB9bo55^?7#N?l<bQxLw+$r+3cJ|N?IIl zTY6B5)EPA0L?d&hJ(*ON{G&L+4D&0kh0uC}e-tqWrUdw;b+ovj?t7K~)*96qX;vk> z|Cga=%HO?}!0mG*^ltQ$SfE;IyviSXYhSp~e8VDzjvdLGC-2L1JXWXf=K|{_Ur~W7 zitkxKE zuqRD%eF>OgpfxCp!5W?LL6w(QHxz7_#H6Yt-KJEL`vtcNl|bVy%j!Bo%*qOviv6UG z=LGT*nA`H-XT{I=XhKe`W2#}UX319LN~@v5G&vio1xh|?A1^Kc@k7h_Ck*WonG;p& z$jbMY;|K^B(Q25GRj;Aw`1QmIZU;p0JsIjQBvC-Xpy4jNajPb^4Ce**8%&r|I13>`HaWjk|3_ML5{UtfLeC-mXczLKSyb+NI(j$ z4I7A}!D5$6sv%hI-Xl7^pkTM2%kF<1*Q~i|)yDZ58U(gwOPtGAouMFx(lGpQ4Pz|a zL^m20S9XmCq$T=mo`T6Nb+EAFkfGBsqrR9jiqLfoclg%zFsXpcL7?$cekyYGFmBXg zv?M0JEcAw~YFT;s4pwCMSLA|DsFG|TeKO!2%lN1LAPMHaL~(PTvI8+=+*+cz3rsL&KD zk^?C&QTmiB+AjOvd?PoJa@!yGf=h%m>alt9|fYI-933o55sJ zc{KumtHG6J7-Ox}glKS4<-j09ooz%h=;1rHXiU|L=<-|&!^qq~xOIQh? zRxcdL&%hTyO7YW|)1z-)=SnSs#FCD#gqzM@;mo38f+62s(;I*ejX{ixJOPa1GQ7y^ zw5SHq3yXiTH_V4Wb7U=1JQv-kNCW)YeNfzYxknWp!6hoI|K4AruLR!vTs2A_>2q5VV3|GAc8v zD3c31q~s=!d|R7Zr&VGj;4kpWKXxVUh`WpgZwf*^Va?YvJXVRMg#Zw=XIkbh@X>&AH!T@OJPfJl*OWx0} z4@eR)8T3;d;y#`=oZNx~lz1CNoO??KH`we2i#u9^*!L>BkQb0Gyxt;GS_6c%zjq|YG zC$_OsE4`d5b?5lAME-Vz0w@v>9GsNzk}d<7T|1u6D>u7tv@U%SSQ++6zeMQ>>T>X| zeAuvL&SH(po~!J_h|NNA6YQO`AOd_YVbDu8?070;mhvO`WrkM`#HVz}p#)k(MD@eh z>E~G^r7_CZ0f4gr6Wc81agjHMU##!ON!UYQc0>_+Tk;=PhHj^)wf&XlGO*r_0MSv_ zHMZ4|tw%AwFF2Z|qGgD!mdH)kP~AmTGbz=VtTgOnP+mZk@t+!W!mgmOTB7d_l@L#7 zp~EpR%P?{gLd~j(`FLJoPz5=tw`juZwIYkulsclvCr~8&VqIf7Pt7Bxz zWnjJh;lJ^**{zr&Nng`-4c4~WUU^_L?F?BBAKMT5xe zHacn*5w-P9ZC+tjqYmhaOtc?sqR;iGgIxOIK*)h^@Muq7kqV;Pf z1_1LjN^hlW1yXKCoLvWUNP=kmX{e8{s>{Zv$~)^Jbis&k?~+b&(tg;^Tf%fqEKAyFaccs#4(mOKqXOBNM4t6W zaf_eqg60juOYY~eR`(ksJj4`N8JWRPeWOXnMbX`O54ZdbPh$5bDblvJwR>{w1QgYB z?&`mIWSM&wqnHgkA%34tWNtzH9m8KMhG!|i!&swq zSWTAa4pon*c5>f@uQjG%NQyS?oyOsstDHQG5OnE#07eEeesQ5TD3fThw(r8fiCguW zZ-Rp3vujvCsmEfFDNJI?Crr->0O8CI*8rY?B@aeJ^KHki`N6!vQ^ucXqX)4I{*@PU zkP!7{bMve#b~Q|8t|0=vziDIA;WI&ZA6~ZiBn`^((zRYa9-MBU?L#zFv59R5=1rv^ zY}dSe?77Kx>0e|$9ChJ=>Em5?e^0>9b;F`}1ne~C4?ik)yeB8!8avp;0C{$y0{MT0 zg&%_)G=Ghdv8e_qbp}TxE}$GB3qLl)4J69v8@!K2{6{Kl5^a&#S`r3L7c#W}L^1j| zyO4w3_at#2B)Vg!)X`>vl@K{{Y&1WtsnD++X(o`lO(Zld z6CYh%7Lg}AI!2ORLJx-V0dOg)4{kru|8z`K>jvXR=pOap?%*+ogsJUa#iJiEbBxo&&-4I`hy&~MWa?LLcm!D0NtFKS`$fi~aDIMcuvv{?5E zvonP3mx$s#>YAKj6}QteWPB@@1p-2C6ayV9GLP5YvPNx9M>-40e<8S|D?UvoGeJsdy1agd; zDu(Kpt^3K|Kkf}s6Lk3$pAco>>Bm&b78Mz+877sCkhCdifZ{dmR}Vfhs+ywnqYUHn z%6T@pzlzYpYXqURJI4>bH1+65HD7reD~9%hC{-?9GBY1~x~%|F26w^*cIyCy<@Xl? zI1)Yn51mw%KjVVNr^sb@EQ+BAdFw`rgKw$^gEs z+D${`PZG`j0 zqWZY5`cL!1HxfeD?48v(u_%bHo?tc#=(B)^&0KAYZc5YL{ZOX-nNeS7c;Lsv!gx)>cw7 z|Ni;g<@qvP?vzw-5~itYJin%v1HzaCR10m2pU;$%O;eox(Ib0s@-DV=#lB{fw5^V^ z%-bd68E-!C%Sr{3R8^aDRg@GpO0ecRapH#AM%8w1XnsX1kXpL`jmJ?M73MLnkWY*S zctsl48{(W>3<q&I5h4r7?Lk)p%WEWPua zP!iQM!}pECE0#}o<)*Dqb9F?&At8T@uDWXdb@%nXVl-*=9XxH6Go(edb!2E_ZUldy zi|$*i{Lkzsj2hUW?6WpBfABkRukc?i0X-jN>boxs=-l> z@yk#VdUX4tqa&I_v)MDQATN?gMvRP7!ep6zns91nno_@{Y!FLmW1_oHwA6o=0XH@2IK2=+z5LE*qQZL?7JO@;yZ|k%)+FU!YHr=9CXhh#V=MYi=V36M24>!_! zm}-BHt*g)Id6eC%%}(^iU|<*o9G|ofaOzIFQ0_dL+An@zUm5-AoxNWNC);hv@u@EO zN6()4(Z(jGrKBvep%CLU>uy@d6NwmEG_N!ThZa?^)mgfQ0ILIu4*6D8bKTZ(HusDS z|M=H+#J}1f$8j}%y6u-v(0J#(mqusQ=|r~<(Z)-X`xb^EMX%vd5)CalAQ&_h5P~~E zR)`rOlG|deY!^t}SpyVg7N)rDVqf$%Wa&dYQh=Qb3t_micQP}+ioQ?a1wyBpurtX_ z;m(u}zXeMDaH6MwjXgXuN&7*B;$;A9nBdY854S0XsF^Rxm@J62PN@MSR!z+-%H{6o z?RfB?WRDUQ1b>e)QcxZrPr7#H+Pgxy8D}AE>SPbAewlbd=O_K*$?yX^!dPmD@e- zxoFJ#ImU-2aUZKmATyGsAals!lHACFGef20XjH$ctCtkL>kA8)zwSj9$vHl-^+0oMvJz$ z#EV-`$Db+mawa}-Kq*O4$T2&e_&S#Y+#BFoa#S^p*{|K0a`qa($7u{Upn=KtS9#%Q1yvY!kpey@h>MrOWMIqDT}p zI3FmXY3nGo@1eJs70~v4lMyY!7v`iBJ=~<2=)an?iPos{nN+sK%<6D%mh;YON8uD9 z$RAC9)n2QRb+MY#Ku=5D+V7mpA~2r{HGG zvA8iHT_e3ufZZGN;`cDrr{u!(OQa3k^nK6hAyrX^pB6*Jquv(?Qgbrm7_3{oqc%Q{ zk|UYy4__#32trk7}TRT)PwF{494Ym8|ubNOyKxb=mJh z{#wn0z+x6c!NB{(TM|s26N0r3w46b(tt}WIRx4hO!LD|v;HGzwSFgL@r z+mqf}<}gfs^SPDjq?da5TyW^z+&#*sI)68DpXmdpV4mXyF1xXMvr%u!nJe>uAt9$C zedi~{lK#kX9SUm&-`sy1^JdZAURm9McGd@Yb+v1;BRj?UABGbf-r2{ky)Pi-mgW8e zn>l;4tpZX{@ep|JqHXjcyJ!ZU7@Me*`%QlB_<|SZKWqYwgy~LuC^JVkq)XuFLso|v zU-fi2WWs0~Z_UNx11dFHLh zLR4@X(Q%omO%Nss_zy@xzCkasrLDa&A|&49{17!|BJO!?nTgly%ztD#E_u%W7Yw{> zSbr-+r^z|4+~2oWN~bTyPN5IX%2R`XDhl2LQYYTlGK71Nt5pWpSP&QCDpJSvl3B8#YCQJ&4llo7I8{3N@_q4~4k6uUA)E`R=K>=%DL=PZm@xc@2lde;d8OUwr(ogMg`ZBqQvgVdb# zn*7H2T!4I0tA&V&_#riBcg+(K25sSu`OPvWzuLD$V0N=8_^f%ll$F%(Hg$!!N|SqG z$zzx{+GG$0f#QTOd)wnrb_Y`q^sG=3#ggW>@>y7g{#{tSv97-4R&lfn;Oejj?*2{aGLGx zcwi~mEU2m282=(L8pniNJa#&uAvL>TJdNh7*(V##5)>Pa`P0Ku?j<0Yo*3ff?dRq= zArXdI`Ts_!=Bn@}OgDG-?*ND~ zq*A9@$vnc{>csc{otcM_BF~99$GNW@`)~y%)v&bix+GNG7cq9A?b#z8=;w65iY{@Wk4<`B(pe4@*Dp`#EtNFcXjb8-R ze>=_{le+m`SzkSZj=cgTVe`0m89{qm){tf&4 zZHuVB&AJcA*PIaw@7~f?!-%+l&P}YSFV3Y=q#xWizn{+v+o#{n;J!SUL?4bUKB(W1 ze=G3yK~Em)^xwhBy5~Xbs93ACNca7^tST4PT$zmuiMdIOpa=eZv}2or$TNnoHALGgQvK#D~IQCw{t&M z?tOZnXR8NtX9t2`cE%8I($as?Al}|6yiZr-ObPG`Acnnp2WfwFdIiNJLj^i`$c!KA z_>%+Mf<6aldzg`d{eqbMcEx%^Y=bh-nns=qK__;#P4S|`_@Za z*@Lsq)81aobMI847s}?y(KphKt_(!c{{N{?zV8g_Or*I$1UVq#VqhNUzr!`xTuV4P zj?~n?kmEqVqQj#v`nC0muD`7Z3BLWlb~;=OS9t~v-I?s=nOOKRGIDcs!?EEYzJzcV zzYou@jgFzfL})X3v^u|;-#)L*?LGnWg75Bun#-Ea@A}}ElYHRo_5U30AIR$H(TyM# H2q6ChNNVT} diff --git a/spec/features/projects/services/slack_service_spec.rb b/spec/features/projects/services/slack_service_spec.rb index 16541f51d98..320ed13a01d 100644 --- a/spec/features/projects/services/slack_service_spec.rb +++ b/spec/features/projects/services/slack_service_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' feature 'Projects > Slack service > Setup events', feature: true do let(:user) { create(:user) } - let(:service) { SlackService.new } - let(:project) { create(:project, slack_service: service) } + let(:service) { SlackNotificationService.new } + let(:project) { create(:project, slack_notification_service: service) } background do service.fields diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 068137f6255..9b49d6837c3 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -136,8 +136,8 @@ project: - assembla_service - asana_service - gemnasium_service -- slack_service -- mattermost_service +- slack_notification_service +- mattermost_notification_service - buildkite_service - bamboo_service - teamcity_service diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb new file mode 100644 index 00000000000..b4fb1cd9ed9 --- /dev/null +++ b/spec/models/project_services/chat_notification_service_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe ChatNotificationService, models: true do + describe "Associations" do + + before do + allow(subject).to receive(:activated?).and_return(true) + end + + it { is_expected.to validate_presence_of :webhook } + end +end diff --git a/spec/models/project_services/chat_service_spec.rb b/spec/models/project_services/chat_service_spec.rb index e6314a43501..c6a45a3e1be 100644 --- a/spec/models/project_services/chat_service_spec.rb +++ b/spec/models/project_services/chat_service_spec.rb @@ -2,7 +2,14 @@ require 'spec_helper' describe ChatService, models: true do describe "Associations" do - before { allow(subject).to receive(:activated?).and_return(true) } - it { is_expected.to validate_presence_of :webhook } + it { is_expected.to have_many :chat_names } + end + + describe '#valid_token?' do + subject { described_class.new } + + it 'is false as it has no token' do + expect(subject.valid_token?('wer')).to be_falsey + end end end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/mattermost_notification_service_spec.rb similarity index 54% rename from spec/models/project_services/slack_service_spec.rb rename to spec/models/project_services/mattermost_notification_service_spec.rb index 4928391fd7e..c01e64b4c8e 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/mattermost_notification_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe SlackService, models: true do +describe MattermostNotificationService, models: true do it_behaves_like "slack or mattermost" end diff --git a/spec/models/project_services/mattermost_service_spec.rb b/spec/models/project_services/slack_notification_service_spec.rb similarity index 56% rename from spec/models/project_services/mattermost_service_spec.rb rename to spec/models/project_services/slack_notification_service_spec.rb index 1e5b4c715c3..59ddddf7454 100644 --- a/spec/models/project_services/mattermost_service_spec.rb +++ b/spec/models/project_services/slack_notification_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe MattermostService, models: true do +describe SlackNotificationService, models: true do it_behaves_like "slack or mattermost" end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1d8e42202ea..bab3c3dbb02 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -22,8 +22,8 @@ describe Project, models: true do it { is_expected.to have_many(:protected_branches).dependent(:destroy) } it { is_expected.to have_many(:chat_services) } it { is_expected.to have_one(:forked_project_link).dependent(:destroy) } - it { is_expected.to have_one(:slack_service).dependent(:destroy) } - it { is_expected.to have_one(:mattermost_service).dependent(:destroy) } + it { is_expected.to have_one(:slack_notification_service).dependent(:destroy) } + it { is_expected.to have_one(:mattermost_notification_service).dependent(:destroy) } it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } it { is_expected.to have_many(:boards).dependent(:destroy) } From 24d2e662d4251ed8c73a57319576af6de3d488a1 Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Fri, 2 Dec 2016 12:38:04 -0800 Subject: [PATCH 133/386] Limit description container for mrs while viewing side by side diff --- .../stylesheets/framework/variables.scss | 3 +- app/assets/stylesheets/pages/issuable.scss | 52 +++++++++++++++---- app/assets/stylesheets/pages/notes.scss | 4 -- .../projects/merge_requests/_show.html.haml | 2 +- 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 55d97b9219c..13abe6033b2 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -166,8 +166,7 @@ $row-hover-border: #b2d7ff; $progress-color: #c0392b; $header-height: 50px; $fixed-layout-width: 1280px; -$limited-layout-width: 958px; -$line-length-width: 700px; +$limited-layout-width: 990px; $gl-avatar-size: 40px; $error-exclamation-point: #e62958; $border-radius-default: 2px; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 2357dd2fe6f..0b15a72e6b4 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -1,13 +1,46 @@ -.container-limited.limit-container-width { - .issue-details { - .description, - .note-body { - p, - ul, - ol, - .code { - max-width: $line-length-width; +// Limit MR description for side-by-side diff view +.limit-container-width { + .detail-page-header { + max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); + margin-left: auto; + margin-right: auto; + } + + .issuable-details { + .detail-page-description, + .mr-source-target, + .mr-state-widget, + .merge-manually { + max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); + margin-left: auto; + margin-right: auto; + } + + .merge-request-tabs-holder { + &.affix { + border-bottom: 1px solid $border-color; + + .nav-links { + border: 0; + } } + + .container-fluid { + padding-left: 0; + padding-right: 0; + max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); + margin-left: auto; + margin-right: auto; + } + } + } + + .diffs { + .mr-version-controls, + .files-changed { + max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); + margin-left: auto; + margin-right: auto; } } } @@ -23,7 +56,6 @@ .description img:not(.emoji) { border: 1px solid $table-border-gray; padding: 5px; - margin: 5px; max-height: calc(100vh - 100px); } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 10eb3d4203e..d697c8d25df 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -383,10 +383,6 @@ ul.notes { .note-action-button { margin-left: 10px; } - - @media (min-width: $screen-sm-min) { - position: relative; - } } .discussion-actions { diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 0db5548d36e..1187b04edb8 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -42,7 +42,7 @@ = render "projects/merge_requests/widget/show.html.haml" - if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user) - .light.prepend-top-default.append-bottom-default + .merge-manually.light.prepend-top-default.append-bottom-default You can also accept this merge request manually using the = succeed '.' do = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" From b6a1c0bf9bf9dd9c9a017d5867d856cd9086d8fd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 15 Dec 2016 21:30:35 +0200 Subject: [PATCH 134/386] Add missing group policy spec Signed-off-by: Dmitriy Zaporozhets --- spec/policies/group_policy_spec.rb | 108 +++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 spec/policies/group_policy_spec.rb diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb new file mode 100644 index 00000000000..a20ac303a53 --- /dev/null +++ b/spec/policies/group_policy_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe GroupPolicy, models: true do + let(:guest) { create(:user) } + let(:reporter) { create(:user) } + let(:developer) { create(:user) } + let(:master) { create(:user) } + let(:owner) { create(:user) } + let(:admin) { create(:admin) } + let(:group) { create(:group) } + + let(:master_permissions) do + [ + :create_projects, + :admin_milestones, + :admin_label + ] + end + + let(:owner_permissions) do + [ + :admin_group, + :admin_namespace, + :admin_group_member, + :change_visibility_level + ] + end + + before do + group.add_guest(guest) + group.add_reporter(reporter) + group.add_developer(developer) + group.add_master(master) + group.add_owner(owner) + end + + subject { described_class.abilities(current_user, group).to_set } + + context 'with no user' do + let(:current_user) { nil } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'guests' do + let(:current_user) { guest } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'reporter' do + let(:current_user) { reporter } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'developer' do + let(:current_user) { developer } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'master' do + let(:current_user) { master } + + it do + is_expected.to include(:read_group) + is_expected.to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'owner' do + let(:current_user) { owner } + + it do + is_expected.to include(:read_group) + is_expected.to include(*master_permissions) + is_expected.to include(*owner_permissions) + end + end + + context 'admin' do + let(:current_user) { admin } + + it do + is_expected.to include(:read_group) + is_expected.to include(*master_permissions) + is_expected.to include(*owner_permissions) + end + end +end From 1235d96e973258b9c968049945426cb28bea9998 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 12:10:56 +0000 Subject: [PATCH 135/386] Add dropdown statuses in mini-pipeline graph --- .../projects/ci/pipelines/_pipeline.html.haml | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 3f05a21990f..f17a0d668b7 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -43,10 +43,26 @@ %td.stage-cell - pipeline.stages.each do |stage| - if stage.status + - status = ci_label_for_status(stage.detailed_status) + - hasMultipleBuilds = stage.statuses.count > 1 - tooltip = "#{stage.name.titleize}: #{stage.status || 'not found'}" - .stage-container - = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage.name), class: "has-tooltip ci-status-icon-#{stage.status}", title: tooltip do - = ci_icon_for_status(stage.status) + + - if status + .stage-container + - if hasMultipleBuilds + .dropdown.inline + %a.dropdown-toggle{id: "dropdown-#{stage.name}", "data-toggle"=> "dropdown", "aria-haspopup"=> "true", "aria-expanded" => "false" } + = ci_icon_for_status(stage.detailed_status) + %span.caret + .dropdown-menu.grouped-pipeline-dropdown{"aria-labelledby"=> "dropdown-#{stage.name}"} + .arrow + %ul + - stage.statuses.each do |status| + %li + status + - else + = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage.name), class: "has-tooltip ci-status-icon-#{stage.status}", title: tooltip do + = ci_icon_for_status(stage.detailed_status) %td - if pipeline.duration From 01876eccad2bbc9ea2f35cb27c41b7373b20f26b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 13:54:23 +0000 Subject: [PATCH 136/386] Initial commit --- app/assets/stylesheets/framework/icons.scss | 24 +++++++++++++++++ app/assets/stylesheets/pages/pipelines.scss | 26 +++++++++++++++++++ .../projects/ci/pipelines/_pipeline.html.haml | 7 ++--- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 226bd2ead31..88b391ef2df 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -4,6 +4,10 @@ svg { fill: $gl-success; } + + .builds-dropdown-caret { + color: $gl-success; + } } .ci-status-icon-failed { @@ -12,6 +16,10 @@ svg { fill: $gl-danger; } + + .builds-dropdown-caret { + color: $gl-danger; + } } .ci-status-icon-pending, @@ -21,6 +29,10 @@ svg { fill: $gl-warning; } + + .builds-dropdown-caret { + color: $gl-warning; + } } .ci-status-icon-running { @@ -29,6 +41,10 @@ svg { fill: $blue-normal; } + + .builds-dropdown-caret { + color: $blue-normal; + } } .ci-status-icon-canceled, @@ -39,6 +55,10 @@ svg { fill: $gl-gray; } + + .builds-dropdown-caret { + color: $gl-gray; + } } .ci-status-icon-created, @@ -48,4 +68,8 @@ svg { fill: $gray-darkest; } + + .builds-dropdown-caret { + color: $gray-darkest; + } } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index be22e7bdc79..50e8165a017 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -734,3 +734,29 @@ padding: 5px 5px 5px 7px; } } + +/** + * Builds dropdown in mini pipeline + */ +.builds-dropdown { + border: none; + background: transparent; + padding: 0; + font-size: inherit; + border-radius: 0; + cursor: pointer; + + .dropdown-caret { + display: none; + position: absolute; + top: 3px; + right: 6px; + font-size: 10px; + } + + &:hover { + .dropdown-caret { + display: block; + } + } +} diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index f17a0d668b7..931dd9d3a71 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -51,15 +51,16 @@ .stage-container - if hasMultipleBuilds .dropdown.inline - %a.dropdown-toggle{id: "dropdown-#{stage.name}", "data-toggle"=> "dropdown", "aria-haspopup"=> "true", "aria-expanded" => "false" } + %a.dropdown-toggle.builds-dropdown{id: "dropdown-#{stage.name}", title: tooltip, class: "has-tooltip ci-status-icon-#{stage.status}", "data-toggle"=> "dropdown", "aria-haspopup"=> "true", "aria-expanded" => "false"} = ci_icon_for_status(stage.detailed_status) - %span.caret + = icon('caret-down', class: 'dropdown-caret') .dropdown-menu.grouped-pipeline-dropdown{"aria-labelledby"=> "dropdown-#{stage.name}"} .arrow %ul - stage.statuses.each do |status| %li - status + = ci_icon_for_status(status) + -# =render 'ci/status/icon_with_name_and_action', subject: status - else = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage.name), class: "has-tooltip ci-status-icon-#{stage.status}", title: tooltip do = ci_icon_for_status(stage.detailed_status) From fa4d41bf1836755cbf1f28af1d7841dcd81efeb8 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 17:14:10 +0000 Subject: [PATCH 137/386] Render with new partials --- .../projects/ci/pipelines/_pipeline.html.haml | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 931dd9d3a71..e82faf4f6d3 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -43,27 +43,28 @@ %td.stage-cell - pipeline.stages.each do |stage| - if stage.status - - status = ci_label_for_status(stage.detailed_status) + - detailed_status = stage.detailed_status(current_user) + - details_path = detailed_status.details_path if detailed_status.has_details? + - klass = "has-tooltip ci-status-icon ci-status-icon-#{detailed_status}" - hasMultipleBuilds = stage.statuses.count > 1 - tooltip = "#{stage.name.titleize}: #{stage.status || 'not found'}" - - if status - .stage-container - - if hasMultipleBuilds - .dropdown.inline - %a.dropdown-toggle.builds-dropdown{id: "dropdown-#{stage.name}", title: tooltip, class: "has-tooltip ci-status-icon-#{stage.status}", "data-toggle"=> "dropdown", "aria-haspopup"=> "true", "aria-expanded" => "false"} - = ci_icon_for_status(stage.detailed_status) - = icon('caret-down', class: 'dropdown-caret') - .dropdown-menu.grouped-pipeline-dropdown{"aria-labelledby"=> "dropdown-#{stage.name}"} - .arrow - %ul - - stage.statuses.each do |status| - %li - = ci_icon_for_status(status) - -# =render 'ci/status/icon_with_name_and_action', subject: status - - else - = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage.name), class: "has-tooltip ci-status-icon-#{stage.status}", title: tooltip do - = ci_icon_for_status(stage.detailed_status) + .stage-container + - if hasMultipleBuilds + .dropdown.inline.build-content + %button.dropdown-menu-toggle.has-tooltip{id: "dropdown-#{stage.name}", title: tooltip, class: klass, "data-toggle"=> "dropdown", "aria-haspopup"=> "true", "aria-expanded" => "false"} + = custom_icon(detailed_status.icon) + = icon('caret-down', class: 'dropdown-caret') + .dropdown-menu.grouped-pipeline-dropdown{"aria-labelledby"=> "dropdown-#{stage.name}"} + .arrow + %ul + - stage.statuses.each do |status| + %li + = render 'ci/status/graph_badge', subject: status + - else + - if details_path + = link_to details_path, class: klass, title: tooltip do + = custom_icon(detailed_status.icon) %td - if pipeline.duration From 708125853117916ce3eeb809e5bb7518c8e5e3d8 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 18:34:56 +0000 Subject: [PATCH 138/386] Dropdown with arrow --- app/assets/stylesheets/pages/pipelines.scss | 288 ++++++++++-------- .../projects/ci/pipelines/_pipeline.html.haml | 15 +- 2 files changed, 169 insertions(+), 134 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 50e8165a017..df88e7b5925 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -576,17 +576,14 @@ } } - .ci-status-text { - max-width: 110px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: bottom; - display: inline-block; - position: relative; - font-weight: 100; + // Position in the pipeline graph + .grouped-pipeline-dropdown { + right: -206px; + top: -11px; } +} +<<<<<<< HEAD .dropdown-menu-toggle { background-color: transparent; border: none; @@ -594,110 +591,81 @@ color: $gl-text-color-light; white-space: normal; overflow: visible; +======= +.dropdown-counter-badge { + float: right; + color: $border-color; + font-weight: 100; + font-size: 15px; + margin-right: 2px; +} +>>>>>>> 5ba6f0d... Dropdown with arrow - &:focus { - outline: none; - } +.grouped-pipeline-dropdown { + padding: 0; + width: 191px; + left: auto; + right: -206px; + top: -11px; + box-shadow: 0 1px 5px $black-transparent; + + a { + display: inline-block; &:hover { + background-color: $stage-hover-bg; + } + } + + ul { + max-height: 245px; + overflow: auto; + margin: 5px 0; + + li { + padding-top: 2px; + margin: 0 5px; + } + + li:first-child { + padding-top: 6px; + } + + li:last-child { + padding-bottom: 6px; + } + } +} + +.ci-status-text { + max-width: 110px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: bottom; + display: inline-block; + position: relative; + font-weight: 100; +} + + +.dropdown-menu-toggle { + background-color: transparent; + border: none; + padding: 0; + color: $gl-text-color-light; + flex-grow: 1; + + + &:focus { + outline: none; + } + + &:hover { + color: $gl-text-color; + + .dropdown-counter-badge { color: $gl-text-color; - - .dropdown-counter-badge { - color: $gl-text-color; - } - } - } - - .dropdown-counter-badge { - float: right; - clear: right; - color: $border-color; - font-weight: 100; - font-size: 15px; - margin-right: 2px; - } - - .grouped-pipeline-dropdown { - padding: 0; - width: 191px; - left: auto; - right: -206px; - top: -11px; - box-shadow: 0 1px 5px $black-transparent; - - a { - display: inline-block; - - &:hover { - background-color: $stage-hover-bg; - } - } - - ul { - max-height: 245px; - overflow: auto; - margin: 5px 0; - - li { - margin: 0 5px; - padding-left: 0; - padding-bottom: 0; - margin-bottom: 0; - line-height: 1.2; - } - } - - .dropdown-build { - color: $gl-text-color-light; - - a.ci-action-icon-container { - padding: 0; - font-size: 11px; - float: right; - margin-top: 4px; - display: inline-block; - position: relative; - - i { - font-size: 11px; - margin-top: 0; - } - } - - &:hover { - background-color: $stage-hover-bg; - border-radius: 3px; - color: $gl-text-color; - } - - .ci-action-icon-container { - i { - width: 25px; - height: 25px; - - &::before { - top: 1px; - left: 1px; - } - } - } - - .stage { - max-width: 100px; - width: 100px; - } - - .ci-status-icon svg { - height: 18px; - width: 18px; - } - - .ci-status-text { - max-width: 95px; - padding-bottom: 3px; - position: relative; - top: 3px; - } } } } @@ -735,28 +703,94 @@ } } -/** - * Builds dropdown in mini pipeline - */ -.builds-dropdown { - border: none; - background: transparent; - padding: 0; - font-size: inherit; - border-radius: 0; - cursor: pointer; +.dropdown-build { + color: $gl-text-color-light; - .dropdown-caret { - display: none; - position: absolute; - top: 3px; - right: 6px; - font-size: 10px; + a.ci-action-icon-container { + padding: 0; + font-size: 11px; + float: right; + margin-top: 5px; + + i { + font-size: 11px; + margin-top: 0; + } } &:hover { - .dropdown-caret { - display: block; + background-color: $stage-hover-bg; + border-radius: 3px; + color: $gl-text-color; + } + + .ci-action-icon-container { + i { + width: 25px; + height: 25px; + + &:before{ + top: 1px; + left: 1px; + } + } + } + + .stage { + max-width: 100px; + width: 100px; + } + + .ci-status-icon svg { + height: 18px; + width: 18px; + } + + .ci-status-text { + max-width: 95px; + } +} + +/** + * Builds dropdown in mini pipeline + */ +.mini-pipeline-graph { + .builds-dropdown { + background-color: transparent; + border: none; + padding: 0; + color: #8c8c8c; + flex-grow: 1; + } + + .grouped-pipeline-dropdown { + right: -172px; + top: 23px; + } + + .arrow-up { + &::before, + &::after { + content: ''; + display: inline-block; + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + top: -6px; + left: 2px; + border-width: 0 5px 6px 5px; + } + + &::before { + border-width: 0 5px 5px 5px; + border-bottom-color: $border-color; + } + + &::after { + margin-top: 1px; + border-bottom-color: $white-light; } } } diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index e82faf4f6d3..9ecdb6269b9 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -49,17 +49,18 @@ - hasMultipleBuilds = stage.statuses.count > 1 - tooltip = "#{stage.name.titleize}: #{stage.status || 'not found'}" - .stage-container + .stage-container.mini-pipeline-graph - if hasMultipleBuilds .dropdown.inline.build-content - %button.dropdown-menu-toggle.has-tooltip{id: "dropdown-#{stage.name}", title: tooltip, class: klass, "data-toggle"=> "dropdown", "aria-haspopup"=> "true", "aria-expanded" => "false"} - = custom_icon(detailed_status.icon) - = icon('caret-down', class: 'dropdown-caret') - .dropdown-menu.grouped-pipeline-dropdown{"aria-labelledby"=> "dropdown-#{stage.name}"} - .arrow + %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } + %span{ class: klass } + = ci_icon_for_status(detailed_status.icon) + %span= icon('caret-down', class: 'dropdown-caret') + .dropdown-menu.grouped-pipeline-dropdown + .arrow-up %ul - stage.statuses.each do |status| - %li + %li.dropdown-build = render 'ci/status/graph_badge', subject: status - else - if details_path From 222eae7b7b17da923b0291c70ec2b6a4101186e6 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 18:41:21 +0000 Subject: [PATCH 139/386] Dropdown li style --- app/assets/stylesheets/pages/pipelines.scss | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index df88e7b5925..b1575d5213d 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -192,10 +192,6 @@ border-bottom: 2px solid $border-color; } } - - a { - display: block; - } } } @@ -759,8 +755,7 @@ background-color: transparent; border: none; padding: 0; - color: #8c8c8c; - flex-grow: 1; + color: $gl-text-color-light; } .grouped-pipeline-dropdown { @@ -768,6 +763,10 @@ top: 23px; } + .grouped-pipeline-dropdown a { + color: $gl-text-color-light; + } + .arrow-up { &::before, &::after { From 9431fcb31c22d4d9bde7366d930264cc1f5e03a6 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 14 Dec 2016 23:47:04 +0000 Subject: [PATCH 140/386] style dropdown --- app/assets/stylesheets/pages/pipelines.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index b1575d5213d..e0431e26769 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -756,6 +756,10 @@ border: none; padding: 0; color: $gl-text-color-light; + border: none; + background: transparent; + padding: 0; + margin: 0; } .grouped-pipeline-dropdown { From 5ce5e7a0e28062dd6b4a4195f9befe5017dd75ff Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 00:17:40 +0000 Subject: [PATCH 141/386] Removes duplicate declaration --- app/assets/stylesheets/pages/pipelines.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index e0431e26769..f98bf73756f 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -757,9 +757,7 @@ padding: 0; color: $gl-text-color-light; border: none; - background: transparent; - padding: 0; - margin: 0; + margin: 0; } .grouped-pipeline-dropdown { From 7bfe87242d9d897cdc68eb0df90345f2ec497c36 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 11:58:25 +0000 Subject: [PATCH 142/386] Add new borderless icons --- app/views/shared/icons/_icon_status_canceled_borderless.svg | 1 + app/views/shared/icons/_icon_status_created_borderless.svg | 1 + app/views/shared/icons/_icon_status_failed_borderless.svg | 1 + app/views/shared/icons/_icon_status_manual_borderless.svg | 1 + app/views/shared/icons/_icon_status_pending_borderless.svg | 1 + app/views/shared/icons/_icon_status_running_borderless.svg | 1 + app/views/shared/icons/_icon_status_skipped_borderless.svg | 1 + app/views/shared/icons/_icon_status_success_borderless.svg | 1 + app/views/shared/icons/_icon_status_warning_borderless.svg | 1 + 9 files changed, 9 insertions(+) create mode 100644 app/views/shared/icons/_icon_status_canceled_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_created_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_failed_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_manual_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_pending_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_running_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_skipped_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_success_borderless.svg create mode 100644 app/views/shared/icons/_icon_status_warning_borderless.svg diff --git a/app/views/shared/icons/_icon_status_canceled_borderless.svg b/app/views/shared/icons/_icon_status_canceled_borderless.svg new file mode 100644 index 00000000000..bf7fb29185f --- /dev/null +++ b/app/views/shared/icons/_icon_status_canceled_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_created_borderless.svg b/app/views/shared/icons/_icon_status_created_borderless.svg new file mode 100644 index 00000000000..1810d023be8 --- /dev/null +++ b/app/views/shared/icons/_icon_status_created_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_failed_borderless.svg b/app/views/shared/icons/_icon_status_failed_borderless.svg new file mode 100644 index 00000000000..b7022350c74 --- /dev/null +++ b/app/views/shared/icons/_icon_status_failed_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_manual_borderless.svg b/app/views/shared/icons/_icon_status_manual_borderless.svg new file mode 100644 index 00000000000..5eec665688b --- /dev/null +++ b/app/views/shared/icons/_icon_status_manual_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_pending_borderless.svg b/app/views/shared/icons/_icon_status_pending_borderless.svg new file mode 100644 index 00000000000..8d66e9e6c9c --- /dev/null +++ b/app/views/shared/icons/_icon_status_pending_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_running_borderless.svg b/app/views/shared/icons/_icon_status_running_borderless.svg new file mode 100644 index 00000000000..2757a168ed5 --- /dev/null +++ b/app/views/shared/icons/_icon_status_running_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_skipped_borderless.svg b/app/views/shared/icons/_icon_status_skipped_borderless.svg new file mode 100644 index 00000000000..fb3e930b3cb --- /dev/null +++ b/app/views/shared/icons/_icon_status_skipped_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_success_borderless.svg b/app/views/shared/icons/_icon_status_success_borderless.svg new file mode 100644 index 00000000000..8ee5be7ab78 --- /dev/null +++ b/app/views/shared/icons/_icon_status_success_borderless.svg @@ -0,0 +1 @@ + diff --git a/app/views/shared/icons/_icon_status_warning_borderless.svg b/app/views/shared/icons/_icon_status_warning_borderless.svg new file mode 100644 index 00000000000..7b061624521 --- /dev/null +++ b/app/views/shared/icons/_icon_status_warning_borderless.svg @@ -0,0 +1 @@ + From 83ebc97ba76abc8375c147565603a14a18223649 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 12:11:14 +0000 Subject: [PATCH 143/386] Adds new partial for borderless mini graph icons --- app/views/ci/status/_mini_graph_badge.html.haml | 6 ++++++ app/views/projects/ci/pipelines/_pipeline.html.haml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 app/views/ci/status/_mini_graph_badge.html.haml diff --git a/app/views/ci/status/_mini_graph_badge.html.haml b/app/views/ci/status/_mini_graph_badge.html.haml new file mode 100644 index 00000000000..6031339a802 --- /dev/null +++ b/app/views/ci/status/_mini_graph_badge.html.haml @@ -0,0 +1,6 @@ +- status = subject.detailed_status(current_user) +- icon = "#{status.icon}_borderless" +- klass = "ci-status-icon ci-status-icon-#{status}" + +%span.mini-pipeline-graph-icon-container + %span{ class: klass }= custom_icon(icon) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 9ecdb6269b9..d664d925df9 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -54,7 +54,7 @@ .dropdown.inline.build-content %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } %span{ class: klass } - = ci_icon_for_status(detailed_status.icon) + = render 'ci/status/mini_graph_badge', subject: stage %span= icon('caret-down', class: 'dropdown-caret') .dropdown-menu.grouped-pipeline-dropdown .arrow-up @@ -65,7 +65,7 @@ - else - if details_path = link_to details_path, class: klass, title: tooltip do - = custom_icon(detailed_status.icon) + = render 'ci/status/mini_graph_badge', subject: stage %td - if pipeline.duration From 6fde0e094d5e2fb3edea3228ef8f7f193fc4f067 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 13:22:39 +0000 Subject: [PATCH 144/386] CSS: dropdown on hover --- app/assets/stylesheets/pages/pipelines.scss | 52 +++++++++++++++++++ .../ci/status/_mini_graph_badge.html.haml | 3 +- .../projects/ci/pipelines/_pipeline.html.haml | 8 +-- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index f98bf73756f..105c3fc3e7c 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -22,6 +22,7 @@ .table.ci-table { min-width: 1200px; + table-layout: fixed; .pipeline-id { color: $black; @@ -795,3 +796,54 @@ } } } + +.mini-pipeline-graph-icon-container .ci-status-icon { + display: inline-block; + border: 1px solid; + border-radius: 20px; + margin-right: 1px; + width: 20px; + height: 20px; + position: relative; + + svg { + top: -1px; + position: relative; + } +} + +.builds-dropdown { + &:focus { + margin-right: -6px; + + .ci-status-icon { + width: 27px; + padding: 0px 6px 0px 0px; + + + .dropdown-caret { + display: inline-block; + } + } + } + + .mini-pipeline-graph-icon-container { + .ci-status-icon:hover, + .ci-status-icon:focus { + width: 27px; + padding: 0px 6px 0px 0px; + + + .dropdown-caret { + display: inline-block; + } + } + + .dropdown-caret { + font-size: 12px; + position: relative; + top: 3px; + left: -11px; + margin-right: -6px; + display: none; + } + } +} diff --git a/app/views/ci/status/_mini_graph_badge.html.haml b/app/views/ci/status/_mini_graph_badge.html.haml index 6031339a802..34e07e75ae8 100644 --- a/app/views/ci/status/_mini_graph_badge.html.haml +++ b/app/views/ci/status/_mini_graph_badge.html.haml @@ -2,5 +2,4 @@ - icon = "#{status.icon}_borderless" - klass = "ci-status-icon ci-status-icon-#{status}" -%span.mini-pipeline-graph-icon-container - %span{ class: klass }= custom_icon(icon) +%span{ class: klass }= custom_icon(icon) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index d664d925df9..0651447f616 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -54,8 +54,9 @@ .dropdown.inline.build-content %button.has-tooltip.builds-dropdown{ type: 'button', data: { toggle: 'dropdown', title: tooltip} } %span{ class: klass } - = render 'ci/status/mini_graph_badge', subject: stage - %span= icon('caret-down', class: 'dropdown-caret') + %span.mini-pipeline-graph-icon-container + = render 'ci/status/mini_graph_badge', subject: stage + = icon('caret-down', class: 'dropdown-caret') .dropdown-menu.grouped-pipeline-dropdown .arrow-up %ul @@ -65,7 +66,8 @@ - else - if details_path = link_to details_path, class: klass, title: tooltip do - = render 'ci/status/mini_graph_badge', subject: stage + %span.mini-pipeline-graph-icon-container + = render 'ci/status/mini_graph_badge', subject: stage %td - if pipeline.duration From e36b00e7137a7fd024ba4639ac6cb077edd2618a Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 14:46:28 +0000 Subject: [PATCH 145/386] CSS - Adds background color. Removes unused CSS Fix scss linter errors Adds changelog entry Increase icons size. Adds transition animation Fix jumping icon. Reduce icon size Fix columns width Changes after review Fix transition --- app/assets/stylesheets/framework/icons.scss | 24 ---- app/assets/stylesheets/pages/pipelines.scss | 116 ++++++++++++------ .../projects/commit/_pipelines_list.haml | 14 +-- app/views/projects/pipelines/index.html.haml | 12 +- .../19703-direct-link-pipelines.yml | 4 + 5 files changed, 93 insertions(+), 77 deletions(-) create mode 100644 changelogs/unreleased/19703-direct-link-pipelines.yml diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 88b391ef2df..226bd2ead31 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -4,10 +4,6 @@ svg { fill: $gl-success; } - - .builds-dropdown-caret { - color: $gl-success; - } } .ci-status-icon-failed { @@ -16,10 +12,6 @@ svg { fill: $gl-danger; } - - .builds-dropdown-caret { - color: $gl-danger; - } } .ci-status-icon-pending, @@ -29,10 +21,6 @@ svg { fill: $gl-warning; } - - .builds-dropdown-caret { - color: $gl-warning; - } } .ci-status-icon-running { @@ -41,10 +29,6 @@ svg { fill: $blue-normal; } - - .builds-dropdown-caret { - color: $blue-normal; - } } .ci-status-icon-canceled, @@ -55,10 +39,6 @@ svg { fill: $gl-gray; } - - .builds-dropdown-caret { - color: $gl-gray; - } } .ci-status-icon-created, @@ -68,8 +48,4 @@ svg { fill: $gray-darkest; } - - .builds-dropdown-caret { - color: $gray-darkest; - } } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 105c3fc3e7c..33d3a800e7c 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -28,12 +28,16 @@ color: $black; } - .branch-commit { - width: 30%; + .pipeline-date, + .pipeline-status { + width: 10%; + } - .branch-name { - max-width: 195px; - } + .pipeline-info, + .pipeline-commit, + .pipeline-actions, + .pipeline-stages { + width: 20%; } } } @@ -107,7 +111,7 @@ .branch-name { font-weight: bold; - max-width: 150px; + max-width: 120px; overflow: hidden; display: inline-block; white-space: nowrap; @@ -133,7 +137,7 @@ .commit-title { margin-top: 4px; - max-width: 300px; + max-width: 225px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -580,15 +584,6 @@ } } -<<<<<<< HEAD - .dropdown-menu-toggle { - background-color: transparent; - border: none; - padding: 0; - color: $gl-text-color-light; - white-space: normal; - overflow: visible; -======= .dropdown-counter-badge { float: right; color: $border-color; @@ -596,7 +591,6 @@ font-size: 15px; margin-right: 2px; } ->>>>>>> 5ba6f0d... Dropdown with arrow .grouped-pipeline-dropdown { padding: 0; @@ -707,7 +701,9 @@ padding: 0; font-size: 11px; float: right; - margin-top: 5px; + margin-top: 4px; + display: inline-block; + position: relative; i { font-size: 11px; @@ -726,7 +722,7 @@ width: 25px; height: 25px; - &:before{ + &::before { top: 1px; left: 1px; } @@ -782,11 +778,11 @@ border-style: solid; top: -6px; left: 2px; - border-width: 0 5px 6px 5px; + border-width: 0 5px 6px; } &::before { - border-width: 0 5px 5px 5px; + border-width: 0 5px 5px; border-bottom-color: $border-color; } @@ -797,53 +793,93 @@ } } +/** + * Icons in mini pipeline graph + */ .mini-pipeline-graph-icon-container .ci-status-icon { - display: inline-block; - border: 1px solid; - border-radius: 20px; - margin-right: 1px; - width: 20px; - height: 20px; - position: relative; + display: inline-block; + border: 1px solid; + border-radius: 20px; + margin-right: 1px; + width: 20px; + height: 20px; + position: relative; + z-index: 2; + transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); - svg { - top: -1px; - position: relative; - } + svg { + top: -1px; + } } .builds-dropdown { &:focus { - margin-right: -6px; + outline: none; + margin-right: -8px; .ci-status-icon { - width: 27px; - padding: 0px 6px 0px 0px; + width: 28px; + padding: 0 8px 0 0; + transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); + .dropdown-caret { - display: inline-block; + display: inline-block; } } } + &:focus, + &:active { + .ci-status-icon-success { + background-color: rgba($gl-success, .1); + } + + .ci-status-icon-failed { + background-color: rgba($gl-danger, .1); + } + + .ci-status-icon-pending, + .ci-status-icon-success_with_warnings { + background-color: rgba($gl-warning, .1); + } + + .ci-status-icon-running { + background-color: rgba($blue-normal, .1); + } + + .ci-status-icon-canceled, + .ci-status-icon-disabled, + .ci-status-icon-not-found { + background-color: rgba($gl-gray, .1); + } + + .ci-status-icon-created, + .ci-status-icon-skipped { + background-color: rgba($gray-darkest, .1); + } + + } + .mini-pipeline-graph-icon-container { .ci-status-icon:hover, .ci-status-icon:focus { - width: 27px; - padding: 0px 6px 0px 0px; + width: 28px; + padding: 0 8px 0 0; + transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); + .dropdown-caret { - display: inline-block; + display: inline-block; } } .dropdown-caret { - font-size: 12px; + font-size: 11px; position: relative; top: 3px; left: -11px; margin-right: -6px; display: none; + z-index: 2; } } } diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index 7f42fde0fea..0c2f45c6035 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -5,11 +5,11 @@ - else .table-holder %table.table.ci-table - %tbody - %th Status - %th Pipeline - %th Commit - %th Stages - %th - %th + %thead + %th.pipeline-status Status + %th.pipeline-info Pipeline + %th.pipeline-commit Commit + %th.pipeline-stages Stages + %th.pipeline-date + %th.pipeline-actions = render pipelines, commit_sha: true, stage: true, allow_retry: true, show_commit: false diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 030cd8ef78f..4d009871f0d 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -44,12 +44,12 @@ .table-holder %table.table.ci-table %thead - %th Status - %th Pipeline - %th Commit - %th Stages - %th - %th.hidden-xs + %th.pipeline-status Status + %th.pipeline-info Pipeline + %th.pipeline-commit Commit + %th.pipeline-stages Stages + %th.pipeline-date + %th.pipeline-actions.hidden-xs = render @pipelines, commit_sha: true, stage: true, allow_retry: true = paginate @pipelines, theme: 'gitlab' diff --git a/changelogs/unreleased/19703-direct-link-pipelines.yml b/changelogs/unreleased/19703-direct-link-pipelines.yml new file mode 100644 index 00000000000..d846ad41e0f --- /dev/null +++ b/changelogs/unreleased/19703-direct-link-pipelines.yml @@ -0,0 +1,4 @@ +--- +title: Adds Direct link from pipeline list to builds +merge_request: 8097 +author: From e42de89a15c858866d78a4d2a5837a0feec922a5 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 15 Dec 2016 17:30:49 +0000 Subject: [PATCH 146/386] Changes after review Changes after review Fix tooltip title Remove unneeded string interpolation --- app/assets/stylesheets/pages/pipelines.scss | 10 +- app/views/ci/status/_graph_badge.html.haml | 4 +- .../ci/builds/_build_pipeline.html.haml | 13 -- .../_generic_commit_status_pipeline.html.haml | 10 -- .../projects/pipelines/pipeline_spec.rb | 121 +++++++++++------- 5 files changed, 76 insertions(+), 82 deletions(-) delete mode 100644 app/views/projects/ci/builds/_build_pipeline.html.haml delete mode 100644 app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index d3f39570f11..be22e7bdc79 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -645,14 +645,6 @@ margin-bottom: 0; line-height: 1.2; } - - li:first-child { - padding-top: 6px; - } - - li:last-child { - padding-bottom: 6px; - } } .dropdown-build { @@ -741,4 +733,4 @@ .ci-play-icon { padding: 5px 5px 5px 7px; } -} \ No newline at end of file +} diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml index a7e8544e7d4..c7d04ab61e9 100644 --- a/app/views/ci/status/_graph_badge.html.haml +++ b/app/views/ci/status/_graph_badge.html.haml @@ -5,7 +5,7 @@ - klass = "ci-status-icon ci-status-icon-#{status}" - if status.has_details? - = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status}" } do + = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status.label}" } do %span{ class: klass }= custom_icon(status.icon) .ci-status-text= subject.name - else @@ -14,6 +14,6 @@ - if status.has_action? = link_to status.action_path, method: status.action_method, - title: "#{subject.name}: #{status.action_title}", class: 'ci-action-icon-container' do + title: status.action_title, class: 'ci-action-icon-container' do %i.ci-action-icon-wrapper = icon(status.action_icon, class: status.action_class) diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml deleted file mode 100644 index ad1a7360a8b..00000000000 --- a/app/views/projects/ci/builds/_build_pipeline.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -- is_playable = subject.playable? && can?(current_user, :update_build, @project) -- if is_playable - = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.js-pipeline-graph', placement: 'bottom' } do - = ci_icon_for_status('play') - .ci-status-text= subject.name -- elsif can?(current_user, :read_build, @project) - = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.js-pipeline-graph', placement: 'bottom' } do - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - .ci-status-text= subject.name -- else - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml deleted file mode 100644 index 1bba0443154..00000000000 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%a{ data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.js-pipeline-graph', placement: 'bottom' } } - - if subject.target_url - = link_to subject.target_url do - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - %span.ci-status-text= subject.name - - else - %span{class: "ci-status-icon ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - %span.ci-status-text= subject.name diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 80a596d34c9..0a77eaa123c 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -38,63 +38,88 @@ describe "Pipelines", feature: true, js: true do expect(page).to have_css('#js-tab-pipeline.active') end - context 'pipeline graph' do - it 'shows a running icon and a cancel action for the running build' do - title = "#{@running.name} - #{@running.status}" + describe 'pipeline graph' do + context 'when pipeline has running builds' do + it 'shows a running icon and a cancel action for the running build' do + page.within('a[data-title="deploy - running"]') do + expect(page).to have_selector('.ci-status-icon-running') + expect(page).to have_content('deploy') + end - page.within("a[data-title='#{title}']") do - expect(page).to have_selector('.ci-status-icon-running') - expect(page).to have_content('deploy') + page.within('a[data-title="deploy - running"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-ban') + end end - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-ban') - end + it 'should be possible to cancel the running build' do + find('a[data-title="deploy - running"] + .ci-action-icon-container').trigger('click') + expect(page).not_to have_content('Cancel running') + end end - it 'shows the success icon and a retry action for the successfull build' do - title = "#{@success.name} - #{@success.status}" + context 'when pipeline has successful builds' do + it 'shows the success icon and a retry action for the successfull build' do + page.within('a[data-title="build - passed"]') do + expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_content('build') + end - page.within("a[data-title='#{title}']") do + page.within('a[data-title="build - passed"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + end + end + + it 'should be possible to retry the success build' do + find('a[data-title="build - passed"] + .ci-action-icon-container').trigger('click') + + expect(page).not_to have_content('Retry build') + end + end + + context 'when pipeline has failed builds' do + it 'shows the failed icon and a retry action for the failed build' do + page.within('a[data-title="test - failed"]') do + expect(page).to have_selector('.ci-status-icon-failed') + expect(page).to have_content('test') + end + + page.within('a[data-title="test - failed"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + end + end + + it 'should be possible to retry the failed build' do + find('a[data-title="test - failed"] + .ci-action-icon-container').trigger('click') + + expect(page).not_to have_content('Retry build') + end + end + + context 'when pipeline has manual builds' do + it 'shows the skipped icon and a play action for the manual build' do + page.within('a[data-title="manual build - manual play action"]') do + expect(page).to have_selector('.ci-status-icon-skipped') + expect(page).to have_content('manual') + end + + page.within('a[data-title="manual build - manual play action"] + .ci-action-icon-container') do + expect(page).to have_selector('.ci-action-icon-container .fa-play') + end + end + + it 'should be possible to play the manual build' do + find('a[data-title="manual build - manual play action"] + .ci-action-icon-container').trigger('click') + + expect(page).not_to have_content('Play build') + end + end + + context 'when pipeline has external build' do + it 'shows the success icon and the generic comit status build' do expect(page).to have_selector('.ci-status-icon-success') - expect(page).to have_content('build') + expect(page).to have_content('jenkins') end - - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-refresh') - end - end - - it 'shows the failed icon and a retry action for the failed build' do - title = "#{@failed.name} - #{@failed.status}" - - page.within("a[data-title='#{title}']") do - expect(page).to have_selector('.ci-status-icon-failed') - expect(page).to have_content('test') - end - - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-refresh') - end - end - - it 'shows the skipped icon and a play action for the manual build' do - title = "#{@manual.name} - #{@manual.status}" - - page.within("a[data-title='#{title}']") do - expect(page).to have_selector('.ci-status-icon-skipped') - expect(page).to have_content('manual') - end - - page.within("a[data-title='#{title}'] + .ci-action-icon-container") do - expect(page).to have_selector('.ci-action-icon-container .fa-play') - end - end - - it 'shows the success icon and the generic comit status build' do - expect(page).to have_selector('.ci-status-icon-success') - expect(page).to have_content('jenkins') end end From 7b664858ff33832cf62fe56322be0cfe04cbbc7d Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 21 Sep 2016 19:55:43 +0800 Subject: [PATCH 147/386] Add docs for pipeline duration --- doc/ci/pipelines.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 03b9c4bb444..f91b9d350f7 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -36,6 +36,37 @@ Clicking on a pipeline will show the builds that were run for that pipeline. Clicking on an individual build will show you its build trace, and allow you to cancel the build, retry it, or erase the build trace. +## How the pipeline duration is calculated + +Total running time for a given pipeline would exclude retries and pending +(queue) time. We could reduce this problem down to finding the union of +periods. + +So each job would be represented as a `Period`, which consists of +`Period#first` as when the job started and `Period#last` as when the +job was finished. A simple example here would be: + +* A (1, 3) +* B (2, 4) +* C (6, 7) + +Here A begins from 1, and ends to 3. B begins from 2, and ends to 4. +C begins from 6, and ends to 7. Visually it could be viewed as: + +``` +0 1 2 3 4 5 6 7 + AAAAAAA + BBBBBBB + CCCC +``` + +The union of A, B, and C would be (1, 4) and (6, 7), therefore the +total running time should be: + +``` +(4 - 1) + (7 - 6) => 4 +``` + ## Badges Build status and test coverage report badges are available. You can find their From 9a2eaecc8b720367a7d079019b22f40627a871b1 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 14 Dec 2016 13:30:08 +0000 Subject: [PATCH 148/386] Correct slack slash commands pretty path --- .../services/mattermost_slash_commands/_help.html.haml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/projects/services/mattermost_slash_commands/_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_help.html.haml index a676c0290a0..01a77a952d1 100644 --- a/app/views/projects/services/mattermost_slash_commands/_help.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_help.html.haml @@ -1,5 +1,4 @@ -- pretty_path_with_namespace = "#{@project ? @project.namespace.name : 'namespace'} / #{@project ? @project.name : 'name'}" -- run_actions_text = "Perform common operations on this project: #{pretty_path_with_namespace}" +- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}" .well This service allows GitLab users to perform common operations on this @@ -27,7 +26,7 @@ .form-group = label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label' .col-sm-10.col-xs-12.input-group - = text_field_tag :display_name, "GitLab / #{pretty_path_with_namespace}", class: 'form-control input-sm', readonly: 'readonly' + = text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly' .input-group-btn = clipboard_button(clipboard_target: '#display_name') From 5920c5d2caaf37899a90c4ae5989b0fc8935e847 Mon Sep 17 00:00:00 2001 From: Munken Date: Fri, 16 Dec 2016 01:44:41 +0000 Subject: [PATCH 149/386] Added KaTeX license and procedure to build it for Gitlab --- vendor/assets/javascripts/katex.js | 41 +++++++++++++++++++++++++++++ vendor/assets/stylesheets/katex.css | 41 +++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/vendor/assets/javascripts/katex.js b/vendor/assets/javascripts/katex.js index 9596b839832..2ce3a5e8d4d 100644 --- a/vendor/assets/javascripts/katex.js +++ b/vendor/assets/javascripts/katex.js @@ -1,3 +1,44 @@ +/* + The MIT License (MIT) + + Copyright (c) 2015 Khan Academy + + This software also uses portions of the underscore.js project, which is + MIT licensed with the following copyright: + + Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative + Reporters & Editors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +/* + Here is how to build a version of KaTeX that works with gitlab. + + The problem is that the procedure for changing font location doesn't work for ''. + + 1. Clone KaTeX. Anything later than 4fb9445a9 (is merged into master) will do. + 2. make (requires node) + 3. sed -i 's,fonts/,,' build/katex.css + 4. Copy build/katex.js, build/katex.css and fonts/* to gitlab. +*/ + (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.katex = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Date: Thu, 15 Dec 2016 11:32:06 -0200 Subject: [PATCH 150/386] Don't use the Route model in migrations --- db/migrate/20161130095245_fill_routes_table.rb | 2 +- db/migrate/20161130101252_fill_projects_routes_table.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20161130095245_fill_routes_table.rb b/db/migrate/20161130095245_fill_routes_table.rb index 6754e583000..c3536d6d911 100644 --- a/db/migrate/20161130095245_fill_routes_table.rb +++ b/db/migrate/20161130095245_fill_routes_table.rb @@ -16,6 +16,6 @@ class FillRoutesTable < ActiveRecord::Migration end def down - Route.delete_all(source_type: 'Namespace') + execute("DELETE FROM routes WHERE source_type = 'Namespace'") end end diff --git a/db/migrate/20161130101252_fill_projects_routes_table.rb b/db/migrate/20161130101252_fill_projects_routes_table.rb index 14700583be5..4f3fe7b03a9 100644 --- a/db/migrate/20161130101252_fill_projects_routes_table.rb +++ b/db/migrate/20161130101252_fill_projects_routes_table.rb @@ -17,6 +17,6 @@ class FillProjectsRoutesTable < ActiveRecord::Migration end def down - Route.delete_all(source_type: 'Project') + execute("DELETE FROM routes WHERE source_type = 'Project'") end end From d494c9a789799f3625c25273c3bbeb43a93a3b88 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 15 Dec 2016 11:42:49 -0200 Subject: [PATCH 151/386] Use optimized query to fill the routes table when running PostgreSQL --- ...161130101252_fill_projects_routes_table.rb | 20 +++++++++++++------ ...202152031_remove_duplicates_from_routes.rb | 5 +++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/db/migrate/20161130101252_fill_projects_routes_table.rb b/db/migrate/20161130101252_fill_projects_routes_table.rb index 4f3fe7b03a9..56ba6fcdbe3 100644 --- a/db/migrate/20161130101252_fill_projects_routes_table.rb +++ b/db/migrate/20161130101252_fill_projects_routes_table.rb @@ -8,12 +8,20 @@ class FillProjectsRoutesTable < ActiveRecord::Migration DOWNTIME_REASON = 'No new projects should be created during data copy' def up - execute <<-EOF - INSERT INTO routes - (source_id, source_type, path) - (SELECT projects.id, 'Project', concat(namespaces.path, '/', projects.path) FROM projects - INNER JOIN namespaces ON projects.namespace_id = namespaces.id) - EOF + if Gitlab::Database.postgresql? + execute <<-EOF + INSERT INTO routes (source_id, source_type, path) + (SELECT DISTINCT ON (namespaces.path, projects.path) projects.id, 'Project', concat(namespaces.path, '/', projects.path) + FROM projects INNER JOIN namespaces ON projects.namespace_id = namespaces.id + ORDER BY namespaces.path, projects.path, projects.id DESC) + EOF + else + execute <<-EOF + INSERT INTO routes (source_id, source_type, path) + (SELECT projects.id, 'Project', concat(namespaces.path, '/', projects.path) + FROM projects INNER JOIN namespaces ON projects.namespace_id = namespaces.id) + EOF + end end def down diff --git a/db/migrate/20161202152031_remove_duplicates_from_routes.rb b/db/migrate/20161202152031_remove_duplicates_from_routes.rb index 510796e05f2..a21bde69995 100644 --- a/db/migrate/20161202152031_remove_duplicates_from_routes.rb +++ b/db/migrate/20161202152031_remove_duplicates_from_routes.rb @@ -7,6 +7,11 @@ class RemoveDuplicatesFromRoutes < ActiveRecord::Migration DOWNTIME = false def up + # We can skip this migration when running a PostgreSQL database because + # we use an optimized query in the "FillProjectsRoutesTable" migration + # to fill these values that avoid duplicate entries in the routes table. + return unless Gitlab::Database.mysql? + select_all("SELECT path FROM #{quote_table_name(:routes)} GROUP BY path HAVING COUNT(*) > 1").each do |row| path = connection.quote(row['path']) execute(%Q{ From fd0d8a28327e2f2a35fe84e057dbc29cc18fc4cc Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 15 Dec 2016 11:46:15 -0200 Subject: [PATCH 152/386] Fix the AddNameIndexToNamespace migration to be reversible --- db/migrate/20161206153754_add_name_index_to_namespace.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20161206153754_add_name_index_to_namespace.rb b/db/migrate/20161206153754_add_name_index_to_namespace.rb index aaa35ed6f0a..b3f3cb68a99 100644 --- a/db/migrate/20161206153754_add_name_index_to_namespace.rb +++ b/db/migrate/20161206153754_add_name_index_to_namespace.rb @@ -13,7 +13,7 @@ class AddNameIndexToNamespace < ActiveRecord::Migration end def down - if index_exists?(:namespaces, :name) + if index_exists?(:namespaces, [:name, :parent_id]) remove_index :namespaces, [:name, :parent_id] end end From ad4ec44673254b4bb91c6ddb391ff9553c9473a5 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 15 Dec 2016 12:56:29 -0200 Subject: [PATCH 153/386] Improve performance on RemoveDuplicatesFromRoutes migration --- ...202152031_remove_duplicates_from_routes.rb | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/db/migrate/20161202152031_remove_duplicates_from_routes.rb b/db/migrate/20161202152031_remove_duplicates_from_routes.rb index a21bde69995..d73b0847506 100644 --- a/db/migrate/20161202152031_remove_duplicates_from_routes.rb +++ b/db/migrate/20161202152031_remove_duplicates_from_routes.rb @@ -12,20 +12,16 @@ class RemoveDuplicatesFromRoutes < ActiveRecord::Migration # to fill these values that avoid duplicate entries in the routes table. return unless Gitlab::Database.mysql? - select_all("SELECT path FROM #{quote_table_name(:routes)} GROUP BY path HAVING COUNT(*) > 1").each do |row| - path = connection.quote(row['path']) - execute(%Q{ - DELETE FROM #{quote_table_name(:routes)} - WHERE path = #{path} - AND id != ( - SELECT id FROM ( - SELECT max(id) AS id - FROM #{quote_table_name(:routes)} - WHERE path = #{path} - ) max_ids - ) - }) - end + execute <<-EOF + DELETE duplicated_rows.* + FROM routes AS duplicated_rows + INNER JOIN ( + SELECT path, MAX(id) as max_id + FROM routes + GROUP BY path + HAVING COUNT(*) > 1 + ) AS good_rows ON good_rows.path = duplicated_rows.path AND good_rows.max_id <> duplicated_rows.id; + EOF end def down From ffa35233573acd31725677547555598fc36072e0 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 16 Dec 2016 10:38:23 +0200 Subject: [PATCH 154/386] BB importer: Added note about linking BB users and GitLab users[ci skip] --- doc/workflow/importing/import_projects_from_bitbucket.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md index 520c4216295..935d6288f3b 100644 --- a/doc/workflow/importing/import_projects_from_bitbucket.md +++ b/doc/workflow/importing/import_projects_from_bitbucket.md @@ -20,7 +20,8 @@ It takes just a few steps to import your existing Bitbucket projects to GitLab. ![Import projects](bitbucket_importer/bitbucket_import_select_project.png) -A new GitLab project will be created with your imported data. +A new GitLab project will be created with your imported data. Keep in mind that if you want to Bitbucket users +to be linked to GitLab user you have to have all of them in GitLab in advance. They will be matched by their BitBucket username. ### Note Milestones and wiki pages are not imported from Bitbucket. From e52e3ab5082599fd5a895de961b07584421a5cd2 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Wed, 23 Nov 2016 21:03:53 +1000 Subject: [PATCH 155/386] Remove whole description from #merge_commit_message and add add closed issues --- app/models/merge_request.rb | 3 ++- spec/models/merge_request_spec.rb | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b73d7acefea..62dd02936e2 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -615,7 +615,8 @@ class MergeRequest < ActiveRecord::Base def merge_commit_message message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n" message << "#{title}\n\n" - message << "#{description}\n\n" if description.present? + mr_closes_issues = closes_issues + message << "Closed Issues: #{mr_closes_issues.map { |issue| issue.to_reference(target_project) }.join(", ")}\n\n" if mr_closes_issues.present? message << "See merge request #{to_reference}" message diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1b71d00eb8f..e1f9d66714d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -410,11 +410,17 @@ describe MergeRequest, models: true do .to match("Remove all technical debt\n\n") end - it 'includes its description in the body' do - request = build(:merge_request, description: 'By removing all code') + it 'includes its closed issues in the body' do + issue = create(:issue, project: subject.project) - expect(request.merge_commit_message) - .to match("By removing all code\n\n") + subject.project.team << [subject.author, :developer] + subject.description = "Closes #{issue.to_reference}" + + allow(subject.project).to receive(:default_branch). + and_return(subject.target_branch) + + expect(subject.merge_commit_message) + .to match("Closed Issues: #{issue.to_reference}") end it 'includes its reference in the body' do From 1a8f43ff3e5481d65f547ac01c03e2d64e14fff0 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Thu, 24 Nov 2016 07:50:49 +1000 Subject: [PATCH 156/386] introduce MergeRequest#issues_mentioned_but_not_closing --- app/models/merge_request.rb | 16 +++++++++ spec/models/merge_request_spec.rb | 57 +++++++++++++++++++------------ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 62dd02936e2..6f660bf0e77 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -568,6 +568,22 @@ class MergeRequest < ActiveRecord::Base end end + def issues_mentioned_but_not_closing(current_user = self.author) + issues = [] + + if target_branch == project.default_branch + messages = [description] + messages.concat(commits.map(&:safe_message)) if merge_request_diff + + ext = Gitlab::ReferenceExtractor.new(project, current_user) + ext.analyze(messages.join("\n")) + + issues = ext.issues + end + + issues - closes_issues + end + def target_project_path if target_project target_project.path_with_namespace diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index e1f9d66714d..688c78fb404 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -252,7 +252,7 @@ describe MergeRequest, models: true do end end - describe 'detection of issues to be closed' do + describe 'detection of issues' do let(:issue0) { create :issue, project: subject.project } let(:issue1) { create :issue, project: subject.project } @@ -265,29 +265,44 @@ describe MergeRequest, models: true do allow(subject).to receive(:commits).and_return([commit0, commit1, commit2]) end - it 'accesses the set of issues that will be closed on acceptance' do + describe 'detection of issues to be closed' do + it 'accesses the set of issues that will be closed on acceptance' do + allow(subject.project).to receive(:default_branch). + and_return(subject.target_branch) + + closed = subject.closes_issues + + expect(closed).to include(issue0, issue1) + end + + it 'only lists issues as to be closed if it targets the default branch' do + allow(subject.project).to receive(:default_branch).and_return('master') + subject.target_branch = 'something-else' + + expect(subject.closes_issues).to be_empty + end + + it 'detects issues mentioned in the description' do + issue2 = create(:issue, project: subject.project) + + subject.description = "Closes #{issue2.to_reference}" + + allow(subject.project).to receive(:default_branch). + and_return(subject.target_branch) + + expect(subject.closes_issues).to include(issue2) + end + end + + it 'detects issues mentioned but not closed' do + mentioned_issue = create(:issue, project: subject.project) + + subject.description = "Is related to #{mentioned_issue.to_reference}" + allow(subject.project).to receive(:default_branch). and_return(subject.target_branch) - closed = subject.closes_issues - - expect(closed).to include(issue0, issue1) - end - - it 'only lists issues as to be closed if it targets the default branch' do - allow(subject.project).to receive(:default_branch).and_return('master') - subject.target_branch = 'something-else' - - expect(subject.closes_issues).to be_empty - end - - it 'detects issues mentioned in the description' do - issue2 = create(:issue, project: subject.project) - subject.description = "Closes #{issue2.to_reference}" - allow(subject.project).to receive(:default_branch). - and_return(subject.target_branch) - - expect(subject.closes_issues).to include(issue2) + expect(subject.issues_mentioned_but_not_closing).to match_array([mentioned_issue]) end end From 80915c35f48463c3a983129961f3130bd9703754 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Thu, 24 Nov 2016 19:21:27 +1000 Subject: [PATCH 157/386] diplays mentioned but not merged message on MR show page --- app/helpers/merge_requests_helper.rb | 11 +++++++++++ .../projects/merge_requests/widget/_open.html.haml | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index a6659ea2fd1..dcd35dc6282 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -59,6 +59,17 @@ module MergeRequestsHelper @mr_closes_issues ||= @merge_request.closes_issues end + def mr_issues_mentioned_but_not_closing + @mr_issues_mentioned_but_not_closing ||= @merge_request.issues_mentioned_but_not_closing + end + + def mr_issues_mentioned_but_not_closing_message + verb = mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is' + issue_text = 'issue'.pluralize(mr_issues_mentioned_but_not_closing.size) + + "The following #{issue_text} #{verb} mentioned but not being closed:" + end + def mr_change_branches_path(merge_request) new_namespace_project_merge_request_path( @project.namespace, @project, diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index eee711dc5af..1f794cad663 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -30,6 +30,15 @@ - elsif @merge_request.can_be_merged? || resolved_conflicts = render 'projects/merge_requests/widget/open/accept' + - if mr_issues_mentioned_but_not_closing.present? + .mr-widget-footer + %span + %i.fa.fa-warning + = mr_issues_mentioned_but_not_closing_message + = succeed '.' do + != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author + = mr_assign_issues_link + - if mr_closes_issues.present? .mr-widget-footer %span From c1515cd865ec11110f9b34c41a41747dcbc3b402 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Fri, 25 Nov 2016 06:46:16 +1000 Subject: [PATCH 158/386] better mentioned but not closing message and icon --- app/helpers/merge_requests_helper.rb | 7 ------- app/views/projects/merge_requests/widget/_open.html.haml | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index dcd35dc6282..20218775659 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -63,13 +63,6 @@ module MergeRequestsHelper @mr_issues_mentioned_but_not_closing ||= @merge_request.issues_mentioned_but_not_closing end - def mr_issues_mentioned_but_not_closing_message - verb = mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is' - issue_text = 'issue'.pluralize(mr_issues_mentioned_but_not_closing.size) - - "The following #{issue_text} #{verb} mentioned but not being closed:" - end - def mr_change_branches_path(merge_request) new_namespace_project_merge_request_path( @project.namespace, @project, diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 1f794cad663..ebea48a4321 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -33,8 +33,8 @@ - if mr_issues_mentioned_but_not_closing.present? .mr-widget-footer %span - %i.fa.fa-warning - = mr_issues_mentioned_but_not_closing_message + %i.fa.fa-info-circle + #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} mentioned but not being closed: = succeed '.' do != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author = mr_assign_issues_link From 0e76daf3da35920d10053dc4d8707e1b6aa7c913 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Fri, 25 Nov 2016 06:49:56 +1000 Subject: [PATCH 159/386] only look for issues mentioned on description on MergeRequest#issues_mentioned_but_not_closing --- app/models/merge_request.rb | 6 ++++-- spec/models/merge_request_spec.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 6f660bf0e77..b8c139d01e2 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -570,18 +570,20 @@ class MergeRequest < ActiveRecord::Base def issues_mentioned_but_not_closing(current_user = self.author) issues = [] + closing_issues = [] if target_branch == project.default_branch messages = [description] - messages.concat(commits.map(&:safe_message)) if merge_request_diff ext = Gitlab::ReferenceExtractor.new(project, current_user) ext.analyze(messages.join("\n")) issues = ext.issues + closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user). + closed_by_message(messages.join("\n")) end - issues - closes_issues + issues - closing_issues end def target_project_path diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 688c78fb404..b2c26874552 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -294,7 +294,7 @@ describe MergeRequest, models: true do end end - it 'detects issues mentioned but not closed' do + it 'detects issues mentioned in description but not closed' do mentioned_issue = create(:issue, project: subject.project) subject.description = "Is related to #{mentioned_issue.to_reference}" From b4764a8dd22c29b7edc3065af9a99713aa5708d3 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Fri, 25 Nov 2016 07:01:05 +1000 Subject: [PATCH 160/386] shorter lines on MergeRequest#merge_commit_message --- app/models/merge_request.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b8c139d01e2..5a5b8bd6010 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -631,10 +631,17 @@ class MergeRequest < ActiveRecord::Base end def merge_commit_message + closes_issues_references = closes_issues.map do |issue| + issue.to_reference(target_project) + end + message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n" message << "#{title}\n\n" - mr_closes_issues = closes_issues - message << "Closed Issues: #{mr_closes_issues.map { |issue| issue.to_reference(target_project) }.join(", ")}\n\n" if mr_closes_issues.present? + + if closes_issues_references.present? + message << "Closed Issues: #{closes_issues_references.join(", ")}\n\n" + end + message << "See merge request #{to_reference}" message From 00a842eacc4b2ff1514b258fbf08e4017f3be447 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Fri, 25 Nov 2016 19:49:56 +1000 Subject: [PATCH 161/386] Add toggle links for using default message and description on change merge commit message container --- .../widget/open/_accept.html.haml | 1 + .../_commit_message_container.html.haml | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 435fe835fae..66096ff7476 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -41,6 +41,7 @@ Modify commit message .js-toggle-content.hide.prepend-top-default = render 'shared/commit_message_container', params: params, + description: @merge_request.description, text: @merge_request.merge_commit_message, rows: 14, hint: true diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index 0a38327baa2..adee374413f 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -14,3 +14,31 @@ %p.hint Try to keep the first line under 52 characters and the others under 72. + - if local_assigns[:description] + %p.hint.use-description-hint + = link_to "#", class: "use-description-link" do + Use Merge Request description as merge commit message + %p.hint.use-default-message-hint.hide + = link_to "#", class: "use-default-message-link" do + Use default Gitlab merge commit message + + + :javascript + $('.use-description-link').on('click', function(e) { + e.preventDefault(); + + $('.use-description-hint').hide(); + $('.use-default-message-hint').show(); + $('.js-commit-message').val("#{escape_javascript local_assigns[:description]}"); + }); + + $('.use-default-message-link').on('click', function(e) { + e.preventDefault(); + + var defaultMessage = "#{escape_javascript (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder])}"; + + $('.use-description-hint').show(); + $('.use-default-message-hint').hide(); + $('.js-commit-message').val(defaultMessage); + }); + From 1a72dc2486601eadec03122f8124d3b553df3571 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Sun, 27 Nov 2016 20:40:56 +1000 Subject: [PATCH 162/386] keep branch being merged, MR title and MR reference in merge commit message when using description --- app/views/shared/_commit_message_container.html.haml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index adee374413f..706eef5a331 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -27,15 +27,21 @@ $('.use-description-link').on('click', function(e) { e.preventDefault(); + var message = "Merge branch '#{j @merge_request.source_branch}' into '#{j @merge_request.target_branch}'\n\n" + message = message + "#{j @merge_request.title}\n\n" + message = message + "#{j local_assigns[:description]}\n\n"; + message = message + "See merge request #{j @merge_request.to_reference}" + + $('.use-description-hint').hide(); $('.use-default-message-hint').show(); - $('.js-commit-message').val("#{escape_javascript local_assigns[:description]}"); + $('.js-commit-message').val(message) }); $('.use-default-message-link').on('click', function(e) { e.preventDefault(); - var defaultMessage = "#{escape_javascript (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder])}"; + var defaultMessage = "#{j (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder])}"; $('.use-description-hint').show(); $('.use-default-message-hint').hide(); From d1980ef9c8c059fb9d4be1a8339dea05e9a442f1 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 28 Nov 2016 07:37:57 +1000 Subject: [PATCH 163/386] only render MR description toggle javascript if description is available --- .../_commit_message_container.html.haml | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index 706eef5a331..a151731ba0a 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -14,7 +14,7 @@ %p.hint Try to keep the first line under 52 characters and the others under 72. - - if local_assigns[:description] + - if local_assigns[:description] %p.hint.use-description-hint = link_to "#", class: "use-description-link" do Use Merge Request description as merge commit message @@ -22,29 +22,28 @@ = link_to "#", class: "use-default-message-link" do Use default Gitlab merge commit message + :javascript + $('.use-description-link').on('click', function(e) { + e.preventDefault(); - :javascript - $('.use-description-link').on('click', function(e) { - e.preventDefault(); - - var message = "Merge branch '#{j @merge_request.source_branch}' into '#{j @merge_request.target_branch}'\n\n" - message = message + "#{j @merge_request.title}\n\n" - message = message + "#{j local_assigns[:description]}\n\n"; - message = message + "See merge request #{j @merge_request.to_reference}" + var message = "Merge branch '#{j @merge_request.source_branch}' into '#{j @merge_request.target_branch}'\n\n" + message = message + "#{j @merge_request.title}\n\n" + message = message + "#{j local_assigns[:description]}\n\n"; + message = message + "See merge request #{j @merge_request.to_reference}" - $('.use-description-hint').hide(); - $('.use-default-message-hint').show(); - $('.js-commit-message').val(message) - }); + $('.use-description-hint').hide(); + $('.use-default-message-hint').show(); + $('.js-commit-message').val(message) + }); - $('.use-default-message-link').on('click', function(e) { - e.preventDefault(); + $('.use-default-message-link').on('click', function(e) { + e.preventDefault(); - var defaultMessage = "#{j (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder])}"; + var defaultMessage = "#{j (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder])}"; - $('.use-description-hint').show(); - $('.use-default-message-hint').hide(); - $('.js-commit-message').val(defaultMessage); - }); + $('.use-description-hint').show(); + $('.use-default-message-hint').hide(); + $('.js-commit-message').val(defaultMessage); + }); From 78f221d12e28b6ea10f8fbc7f83fa39caaad05d0 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 28 Nov 2016 19:02:37 +1000 Subject: [PATCH 164/386] describe #closes_issues and describe # #issues_mentioned_but_not_closing on merge_request_spec.rb --- spec/models/merge_request_spec.rb | 42 ++++++++++++------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index b2c26874552..9ca60e27900 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -252,7 +252,7 @@ describe MergeRequest, models: true do end end - describe 'detection of issues' do + describe '#closes_issues' do let(:issue0) { create :issue, project: subject.project } let(:issue1) { create :issue, project: subject.project } @@ -265,38 +265,28 @@ describe MergeRequest, models: true do allow(subject).to receive(:commits).and_return([commit0, commit1, commit2]) end - describe 'detection of issues to be closed' do - it 'accesses the set of issues that will be closed on acceptance' do - allow(subject.project).to receive(:default_branch). - and_return(subject.target_branch) + it 'accesses the set of issues that will be closed on acceptance' do + allow(subject.project).to receive(:default_branch). + and_return(subject.target_branch) - closed = subject.closes_issues + closed = subject.closes_issues - expect(closed).to include(issue0, issue1) - end - - it 'only lists issues as to be closed if it targets the default branch' do - allow(subject.project).to receive(:default_branch).and_return('master') - subject.target_branch = 'something-else' - - expect(subject.closes_issues).to be_empty - end - - it 'detects issues mentioned in the description' do - issue2 = create(:issue, project: subject.project) - - subject.description = "Closes #{issue2.to_reference}" - - allow(subject.project).to receive(:default_branch). - and_return(subject.target_branch) - - expect(subject.closes_issues).to include(issue2) - end + expect(closed).to include(issue0, issue1) end + it 'only lists issues as to be closed if it targets the default branch' do + allow(subject.project).to receive(:default_branch).and_return('master') + subject.target_branch = 'something-else' + + expect(subject.closes_issues).to be_empty + end + end + + describe '#issues_mentioned_but_not_closing' do it 'detects issues mentioned in description but not closed' do mentioned_issue = create(:issue, project: subject.project) + subject.project.team << [subject.author, :developer] subject.description = "Is related to #{mentioned_issue.to_reference}" allow(subject.project).to receive(:default_branch). From 512c870ed46b5e441fd0b8daa8bd9cab449f7ac0 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 28 Nov 2016 19:06:18 +1000 Subject: [PATCH 165/386] Remove unnecessary code from MergeRequest#issues_mentioned_but_not_closing --- app/models/merge_request.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 5a5b8bd6010..dba0c463fd6 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -573,14 +573,12 @@ class MergeRequest < ActiveRecord::Base closing_issues = [] if target_branch == project.default_branch - messages = [description] - ext = Gitlab::ReferenceExtractor.new(project, current_user) - ext.analyze(messages.join("\n")) + ext.analyze(description) issues = ext.issues closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user). - closed_by_message(messages.join("\n")) + closed_by_message(description) end issues - closing_issues From 58609f842e1344579ed14745bb6bcb365059166f Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 28 Nov 2016 19:48:55 +1000 Subject: [PATCH 166/386] backend completely drives creation of merge commit message --- app/models/merge_request.rb | 3 +- .../merge_requests/widget/_open.html.haml | 3 +- .../widget/open/_accept.html.haml | 3 +- .../_commit_message_container.html.haml | 40 +++++++++---------- spec/models/merge_request_spec.rb | 14 +++++++ 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index dba0c463fd6..2d7be2c2c7e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -628,7 +628,7 @@ class MergeRequest < ActiveRecord::Base self.target_project.repository.branch_names.include?(self.target_branch) end - def merge_commit_message + def merge_commit_message(include_description: false) closes_issues_references = closes_issues.map do |issue| issue.to_reference(target_project) end @@ -640,6 +640,7 @@ class MergeRequest < ActiveRecord::Base message << "Closed Issues: #{closes_issues_references.join(", ")}\n\n" end + message << "#{description}\n\n" if include_description && description.present? message << "See merge request #{to_reference}" message diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index ebea48a4321..bf1e49c98ce 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -37,12 +37,11 @@ #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} mentioned but not being closed: = succeed '.' do != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author - = mr_assign_issues_link - if mr_closes_issues.present? .mr-widget-footer %span - %i.fa.fa-check + = icon('check') Accepting this merge request will close #{"issue".pluralize(mr_closes_issues.size)} = succeed '.' do != markdown issues_sentence(mr_closes_issues), pipeline: :gfm, author: @merge_request.author diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 66096ff7476..d6f7f23533c 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -41,7 +41,8 @@ Modify commit message .js-toggle-content.hide.prepend-top-default = render 'shared/commit_message_container', params: params, - description: @merge_request.description, + message_with_description: @merge_request.merge_commit_message(include_description: true), + message_without_description: @merge_request.merge_commit_message, text: @merge_request.merge_commit_message, rows: 14, hint: true diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index a151731ba0a..3e0186983e4 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -8,42 +8,38 @@ = text_area_tag 'commit_message', (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder]), class: 'form-control js-commit-message', placeholder: local_assigns[:placeholder], + data: local_assigns.slice(:message_with_description, :message_without_description), required: true, rows: (local_assigns[:rows] || 3), id: "commit_message-#{nonce}" - if local_assigns[:hint] %p.hint Try to keep the first line under 52 characters and the others under 72. - - if local_assigns[:description] - %p.hint.use-description-hint - = link_to "#", class: "use-description-link" do - Use Merge Request description as merge commit message - %p.hint.use-default-message-hint.hide - = link_to "#", class: "use-default-message-link" do - Use default Gitlab merge commit message + -if local_assigns.slice(:message_with_description, :message_without_description).present? + %p.hint.with-description-hint + = link_to "#", class: "with-description-link" do + Include description in commit message + %p.hint.without-description-hint.hide + = link_to "#", class: "without-description-link" do + Don't include description in commit message :javascript - $('.use-description-link').on('click', function(e) { + $('.with-description-link').on('click', function(e) { e.preventDefault(); - var message = "Merge branch '#{j @merge_request.source_branch}' into '#{j @merge_request.target_branch}'\n\n" - message = message + "#{j @merge_request.title}\n\n" - message = message + "#{j local_assigns[:description]}\n\n"; - message = message + "See merge request #{j @merge_request.to_reference}" + var textarea = $('.js-commit-message') - - $('.use-description-hint').hide(); - $('.use-default-message-hint').show(); - $('.js-commit-message').val(message) + textarea.val(textarea.data('messageWithDescription')) + $('.with-description-hint').hide(); + $('.without-description-hint').show(); }); - $('.use-default-message-link').on('click', function(e) { + $('.without-description-link').on('click', function(e) { e.preventDefault(); - var defaultMessage = "#{j (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder])}"; + var textarea = $('.js-commit-message') - $('.use-description-hint').show(); - $('.use-default-message-hint').hide(); - $('.js-commit-message').val(defaultMessage); + textarea.val(textarea.data('messageWithoutDescription')) + $('.with-description-hint').show(); + $('.without-description-hint').hide(); }); - diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 9ca60e27900..f74c89bba4d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -440,6 +440,20 @@ describe MergeRequest, models: true do expect(request.merge_commit_message).not_to match("Title\n\n\n\n") end + + it 'includes its description in the body' do + request = build(:merge_request, description: 'By removing all code') + + expect(request.merge_commit_message(include_description: true)) + .to match("By removing all code\n\n") + end + + it 'does not includes its description in the body' do + request = build(:merge_request, description: 'By removing all code') + + expect(request.merge_commit_message) + .not_to match("By removing all code\n\n") + end end describe "#reset_merge_when_build_succeeds" do From e97c7100aed6fb4ca072c80a78b95d5f51805197 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 28 Nov 2016 21:30:25 +1000 Subject: [PATCH 167/386] move javascript code from _commit_message_container view to javascripts/merge_request.js --- app/assets/javascripts/merge_request.js | 26 +++++++++++++++++++ .../_commit_message_container.html.haml | 21 --------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index 70f9a8d1955..309724071d2 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -27,6 +27,8 @@ // Prevent duplicate event bindings this.disableTaskList(); this.initMRBtnListeners(); + this.initMessageWithDescriptionListener(); + this.initMessageWithoutDescriptionListener(); if ($("a.btn-close").length) { this.initTaskList(); } @@ -108,6 +110,30 @@ // note so that we can re-use its form here }; + MergeRequest.prototype.initMessageWithDescriptionListener = function() { + return $('a.with-description-link').on('click', function(e) { + e.preventDefault(); + + var textarea = $('textarea.js-commit-message'); + + textarea.val(textarea.data('messageWithDescription')); + $('p.with-description-hint').hide(); + $('p.without-description-hint').show(); + }); + }; + + MergeRequest.prototype.initMessageWithoutDescriptionListener = function() { + return $('a.without-description-link').on('click', function(e) { + e.preventDefault(); + + var textarea = $('textarea.js-commit-message'); + + textarea.val(textarea.data('messageWithoutDescription')); + $('p.with-description-hint').show(); + $('p.without-description-hint').hide(); + }); + }; + return MergeRequest; })(); diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index 3e0186983e4..803cbb47e55 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -22,24 +22,3 @@ %p.hint.without-description-hint.hide = link_to "#", class: "without-description-link" do Don't include description in commit message - - :javascript - $('.with-description-link').on('click', function(e) { - e.preventDefault(); - - var textarea = $('.js-commit-message') - - textarea.val(textarea.data('messageWithDescription')) - $('.with-description-hint').hide(); - $('.without-description-hint').show(); - }); - - $('.without-description-link').on('click', function(e) { - e.preventDefault(); - - var textarea = $('.js-commit-message') - - textarea.val(textarea.data('messageWithoutDescription')) - $('.with-description-hint').show(); - $('.without-description-hint').hide(); - }); From 4181528569a81004d4e64f3e9726fc653a322cc7 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 28 Nov 2016 22:11:45 +1000 Subject: [PATCH 168/386] Better `Closes issues` text for MergeRequest#merge_commit_message --- app/models/merge_request.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 2d7be2c2c7e..48c30b08502 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -637,7 +637,8 @@ class MergeRequest < ActiveRecord::Base message << "#{title}\n\n" if closes_issues_references.present? - message << "Closed Issues: #{closes_issues_references.join(", ")}\n\n" + issue_text = 'issue'.pluralize(closes_issues_references.size) + message << "Closes #{issue_text} #{closes_issues_references.to_sentence}\n\n" end message << "#{description}\n\n" if include_description && description.present? From 9e321043c72322ae12aba230b49f9da326e66e56 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Tue, 29 Nov 2016 07:10:59 +1000 Subject: [PATCH 169/386] extract duplicate logic into a variable on _commit_message_container --- app/views/shared/_commit_message_container.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index 803cbb47e55..2b2da446d09 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -1,5 +1,6 @@ .form-group.commit_message-group - nonce = SecureRandom.hex + - descriptions = local_assigns.slice(:message_with_description, :message_without_description) = label_tag "commit_message-#{nonce}", class: 'control-label' do Commit message .col-sm-10 @@ -8,14 +9,14 @@ = text_area_tag 'commit_message', (params[:commit_message] || local_assigns[:text] || local_assigns[:placeholder]), class: 'form-control js-commit-message', placeholder: local_assigns[:placeholder], - data: local_assigns.slice(:message_with_description, :message_without_description), + data: descriptions, required: true, rows: (local_assigns[:rows] || 3), id: "commit_message-#{nonce}" - if local_assigns[:hint] %p.hint Try to keep the first line under 52 characters and the others under 72. - -if local_assigns.slice(:message_with_description, :message_without_description).present? + - if descriptions.present? %p.hint.with-description-hint = link_to "#", class: "with-description-link" do Include description in commit message From fedba9fc2568ec784eddbe6aff0fc9ca5f95b116 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Tue, 29 Nov 2016 07:29:15 +1000 Subject: [PATCH 170/386] add mentioned but not closed message to the same line as closes issueswq --- .../merge_requests/widget/_open.html.haml | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index bf1e49c98ce..4aae3ba63a9 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -30,19 +30,20 @@ - elsif @merge_request.can_be_merged? || resolved_conflicts = render 'projects/merge_requests/widget/open/accept' - - if mr_issues_mentioned_but_not_closing.present? - .mr-widget-footer - %span - %i.fa.fa-info-circle - #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} mentioned but not being closed: - = succeed '.' do - != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author - - - if mr_closes_issues.present? + - if mr_closes_issues.present? || mr_issues_mentioned_but_not_closing .mr-widget-footer %span = icon('check') - Accepting this merge request will close #{"issue".pluralize(mr_closes_issues.size)} - = succeed '.' do - != markdown issues_sentence(mr_closes_issues), pipeline: :gfm, author: @merge_request.author - = mr_assign_issues_link + - if mr_closes_issues.present? + Accepting this merge request will close #{"issue".pluralize(mr_closes_issues.size)} + = succeed '.' do + != markdown issues_sentence(mr_closes_issues), pipeline: :gfm, author: @merge_request.author + = mr_assign_issues_link + - if mr_issues_mentioned_but_not_closing.present? + #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} + = succeed '' do + != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author + = succeed '' do + mentioned but not closed. + + From 943ef94912410dd028626711c97cd1ae881a5e4c Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Tue, 29 Nov 2016 07:30:55 +1000 Subject: [PATCH 171/386] better text for mentioned but not closed --- app/views/projects/merge_requests/widget/_open.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 4aae3ba63a9..695359a48a3 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -44,6 +44,6 @@ = succeed '' do != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author = succeed '' do - mentioned but not closed. + mentioned but will not closed. From 99dd58ec557779eadd83aa597d8c16996be60df1 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 5 Dec 2016 20:46:43 +1000 Subject: [PATCH 172/386] Unify commit message listeners in one function --- app/assets/javascripts/merge_request.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index 309724071d2..194a27ae22b 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -27,8 +27,7 @@ // Prevent duplicate event bindings this.disableTaskList(); this.initMRBtnListeners(); - this.initMessageWithDescriptionListener(); - this.initMessageWithoutDescriptionListener(); + this.initCommitMessageListeners(); if ($("a.btn-close").length) { this.initTaskList(); } @@ -110,24 +109,20 @@ // note so that we can re-use its form here }; - MergeRequest.prototype.initMessageWithDescriptionListener = function() { - return $('a.with-description-link').on('click', function(e) { - e.preventDefault(); + MergeRequest.prototype.initCommitMessageListeners = function() { + var textarea = $('textarea.js-commit-message'); - var textarea = $('textarea.js-commit-message'); + $('a.with-description-link').on('click', function(e) { + e.preventDefault(); textarea.val(textarea.data('messageWithDescription')); $('p.with-description-hint').hide(); $('p.without-description-hint').show(); }); - }; - MergeRequest.prototype.initMessageWithoutDescriptionListener = function() { - return $('a.without-description-link').on('click', function(e) { + $('a.without-description-link').on('click', function(e) { e.preventDefault(); - var textarea = $('textarea.js-commit-message'); - textarea.val(textarea.data('messageWithoutDescription')); $('p.with-description-hint').show(); $('p.without-description-hint').hide(); From 603ef5d49ed453cbb47b68d3af078529c6b834a1 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 5 Dec 2016 21:29:17 +1000 Subject: [PATCH 173/386] Show either description or closes issues references on MergeRequest#merge_commit_message so closes issues references are not duplicated --- app/models/merge_request.rb | 14 ++++++++------ .../projects/merge_requests/widget/_open.html.haml | 8 +++----- spec/models/merge_request_spec.rb | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 48c30b08502..da293c3738f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -633,18 +633,20 @@ class MergeRequest < ActiveRecord::Base issue.to_reference(target_project) end - message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n" - message << "#{title}\n\n" + message = [ + "Merge branch '#{source_branch}' into '#{target_branch}'", + title + ] - if closes_issues_references.present? + if !include_description && closes_issues_references.present? issue_text = 'issue'.pluralize(closes_issues_references.size) - message << "Closes #{issue_text} #{closes_issues_references.to_sentence}\n\n" + message << "Closes #{issue_text} #{closes_issues_references.to_sentence}" end - message << "#{description}\n\n" if include_description && description.present? + message << "#{description}" if include_description && description.present? message << "See merge request #{to_reference}" - message + message.join("\n\n") end def reset_merge_when_build_succeeds diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 695359a48a3..f4aa1609a1b 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -30,7 +30,7 @@ - elsif @merge_request.can_be_merged? || resolved_conflicts = render 'projects/merge_requests/widget/open/accept' - - if mr_closes_issues.present? || mr_issues_mentioned_but_not_closing + - if mr_closes_issues.present? || mr_issues_mentioned_but_not_closing.present? .mr-widget-footer %span = icon('check') @@ -41,9 +41,7 @@ = mr_assign_issues_link - if mr_issues_mentioned_but_not_closing.present? #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} - = succeed '' do - != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author - = succeed '' do - mentioned but will not closed. + != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author + #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not closed. diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index f74c89bba4d..1e9790cf644 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -425,7 +425,7 @@ describe MergeRequest, models: true do and_return(subject.target_branch) expect(subject.merge_commit_message) - .to match("Closed Issues: #{issue.to_reference}") + .to match("Closes issue #{issue.to_reference}") end it 'includes its reference in the body' do From 5d478e5ceec3db9c38ef2ab1fafd7235fe3bb244 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 5 Dec 2016 21:43:40 +1000 Subject: [PATCH 174/386] add js prefix to classes used to toggle description on commit message in merge request --- app/assets/javascripts/merge_request.js | 12 ++++++------ app/views/shared/_commit_message_container.html.haml | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index 194a27ae22b..462dbc44e9f 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -112,20 +112,20 @@ MergeRequest.prototype.initCommitMessageListeners = function() { var textarea = $('textarea.js-commit-message'); - $('a.with-description-link').on('click', function(e) { + $('a.js-with-description-link').on('click', function(e) { e.preventDefault(); textarea.val(textarea.data('messageWithDescription')); - $('p.with-description-hint').hide(); - $('p.without-description-hint').show(); + $('p.js-with-description-hint').hide(); + $('p.js-without-description-hint').show(); }); - $('a.without-description-link').on('click', function(e) { + $('a.js-without-description-link').on('click', function(e) { e.preventDefault(); textarea.val(textarea.data('messageWithoutDescription')); - $('p.with-description-hint').show(); - $('p.without-description-hint').hide(); + $('p.js-with-description-hint').show(); + $('p.js-without-description-hint').hide(); }); }; diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index 2b2da446d09..c196bc06b17 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -17,9 +17,9 @@ Try to keep the first line under 52 characters and the others under 72. - if descriptions.present? - %p.hint.with-description-hint - = link_to "#", class: "with-description-link" do + %p.hint.js-with-description-hint + = link_to "#", class: "js-with-description-link" do Include description in commit message - %p.hint.without-description-hint.hide - = link_to "#", class: "without-description-link" do + %p.hint.js-without-description-hint.hide + = link_to "#", class: "js-without-description-link" do Don't include description in commit message From 7d7ae494d476f2e0588740612846da4284b3da0c Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 5 Dec 2016 21:44:29 +1000 Subject: [PATCH 175/386] add guard clause to MergeRequest#issues_mentioned_but_not_closing --- app/models/merge_request.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index da293c3738f..acaf14a12e9 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -569,17 +569,14 @@ class MergeRequest < ActiveRecord::Base end def issues_mentioned_but_not_closing(current_user = self.author) - issues = [] - closing_issues = [] + return [] unless target_branch == project.default_branch - if target_branch == project.default_branch - ext = Gitlab::ReferenceExtractor.new(project, current_user) - ext.analyze(description) + ext = Gitlab::ReferenceExtractor.new(project, current_user) + ext.analyze(description) - issues = ext.issues - closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user). - closed_by_message(description) - end + issues = ext.issues + closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user). + closed_by_message(description) issues - closing_issues end From 5311d7f0aec15768e78924b8fb7cb17cf487baa5 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Mon, 5 Dec 2016 22:52:47 +1000 Subject: [PATCH 176/386] Add feature spec to verify all 4 different states of closing issues message on Merge Request show page. --- .../merge_requests/closes_issues_spec.rb | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 spec/features/merge_requests/closes_issues_spec.rb diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb new file mode 100644 index 00000000000..cfa94e13df2 --- /dev/null +++ b/spec/features/merge_requests/closes_issues_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +feature 'Merge Commit Description', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + let(:issue_1) { create(:issue, project: project)} + let(:issue_2) { create(:issue, project: project)} + let(:merge_request) do + create( + :merge_request, + :simple, + source_project: project, + description: merge_request_description + ) + end + let(:merge_request_description) { 'Merge Request Description' } + + before do + project.team << [user, :master] + + login_as user + + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + + click_link 'Modify commit message' + end + + context 'not closing or mentioning any issue' do + it 'does not display closing issue message' do + expect(page).not_to have_css('.mr-widget-footer') + end + end + + context 'closing issues but not mentioning any other issue' do + let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" } + + it 'does not display closing issue message' do + expect(page).to have_content("Accepting this merge request will close issues #{issue_1.to_reference} and #{issue_2.to_reference}") + end + end + + context 'mentioning issues but not closing them' do + let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" } + + it 'does not display closing issue message' do + expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not closed.") + end + end + + context 'closing some issues and mentioning, but not closing, others' do + let(:merge_request_description) { "Description\n\ncloses #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } + + it 'does not display closing issue message' do + expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not closed.") + end + end +end From 4525cdaec3f9af4a38711137b5fcdcff68cd7d1a Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Thu, 8 Dec 2016 21:39:16 +1000 Subject: [PATCH 177/386] add eslint disable prefix for prefer-arrow-callback rule on header of merge_request.js file --- app/assets/javascripts/merge_request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index 462dbc44e9f..244c2f6746c 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, padded-blocks, max-len */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, padded-blocks, max-len, prefer-arrow-callback */ /* global MergeRequestTabs */ /*= require jquery.waitforimages */ From f3378630c15d43080d2bda03f5165653092a660b Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Sat, 10 Dec 2016 21:58:15 +1000 Subject: [PATCH 178/386] add feature specs to test toggling of merge commit message between message with description and without --- .../merge_requests/closes_issues_spec.rb | 4 +- .../merge_commit_message_toggle_spec.rb | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 spec/features/merge_requests/merge_commit_message_toggle_spec.rb diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb index cfa94e13df2..dc32c8f7373 100644 --- a/spec/features/merge_requests/closes_issues_spec.rb +++ b/spec/features/merge_requests/closes_issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Merge Commit Description', feature: true do +feature 'Merge Request closing issues message', feature: true do let(:user) { create(:user) } let(:project) { create(:project, :public) } let(:issue_1) { create(:issue, project: project)} @@ -21,8 +21,6 @@ feature 'Merge Commit Description', feature: true do login_as user visit namespace_project_merge_request_path(project.namespace, project, merge_request) - - click_link 'Modify commit message' end context 'not closing or mentioning any issue' do diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb new file mode 100644 index 00000000000..2c78234bd0f --- /dev/null +++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +feature 'Clicking toggle commit message link', feature: true, js: true do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + let(:issue_1) { create(:issue, project: project)} + let(:issue_2) { create(:issue, project: project)} + let(:merge_request) do + create( + :merge_request, + :simple, + source_project: project, + description: "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" + ) + end + let(:textbox) { page.find(:css, '.js-commit-message', visible: false) } + let(:include_link) { page.find(:css, '.js-with-description-link', visible: false) } + let(:do_not_include_link) { page.find(:css, '.js-without-description-link', visible: false) } + let(:default_message) do + [ + "Merge branch 'feature' into 'master'", + merge_request.title, + "Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}", + "See merge request #{merge_request.to_reference}" + ].join("\n\n") + end + let(:message_with_description) do + [ + "Merge branch 'feature' into 'master'", + merge_request.title, + merge_request.description, + "See merge request #{merge_request.to_reference}" + ].join("\n\n") + end + + before do + project.team << [user, :master] + + login_as user + + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + + expect(textbox).not_to be_visible + click_link "Modify commit message" + expect(textbox).to be_visible + end + + it "toggles commit message between message with description and without description " do + expect(textbox.value).to eq(default_message) + + click_link "Include description in commit message" + + expect(textbox.value).to eq(message_with_description) + + click_link "Don't include description in commit message" + + expect(textbox.value).to eq(default_message) + end + + it "toggles link between 'Include description' and 'Don't include description'" do + expect(include_link).to be_visible + expect(do_not_include_link).not_to be_visible + + click_link "Include description in commit message" + + expect(include_link).not_to be_visible + expect(do_not_include_link).to be_visible + + click_link "Don't include description in commit message" + + expect(include_link).to be_visible + expect(do_not_include_link).not_to be_visible + end +end From 3e3d6b53dcdc50bbe45c4b3ff43faf3d2728f72c Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Tue, 13 Dec 2016 23:00:00 +1000 Subject: [PATCH 179/386] Change closes issues reference text on MergeRequest#merge_commit_message to match existing text generated by the system --- app/models/merge_request.rb | 3 +-- .../merge_requests/merge_commit_message_toggle_spec.rb | 2 +- spec/models/merge_request_spec.rb | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index acaf14a12e9..b7c775777c7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -636,8 +636,7 @@ class MergeRequest < ActiveRecord::Base ] if !include_description && closes_issues_references.present? - issue_text = 'issue'.pluralize(closes_issues_references.size) - message << "Closes #{issue_text} #{closes_issues_references.to_sentence}" + message << "Closes #{closes_issues_references.to_sentence}" end message << "#{description}" if include_description && description.present? diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb index 2c78234bd0f..3dbe26cddb0 100644 --- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb +++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb @@ -20,7 +20,7 @@ feature 'Clicking toggle commit message link', feature: true, js: true do [ "Merge branch 'feature' into 'master'", merge_request.title, - "Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}", + "Closes #{issue_1.to_reference} and #{issue_2.to_reference}", "See merge request #{merge_request.to_reference}" ].join("\n\n") end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1e9790cf644..5da00a8636a 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -416,16 +416,16 @@ describe MergeRequest, models: true do end it 'includes its closed issues in the body' do - issue = create(:issue, project: subject.project) + issue = create(:issue, project: subject.project) subject.project.team << [subject.author, :developer] - subject.description = "Closes #{issue.to_reference}" + subject.description = "This issue Closes #{issue.to_reference}" allow(subject.project).to receive(:default_branch). and_return(subject.target_branch) expect(subject.merge_commit_message) - .to match("Closes issue #{issue.to_reference}") + .to match("Closes #{issue.to_reference}") end it 'includes its reference in the body' do From 8b26ff58e16e55077ddd704add5997924bcd94b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Fri, 16 Dec 2016 09:17:15 +0000 Subject: [PATCH 180/386] Update templates.rb --- lib/api/templates.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/templates.rb b/lib/api/templates.rb index 2d887e15f28..e23f99256a5 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -11,7 +11,7 @@ module API }, dockerfiles: { klass: Gitlab::Template::DockerfileTemplate, - gitlab_version: 8.9 + gitlab_version: 8.15 } }.freeze PROJECT_TEMPLATE_REGEX = From 1d0ccec6dd8375b751846f69bb170ebd11e9a391 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 22 Nov 2016 14:23:53 +0530 Subject: [PATCH 181/386] Add a `scopes` column to the `personal_access_tokens` table --- app/models/personal_access_token.rb | 2 + ...column_scopes_to_personal_access_tokens.rb | 36 +++++++++++++++++ ...cess_tokens_default_back_to_empty_array.rb | 39 +++++++++++++++++++ db/schema.rb | 1 + spec/factories/personal_access_tokens.rb | 1 + 5 files changed, 79 insertions(+) create mode 100644 db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb create mode 100644 db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index c4b095e0c04..10a34c42fd8 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -2,6 +2,8 @@ class PersonalAccessToken < ActiveRecord::Base include TokenAuthenticatable add_authentication_token_field :token + serialize :scopes, Array + belongs_to :user scope :active, -> { where(revoked: false).where("expires_at >= NOW() OR expires_at IS NULL") } diff --git a/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb b/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb new file mode 100644 index 00000000000..ab7f0365603 --- /dev/null +++ b/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb @@ -0,0 +1,36 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddColumnScopesToPersonalAccessTokens < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + disable_ddl_transaction! + + def up + # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. + # It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to + # `[]`. + add_column_with_default :personal_access_tokens, :scopes, :string, default: ['api'].to_yaml + end + + def down + remove_column :personal_access_tokens, :scopes + end +end diff --git a/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb b/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb new file mode 100644 index 00000000000..018cc3d4747 --- /dev/null +++ b/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb @@ -0,0 +1,39 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class ChangePersonalAccessTokensDefaultBackToEmptyArray < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def up + # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. + # It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to + # `[]`. + change_column_default :personal_access_tokens, :scopes, [].to_yaml + end + + def down + # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. + # It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to + # `[]`. + change_column_default :personal_access_tokens, :scopes, ['api'].to_yaml + end +end diff --git a/db/schema.rb b/db/schema.rb index 67ff83d96d9..a1a22df0d53 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -854,6 +854,7 @@ ActiveRecord::Schema.define(version: 20161212142807) do t.datetime "expires_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "scopes", default: "--- []\n", null: false end add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb index da4c72bcb5b..811eab7e15b 100644 --- a/spec/factories/personal_access_tokens.rb +++ b/spec/factories/personal_access_tokens.rb @@ -5,5 +5,6 @@ FactoryGirl.define do name { FFaker::Product.brand } revoked false expires_at { 5.days.from_now } + scopes ['api'] end end From 6c809dfae84e702f7a49d3fac5725745264e0ff9 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 22 Nov 2016 14:27:31 +0530 Subject: [PATCH 182/386] Allow creating personal access tokens / OAuth applications with scopes. --- app/assets/stylesheets/pages/profile.scss | 10 +++++++ .../admin/applications_controller.rb | 6 ++++- .../concerns/oauth_applications.rb | 14 ++++++++++ .../oauth/applications_controller.rb | 6 +++++ .../personal_access_tokens_controller.rb | 12 ++++----- app/views/admin/applications/_form.html.haml | 10 +++++++ app/views/admin/applications/show.html.haml | 15 +++++++++-- .../doorkeeper/applications/_form.html.haml | 9 +++++++ .../doorkeeper/applications/show.html.haml | 15 ++++++++++- .../personal_access_tokens/_form.html.haml | 22 ++++++++++++++++ .../personal_access_tokens/index.html.haml | 17 +++--------- .../profiles/personal_access_tokens_spec.rb | 26 +++++++++++++++++++ 12 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 app/controllers/concerns/oauth_applications.rb create mode 100644 app/views/profiles/personal_access_tokens/_form.html.haml diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8a5b0e20a86..8b1976bd925 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -262,3 +262,13 @@ table.u2f-registrations { border-right: solid 1px transparent; } } + +.oauth-application-show { + .scope-name { + font-weight: 600; + } + + .scopes-list { + padding-left: 18px; + } +} \ No newline at end of file diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index 471d24934a0..759044910bb 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -1,4 +1,6 @@ class Admin::ApplicationsController < Admin::ApplicationController + include OauthApplications + before_action :set_application, only: [:show, :edit, :update, :destroy] def index @@ -10,9 +12,11 @@ class Admin::ApplicationsController < Admin::ApplicationController def new @application = Doorkeeper::Application.new + @scopes = Doorkeeper.configuration.scopes end def edit + @scopes = Doorkeeper.configuration.scopes end def create @@ -47,6 +51,6 @@ class Admin::ApplicationsController < Admin::ApplicationController # Only allow a trusted parameter "white list" through. def application_params - params[:doorkeeper_application].permit(:name, :redirect_uri) + params[:doorkeeper_application].permit(:name, :redirect_uri, :scopes) end end diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb new file mode 100644 index 00000000000..34ad43ededd --- /dev/null +++ b/app/controllers/concerns/oauth_applications.rb @@ -0,0 +1,14 @@ +module OauthApplications + extend ActiveSupport::Concern + + included do + before_action :prepare_scopes, only: [:create, :update] + end + + def prepare_scopes + scopes = params.dig(:doorkeeper_application, :scopes) + if scopes + params[:doorkeeper_application][:scopes] = scopes.join(' ') + end + end +end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index 0f54dfa4efc..b5449a6c30e 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -2,6 +2,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController include Gitlab::CurrentSettings include Gitlab::GonHelper include PageLayoutHelper + include OauthApplications before_action :verify_user_oauth_applications_enabled before_action :authenticate_user! @@ -13,6 +14,10 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController set_index_vars end + def edit + @scopes = Doorkeeper.configuration.scopes + end + def create @application = Doorkeeper::Application.new(application_params) @@ -40,6 +45,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController @authorized_tokens = current_user.oauth_authorized_tokens @authorized_anonymous_tokens = @authorized_tokens.reject(&:application) @authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?) + @scopes = Doorkeeper.configuration.scopes # Don't overwrite a value possibly set by `create` @application ||= Doorkeeper::Application.new diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 508b82a9a6c..6e007f17913 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -1,8 +1,6 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController - before_action :load_personal_access_tokens, only: :index - def index - @personal_access_token = current_user.personal_access_tokens.build + set_index_vars end def create @@ -12,7 +10,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController flash[:personal_access_token] = @personal_access_token.token redirect_to profile_personal_access_tokens_path, notice: "Your new personal access token has been created." else - load_personal_access_tokens + set_index_vars render :index end end @@ -32,10 +30,12 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController private def personal_access_token_params - params.require(:personal_access_token).permit(:name, :expires_at) + params.require(:personal_access_token).permit(:name, :expires_at, scopes: []) end - def load_personal_access_tokens + def set_index_vars + @personal_access_token ||= current_user.personal_access_tokens.build + @scopes = Gitlab::Auth::SCOPES @active_personal_access_tokens = current_user.personal_access_tokens.active.order(:expires_at) @inactive_personal_access_tokens = current_user.personal_access_tokens.inactive end diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index 4aacbb8cd77..36d2f415a05 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -18,6 +18,16 @@ Use %code= Doorkeeper.configuration.native_redirect_uri for local tests + + .form-group + = f.label :scopes, class: 'col-sm-2 control-label' + .col-sm-10 + - @scopes.each do |scope| + %fieldset + = check_box_tag 'doorkeeper_application[scopes][]', scope, application.scopes.include?(scope), id: "doorkeeper_application_scopes_#{scope}" + = label_tag "doorkeeper_application_scopes_#{scope}", scope + %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" + .form-actions = f.submit 'Submit', class: "btn btn-save wide" = link_to "Cancel", admin_applications_path, class: "btn btn-default" diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 3eb9d61972b..3418dc96496 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -2,8 +2,7 @@ %h3.page-title Application: #{@application.name} - -.table-holder +.table-holder.oauth-application-show %table.table %tr %td @@ -23,6 +22,18 @@ - @application.redirect_uri.split.each do |uri| %div %span.monospace= uri + + - if @application.scopes.present? + %tr + %td + Scopes + %td + %ul.scopes-list.append-bottom-0 + - @application.scopes.each do |scope| + %li + %span.scope-name= scope + = "(#{t(scope, scope: [:doorkeeper, :scopes])})" + .form-actions = link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left' = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10' diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index 5c98265727a..6fdb04077b6 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -17,5 +17,14 @@ %code= Doorkeeper.configuration.native_redirect_uri for local tests + .form-group + = f.label :scopes, class: 'label-light' + - @scopes.each do |scope| + %fieldset + = check_box_tag 'doorkeeper_application[scopes][]', scope, application.scopes.include?(scope), id: "doorkeeper_application_scopes_#{scope}" + = label_tag "doorkeeper_application_scopes_#{scope}", scope + %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" + + .prepend-top-default = f.submit 'Save application', class: "btn btn-create" diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index 47442b78d48..a18e133c8de 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -2,7 +2,7 @@ %h3.page-title Application: #{@application.name} -.table-holder +.table-holder.oauth-application-show %table.table %tr %td @@ -22,6 +22,19 @@ - @application.redirect_uri.split.each do |uri| %div %span.monospace= uri + + - if @application.scopes.present? + %tr + %td + Scopes + %td + %ul.scopes-list.append-bottom-0 + - @application.scopes.each do |scope| + %li + %span.scope-name= scope + = "(#{t(scope, scope: [:doorkeeper, :scopes])})" + + .form-actions = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left' = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10' diff --git a/app/views/profiles/personal_access_tokens/_form.html.haml b/app/views/profiles/personal_access_tokens/_form.html.haml new file mode 100644 index 00000000000..6083fdaa31d --- /dev/null +++ b/app/views/profiles/personal_access_tokens/_form.html.haml @@ -0,0 +1,22 @@ += form_for [:profile, @personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f| + + = form_errors(@personal_access_token) + + .form-group + = f.label :name, class: 'label-light' + = f.text_field :name, class: "form-control", required: true + + .form-group + = f.label :expires_at, class: 'label-light' + = f.text_field :expires_at, class: "datepicker form-control", required: false + + .form-group + = f.label :scopes, class: 'label-light' + - @scopes.each do |scope| + %fieldset + = check_box_tag 'personal_access_token[scopes][]', scope, @personal_access_token.scopes.include?(scope), id: "personal_access_token_scopes_#{scope}" + = label_tag "personal_access_token_scopes_#{scope}", scope + %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" + + .prepend-top-default + = f.submit 'Create Personal Access Token', class: "btn btn-create" diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index 05a2ea67aa2..39eef0f6baf 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -28,21 +28,8 @@ Add a Personal Access Token %p.profile-settings-content Pick a name for the application, and we'll give you a unique token. - = form_for [:profile, @personal_access_token], - method: :post, html: { class: 'js-requires-input' } do |f| - = form_errors(@personal_access_token) - - .form-group - = f.label :name, class: 'label-light' - = f.text_field :name, class: "form-control", required: true - - .form-group - = f.label :expires_at, class: 'label-light' - = f.text_field :expires_at, class: "datepicker form-control", required: false - - .prepend-top-default - = f.submit 'Create Personal Access Token', class: "btn btn-create" + = render "form" %hr @@ -56,6 +43,7 @@ %th Name %th Created %th Expires + %th Scopes %th %tbody - @active_personal_access_tokens.each do |token| @@ -67,6 +55,7 @@ = token.expires_at.to_date.to_s(:medium) - else %span.personal-access-tokens-never-expires-label Never + %td= token.scopes.present? ? token.scopes.join(", ") : "" %td= link_to "Revoke", revoke_profile_personal_access_token_path(token), method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this token? This action cannot be undone." } - else diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index a85930c7543..0ffeeff0921 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -51,6 +51,32 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do expect(active_personal_access_tokens).to have_text(Date.today.next_month.at_beginning_of_month.to_s(:medium)) end + context "scopes" do + it "allows creation of a token with scopes" do + visit profile_personal_access_tokens_path + fill_in "Name", with: FFaker::Product.brand + + check "api" + check "read_user" + + expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1) + expect(created_personal_access_token).to eq(PersonalAccessToken.last.token) + expect(PersonalAccessToken.last.scopes).to match_array(['api', 'read_user']) + expect(active_personal_access_tokens).to have_text('api') + expect(active_personal_access_tokens).to have_text('read_user') + end + + it "allows creation of a token with no scopes" do + visit profile_personal_access_tokens_path + fill_in "Name", with: FFaker::Product.brand + + expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1) + expect(created_personal_access_token).to eq(PersonalAccessToken.last.token) + expect(PersonalAccessToken.last.scopes).to eq([]) + expect(active_personal_access_tokens).to have_text('no scopes') + end + end + context "when creation fails" do it "displays an error message" do disallow_personal_access_token_saves! From e0ef9dc83ebfe102aaf980495b14fd6a06a24fd1 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 16 Dec 2016 12:12:53 +0200 Subject: [PATCH 183/386] BB importer: Milestone importer --- lib/bitbucket/representation/issue.rb | 4 ++++ lib/gitlab/bitbucket_import/importer.rb | 2 ++ spec/lib/bitbucket/representation/issue_spec.rb | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index ffe8a65d839..3af731753d1 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -27,6 +27,10 @@ module Bitbucket raw['title'] end + def milestone + raw.dig('milestone', 'name') + end + def created_at raw['created_on'] end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 567f2b314aa..53c95ea4079 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -67,6 +67,7 @@ module Gitlab description += issue.description label_name = issue.kind + milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil issue = project.issues.create!( iid: issue.iid, @@ -74,6 +75,7 @@ module Gitlab description: description, state: issue.state, author_id: gitlab_user_id(project, issue.author), + milestone: milestone, created_at: issue.created_at, updated_at: issue.updated_at ) diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb index e1f3419c77e..9a195bebd31 100644 --- a/spec/lib/bitbucket/representation/issue_spec.rb +++ b/spec/lib/bitbucket/representation/issue_spec.rb @@ -9,6 +9,12 @@ describe Bitbucket::Representation::Issue do it { expect(described_class.new('kind' => 'bug').kind).to eq('bug') } end + describe '#milestone' do + it { expect(described_class.new({ 'milestone' => { 'name' => '1.0' } }).milestone).to eq('1.0') } + it { expect(described_class.new({}).milestone).to be_nil } + end + + describe '#author' do it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' } }).author).to eq('Ben') } it { expect(described_class.new({}).author).to be_nil } From 9f97fa4d9f4e86e8f1ff1db4621bcf81390936da Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Wed, 14 Dec 2016 20:45:39 +0000 Subject: [PATCH 184/386] Ensure issuable state changes only fire webhooks once * Webhooks for close and reopen events now fired in respective services only * Prevents generic 'update' webhooks firing too --- app/services/issuable_base_service.rb | 7 ++++++- ...ooks-fired-for-issue-closed-and-reopened.yml | 4 ++++ spec/services/issues/update_service_spec.rb | 5 +++++ .../merge_requests/update_service_spec.rb | 5 +++++ .../issuable_update_service_shared_examples.rb | 17 +++++++++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/25339-2-webhooks-fired-for-issue-closed-and-reopened.yml create mode 100644 spec/support/services/issuable_update_service_shared_examples.rb diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index b5f63cc5a1a..ab3d2a9a0cd 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -184,7 +184,8 @@ class IssuableBaseService < BaseService old_labels = issuable.labels.to_a old_mentioned_users = issuable.mentioned_users.to_a - params[:label_ids] = process_label_ids(params, existing_label_ids: issuable.label_ids) + label_ids = process_label_ids(params, existing_label_ids: issuable.label_ids) + params[:label_ids] = label_ids if labels_changing?(issuable.label_ids, label_ids) if params.present? && update_issuable(issuable, params) # We do not touch as it will affect a update on updated_at field @@ -201,6 +202,10 @@ class IssuableBaseService < BaseService issuable end + def labels_changing?(old_label_ids, new_label_ids) + old_label_ids.sort != new_label_ids.sort + end + def change_state(issuable) case params.delete(:state_event) when 'reopen' diff --git a/changelogs/unreleased/25339-2-webhooks-fired-for-issue-closed-and-reopened.yml b/changelogs/unreleased/25339-2-webhooks-fired-for-issue-closed-and-reopened.yml new file mode 100644 index 00000000000..b12eab26b67 --- /dev/null +++ b/changelogs/unreleased/25339-2-webhooks-fired-for-issue-closed-and-reopened.yml @@ -0,0 +1,4 @@ +--- +title: Ensure issuable state changes only fire webhooks once +merge_request: +author: diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 500d224ff98..eafbea46905 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -376,5 +376,10 @@ describe Issues::UpdateService, services: true do let(:mentionable) { issue } include_examples 'updating mentions', Issues::UpdateService end + + include_examples 'issuable update service' do + let(:open_issuable) { issue } + let(:closed_issuable) { create(:closed_issue, project: project) } + end end end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 790ef765f3a..88c786947d3 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -320,5 +320,10 @@ describe MergeRequests::UpdateService, services: true do expect(issue_ids).to be_empty end end + + include_examples 'issuable update service' do + let(:open_issuable) { merge_request } + let(:closed_issuable) { create(:closed_merge_request, source_project: project) } + end end end diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb new file mode 100644 index 00000000000..a3336755773 --- /dev/null +++ b/spec/support/services/issuable_update_service_shared_examples.rb @@ -0,0 +1,17 @@ +shared_examples 'issuable update service' do + context 'changing state' do + before { expect(project).to receive(:execute_hooks).once } + + context 'to reopened' do + it 'executes hooks only once' do + described_class.new(project, user, state_event: 'reopen').execute(closed_issuable) + end + end + + context 'to closed' do + it 'executes hooks only once' do + described_class.new(project, user, state_event: 'close').execute(open_issuable) + end + end + end +end From 2490f804cd4c12533fd6c70fc8014a06f2d19d47 Mon Sep 17 00:00:00 2001 From: Dimitrie Hoekstra Date: Fri, 16 Dec 2016 11:59:10 +0100 Subject: [PATCH 185/386] Additional rounded label fixes --- app/assets/stylesheets/framework/variables.scss | 2 +- app/assets/stylesheets/pages/labels.scss | 4 +++- changelogs/unreleased/rounded-labels-fixes.yml | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/rounded-labels-fixes.yml diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 936aaf38254..d201a538195 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -436,7 +436,7 @@ $jq-ui-default-color: #777; $label-gray-bg: #f8fafc; $label-inverse-bg: #333; $label-remove-border: rgba(0, 0, 0, .1); -$label-border-radius: 14px; +$label-border-radius: 100px; /* * Lint diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 25c91203ff4..703a429d63c 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -98,7 +98,7 @@ } .label { - padding: 9px; + padding: 8px 9px 9px 9px; font-size: 14px; } } @@ -201,6 +201,8 @@ .label-remove { border-left: 1px solid $label-remove-border; z-index: 3; + border-radius: $label-border-radius; + padding: 6px 10px 6px 9px; } .btn { diff --git a/changelogs/unreleased/rounded-labels-fixes.yml b/changelogs/unreleased/rounded-labels-fixes.yml new file mode 100644 index 00000000000..e0fbc6e3b5a --- /dev/null +++ b/changelogs/unreleased/rounded-labels-fixes.yml @@ -0,0 +1,4 @@ +--- +title: Additional rounded label fixes +merge_request: +author: From 7fa06ed55d18af4d055041eb27d38fecf9b5548f Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 22 Nov 2016 14:34:23 +0530 Subject: [PATCH 186/386] Calls to the API are checked for scope. - Move the `Oauth2::AccessTokenValidationService` class to `AccessTokenValidationService`, since it is now being used for personal access token validation as well. - Each API endpoint declares the scopes it accepts (if any). Currently, the top level API module declares the `api` scope, and the `Users` API module declares the `read_user` scope (for GET requests). - Move the `find_user_by_private_token` from the API `Helpers` module to the `APIGuard` module, to avoid littering `Helpers` with more auth-related methods to support `find_user_by_private_token` --- .../access_token_validation_service.rb | 34 ++++++++++ .../oauth2/access_token_validation_service.rb | 42 ------------ config/initializers/doorkeeper.rb | 4 +- config/locales/doorkeeper.en.yml | 1 + lib/api/api.rb | 2 + lib/api/api_guard.rb | 66 +++++++++++++------ lib/api/helpers.rb | 15 +---- lib/api/users.rb | 5 +- lib/gitlab/auth.rb | 4 ++ spec/requests/api/doorkeeper_access_spec.rb | 2 +- spec/requests/api/helpers_spec.rb | 43 +++++++----- .../access_token_validation_service_spec.rb | 42 ++++++++++++ 12 files changed, 166 insertions(+), 94 deletions(-) create mode 100644 app/services/access_token_validation_service.rb delete mode 100644 app/services/oauth2/access_token_validation_service.rb create mode 100644 spec/services/access_token_validation_service_spec.rb diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb new file mode 100644 index 00000000000..69449f3a445 --- /dev/null +++ b/app/services/access_token_validation_service.rb @@ -0,0 +1,34 @@ +module AccessTokenValidationService + # Results: + VALID = :valid + EXPIRED = :expired + REVOKED = :revoked + INSUFFICIENT_SCOPE = :insufficient_scope + + class << self + def validate(token, scopes: []) + if token.expired? + return EXPIRED + + elsif token.revoked? + return REVOKED + + elsif !self.sufficient_scope?(token, scopes) + return INSUFFICIENT_SCOPE + + else + return VALID + end + end + + # True if the token's scope contains any of the required scopes. + def sufficient_scope?(token, required_scopes) + if required_scopes.blank? + true + else + # Check whether the token is allowed access to any of the required scopes. + Set.new(required_scopes).intersection(Set.new(token.scopes)).present? + end + end + end +end diff --git a/app/services/oauth2/access_token_validation_service.rb b/app/services/oauth2/access_token_validation_service.rb deleted file mode 100644 index 264fdccde8f..00000000000 --- a/app/services/oauth2/access_token_validation_service.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Oauth2::AccessTokenValidationService - # Results: - VALID = :valid - EXPIRED = :expired - REVOKED = :revoked - INSUFFICIENT_SCOPE = :insufficient_scope - - class << self - def validate(token, scopes: []) - if token.expired? - return EXPIRED - - elsif token.revoked? - return REVOKED - - elsif !self.sufficient_scope?(token, scopes) - return INSUFFICIENT_SCOPE - - else - return VALID - end - end - - protected - - # True if the token's scope is a superset of required scopes, - # or the required scopes is empty. - def sufficient_scope?(token, scopes) - if scopes.blank? - # if no any scopes required, the scopes of token is sufficient. - return true - else - # If there are scopes required, then check whether - # the set of authorized scopes is a superset of the set of required scopes - required_scopes = Set.new(scopes) - authorized_scopes = Set.new(token.scopes) - - return authorized_scopes >= required_scopes - end - end - end -end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index fc4b0a72add..88cd0f5f652 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -52,8 +52,8 @@ Doorkeeper.configure do # Define access token scopes for your provider # For more information go to # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes - default_scopes :api - # optional_scopes :write, :update + default_scopes(*Gitlab::Auth::DEFAULT_SCOPES) + optional_scopes(*Gitlab::Auth::OPTIONAL_SCOPES) # Change the way client credentials are retrieved from the request object. # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml index a4032a21420..1d728282d90 100644 --- a/config/locales/doorkeeper.en.yml +++ b/config/locales/doorkeeper.en.yml @@ -59,6 +59,7 @@ en: unknown: "The access token is invalid" scopes: api: Access your API + read_user: Read user information flash: applications: diff --git a/lib/api/api.rb b/lib/api/api.rb index cec2702e44d..9d5adffd8f4 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -3,6 +3,8 @@ module API include APIGuard version 'v3', using: :path + before { allow_access_with_scope :api } + rescue_from Gitlab::Access::AccessDeniedError do rack_response({ 'message' => '403 Forbidden' }.to_json, 403) end diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index 8cc7a26f1fa..cd266669b1e 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -6,6 +6,9 @@ module API module APIGuard extend ActiveSupport::Concern + PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN" + PRIVATE_TOKEN_PARAM = :private_token + included do |base| # OAuth2 Resource Server Authentication use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request| @@ -41,30 +44,59 @@ module API # Defaults to empty array. # def doorkeeper_guard(scopes: []) - access_token = find_access_token - return nil unless access_token - - case validate_access_token(access_token, scopes) - when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE - raise InsufficientScopeError.new(scopes) - - when Oauth2::AccessTokenValidationService::EXPIRED - raise ExpiredError - - when Oauth2::AccessTokenValidationService::REVOKED - raise RevokedError - - when Oauth2::AccessTokenValidationService::VALID - @current_user = User.find(access_token.resource_owner_id) + if access_token = find_access_token + case AccessTokenValidationService.validate(access_token, scopes: scopes) + when AccessTokenValidationService::INSUFFICIENT_SCOPE + raise InsufficientScopeError.new(scopes) + when AccessTokenValidationService::EXPIRED + raise ExpiredError + when AccessTokenValidationService::REVOKED + raise RevokedError + when AccessTokenValidationService::VALID + @current_user = User.find(access_token.resource_owner_id) + end end end + def find_user_by_private_token(scopes: []) + token_string = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s + + return nil unless token_string.present? + + find_user_by_authentication_token(token_string) || find_user_by_personal_access_token(token_string, scopes) + end + def current_user @current_user end + # Set the authorization scope(s) allowed for the current request. + # + # Note: A call to this method adds to any previous scopes in place. This is done because + # `Grape` callbacks run from the outside-in: the top-level callback (API::API) runs first, then + # the next-level callback (API::API::Users, for example) runs. All these scopes are valid for the + # given endpoint (GET `/api/users` is accessible by the `api` and `read_user` scopes), and so they + # need to be stored. + def allow_access_with_scope(*scopes) + @scopes ||= [] + @scopes.concat(scopes.map(&:to_s)) + end + private + def find_user_by_authentication_token(token_string) + User.find_by_authentication_token(token_string) + end + + def find_user_by_personal_access_token(token_string, scopes) + access_token = PersonalAccessToken.active.find_by_token(token_string) + return unless access_token + + if AccessTokenValidationService.sufficient_scope?(access_token, scopes) + User.find(access_token.user_id) + end + end + def find_access_token @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) end @@ -72,10 +104,6 @@ module API def doorkeeper_request @doorkeeper_request ||= ActionDispatch::Request.new(env) end - - def validate_access_token(access_token, scopes) - Oauth2::AccessTokenValidationService.validate(access_token, scopes: scopes) - end end module ClassMethods diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 746849ef4c0..4be659fc20b 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -2,8 +2,6 @@ module API module Helpers include Gitlab::Utils - PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN" - PRIVATE_TOKEN_PARAM = :private_token SUDO_HEADER = "HTTP_SUDO" SUDO_PARAM = :sudo @@ -308,7 +306,7 @@ module API private def private_token - params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER] + params[APIGuard::PRIVATE_TOKEN_PARAM] || env[APIGuard::PRIVATE_TOKEN_HEADER] end def warden @@ -323,18 +321,11 @@ module API warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD']) end - def find_user_by_private_token - token = private_token - return nil unless token.present? - - User.find_by_authentication_token(token) || User.find_by_personal_access_token(token) - end - def initial_current_user return @initial_current_user if defined?(@initial_current_user) - @initial_current_user ||= find_user_by_private_token - @initial_current_user ||= doorkeeper_guard + @initial_current_user ||= find_user_by_private_token(scopes: @scopes) + @initial_current_user ||= doorkeeper_guard(scopes: @scopes) @initial_current_user ||= find_user_from_warden unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed? diff --git a/lib/api/users.rb b/lib/api/users.rb index c7db2d71017..0842c3874c5 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -2,7 +2,10 @@ module API class Users < Grape::API include PaginationParams - before { authenticate! } + before do + allow_access_with_scope :read_user if request.get? + authenticate! + end resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do helpers do diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index aca5d0020cf..c3c464248ef 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -2,6 +2,10 @@ module Gitlab module Auth class MissingPersonalTokenError < StandardError; end + SCOPES = [:api, :read_user] + DEFAULT_SCOPES = [:api] + OPTIONAL_SCOPES = SCOPES - DEFAULT_SCOPES + class << self def find_for_git_client(login, password, project:, ip:) raise "Must provide an IP for rate limiting" if ip.nil? diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index 5262a623761..bd9ecaf2685 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -5,7 +5,7 @@ describe API::API, api: true do let!(:user) { create(:user) } let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) } - let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id } + let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" } describe "when unauthenticated" do it "returns authentication success" do diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 4035fd97af5..15b93118ee4 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe API::Helpers, api: true do + include API::APIGuard::HelperMethods include API::Helpers include SentryHelper @@ -15,24 +16,24 @@ describe API::Helpers, api: true do def set_env(user_or_token, identifier) clear_env clear_param - env[API::Helpers::PRIVATE_TOKEN_HEADER] = user_or_token.respond_to?(:private_token) ? user_or_token.private_token : user_or_token + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user_or_token.respond_to?(:private_token) ? user_or_token.private_token : user_or_token env[API::Helpers::SUDO_HEADER] = identifier.to_s end def set_param(user_or_token, identifier) clear_env clear_param - params[API::Helpers::PRIVATE_TOKEN_PARAM] = user_or_token.respond_to?(:private_token) ? user_or_token.private_token : user_or_token + params[API::APIGuard::PRIVATE_TOKEN_PARAM] = user_or_token.respond_to?(:private_token) ? user_or_token.private_token : user_or_token params[API::Helpers::SUDO_PARAM] = identifier.to_s end def clear_env - env.delete(API::Helpers::PRIVATE_TOKEN_HEADER) + env.delete(API::APIGuard::PRIVATE_TOKEN_HEADER) env.delete(API::Helpers::SUDO_HEADER) end def clear_param - params.delete(API::Helpers::PRIVATE_TOKEN_PARAM) + params.delete(API::APIGuard::PRIVATE_TOKEN_PARAM) params.delete(API::Helpers::SUDO_PARAM) end @@ -94,22 +95,22 @@ describe API::Helpers, api: true do describe "when authenticating using a user's private token" do it "returns nil for an invalid token" do - env[API::Helpers::PRIVATE_TOKEN_HEADER] = 'invalid token' + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token' allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } expect(current_user).to be_nil end it "returns nil for a user without access" do - env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false) expect(current_user).to be_nil end it "leaves user as is when sudo not specified" do - env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token expect(current_user).to eq(user) clear_env - params[API::Helpers::PRIVATE_TOKEN_PARAM] = user.private_token + params[API::APIGuard::PRIVATE_TOKEN_PARAM] = user.private_token expect(current_user).to eq(user) end end @@ -117,37 +118,45 @@ describe API::Helpers, api: true do describe "when authenticating using a user's personal access tokens" do let(:personal_access_token) { create(:personal_access_token, user: user) } + before do + allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { false } + end + it "returns nil for an invalid token" do - env[API::Helpers::PRIVATE_TOKEN_HEADER] = 'invalid token' - allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token' expect(current_user).to be_nil end it "returns nil for a user without access" do - env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false) expect(current_user).to be_nil end + it "returns nil for a token without the appropriate scope" do + personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token + allow_access_with_scope('write_user') + expect(current_user).to be_nil + end + it "leaves user as is when sudo not specified" do - env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token expect(current_user).to eq(user) clear_env - params[API::Helpers::PRIVATE_TOKEN_PARAM] = personal_access_token.token + params[API::APIGuard::PRIVATE_TOKEN_PARAM] = personal_access_token.token expect(current_user).to eq(user) end it 'does not allow revoked tokens' do personal_access_token.revoke! - env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token - allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token expect(current_user).to be_nil end it 'does not allow expired tokens' do personal_access_token.update_attributes!(expires_at: 1.day.ago) - env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token - allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token expect(current_user).to be_nil end end diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb new file mode 100644 index 00000000000..8808934fa24 --- /dev/null +++ b/spec/services/access_token_validation_service_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe AccessTokenValidationService, services: true do + + describe ".sufficient_scope?" do + it "returns true if the required scope is present in the token's scopes" do + token = double("token", scopes: [:api, :read_user]) + + expect(described_class.sufficient_scope?(token, [:api])).to be(true) + end + + it "returns true if more than one of the required scopes is present in the token's scopes" do + token = double("token", scopes: [:api, :read_user, :other_scope]) + + expect(described_class.sufficient_scope?(token, [:api, :other_scope])).to be(true) + end + + it "returns true if the list of required scopes is an exact match for the token's scopes" do + token = double("token", scopes: [:api, :read_user, :other_scope]) + + expect(described_class.sufficient_scope?(token, [:api, :read_user, :other_scope])).to be(true) + end + + it "returns true if the list of required scopes contains all of the token's scopes, in addition to others" do + token = double("token", scopes: [:api, :read_user]) + + expect(described_class.sufficient_scope?(token, [:api, :read_user, :other_scope])).to be(true) + end + + it 'returns true if the list of required scopes is blank' do + token = double("token", scopes: []) + + expect(described_class.sufficient_scope?(token, [])).to be(true) + end + + it "returns false if there are no scopes in common between the required scopes and the token scopes" do + token = double("token", scopes: [:api, :read_user]) + + expect(described_class.sufficient_scope?(token, [:other_scope])).to be(false) + end + end +end From 36b3210b9ec4fffd9fa5a73626907e8a6a59f435 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 22 Nov 2016 14:43:37 +0530 Subject: [PATCH 187/386] Validate access token scopes in `Gitlab::Auth` - This module is used for git-over-http, as well as JWT. - The only valid scope here is `api`, currently. --- lib/gitlab/auth.rb | 14 ++++++++--- spec/lib/gitlab/auth_spec.rb | 46 ++++++++++++++++++++++++++++------ spec/requests/git_http_spec.rb | 2 +- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index c3c464248ef..c6a23aa2bdf 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -92,7 +92,7 @@ module Gitlab def oauth_access_token_check(login, password) if login == "oauth2" && password.present? token = Doorkeeper::AccessToken.by_token(password) - if token && token.accessible? + if token && token.accessible? && token_has_scope?(token) user = User.find_by(id: token.resource_owner_id) Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities) end @@ -101,12 +101,20 @@ module Gitlab def personal_access_token_check(login, password) if login && password - user = User.find_by_personal_access_token(password) + token = PersonalAccessToken.active.find_by_token(password) validation = User.by_login(login) - Gitlab::Auth::Result.new(user, nil, :personal_token, full_authentication_abilities) if user.present? && user == validation + + if token && token.user == validation && token_has_scope?(token) + Gitlab::Auth::Result.new(validation, nil, :personal_token, full_authentication_abilities) + end + end end + def token_has_scope?(token) + AccessTokenValidationService.sufficient_scope?(token, ['api']) + end + def lfs_token_check(login, password) deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/) diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index c9d64e99f88..b64413cda12 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -79,14 +79,46 @@ describe Gitlab::Auth, lib: true do expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities)) end - it 'recognizes OAuth tokens' do - user = create(:user) - application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) - token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id) - ip = 'ip' + context "while using OAuth tokens as passwords" do + it 'succeeds for OAuth tokens with the `api` scope' do + user = create(:user) + application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) + token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") + ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2') - expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2') + expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities)) + end + + it 'fails for OAuth tokens with other scopes' do + user = create(:user) + application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) + token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "read_user") + ip = 'ip' + + expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: 'oauth2') + expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, nil)) + end + end + + context "while using personal access tokens as passwords" do + it 'succeeds for personal access tokens with the `api` scope' do + user = create(:user) + personal_access_token = create(:personal_access_token, user: user, scopes: ['api']) + ip = 'ip' + + expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.email) + expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :personal_token, full_authentication_abilities)) + end + + it 'fails for personal access tokens with other scopes' do + user = create(:user) + personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) + ip = 'ip' + + expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: user.email) + expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, nil)) + end end it 'returns double nil for invalid credentials' do diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index f1728d61def..d71bb08c218 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -230,7 +230,7 @@ describe 'Git HTTP requests', lib: true do context "when an oauth token is provided" do before do application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) - @token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id) + @token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") end it "downloads get status 200" do From ac9835c602f1c9b5a35ef40df079faf1d4b91f7b Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 22 Nov 2016 16:20:21 +0530 Subject: [PATCH 188/386] Update CHANGELOG --- changelogs/unreleased/20492-access-token-scopes.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/20492-access-token-scopes.yml diff --git a/changelogs/unreleased/20492-access-token-scopes.yml b/changelogs/unreleased/20492-access-token-scopes.yml new file mode 100644 index 00000000000..a9424ded662 --- /dev/null +++ b/changelogs/unreleased/20492-access-token-scopes.yml @@ -0,0 +1,4 @@ +--- +title: Add scopes for personal access tokens and OAuth tokens +merge_request: 5951 +author: From 4d6da770de94f4bf140507cdf43461b67269ce28 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 24 Nov 2016 13:07:22 +0530 Subject: [PATCH 189/386] Implement minor changes from @dbalexandre's review. - Mainly whitespace changes. - Require the migration adding the `scope` column to the `personal_access_tokens` table to have downtime, since API calls will fail if the new code is in place, but the migration hasn't run. - Minor refactoring - load `@scopes` in a `before_action`, since we're doing it in three different places. --- .../admin/applications_controller.rb | 3 +- .../concerns/oauth_applications.rb | 5 ++++ .../oauth/applications_controller.rb | 6 +--- .../doorkeeper/applications/_form.html.haml | 1 - .../doorkeeper/applications/show.html.haml | 1 - ...column_scopes_to_personal_access_tokens.rb | 23 ++------------- ...cess_tokens_default_back_to_empty_array.rb | 28 ++----------------- lib/api/api_guard.rb | 26 +++++++++-------- lib/gitlab/auth.rb | 1 - .../access_token_validation_service_spec.rb | 1 - 10 files changed, 28 insertions(+), 67 deletions(-) diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index 759044910bb..62f62e99a97 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -2,6 +2,7 @@ class Admin::ApplicationsController < Admin::ApplicationController include OauthApplications before_action :set_application, only: [:show, :edit, :update, :destroy] + before_action :load_scopes, only: [:new, :edit] def index @applications = Doorkeeper::Application.where("owner_id IS NULL") @@ -12,11 +13,9 @@ class Admin::ApplicationsController < Admin::ApplicationController def new @application = Doorkeeper::Application.new - @scopes = Doorkeeper.configuration.scopes end def edit - @scopes = Doorkeeper.configuration.scopes end def create diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb index 34ad43ededd..7210ed3eb32 100644 --- a/app/controllers/concerns/oauth_applications.rb +++ b/app/controllers/concerns/oauth_applications.rb @@ -7,8 +7,13 @@ module OauthApplications def prepare_scopes scopes = params.dig(:doorkeeper_application, :scopes) + if scopes params[:doorkeeper_application][:scopes] = scopes.join(' ') end end + + def load_scopes + @scopes = Doorkeeper.configuration.scopes + end end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index b5449a6c30e..2ae4785b12c 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -7,6 +7,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController before_action :verify_user_oauth_applications_enabled before_action :authenticate_user! before_action :add_gon_variables + before_action :load_scopes, only: [:index, :create, :edit] layout 'profile' @@ -14,10 +15,6 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController set_index_vars end - def edit - @scopes = Doorkeeper.configuration.scopes - end - def create @application = Doorkeeper::Application.new(application_params) @@ -45,7 +42,6 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController @authorized_tokens = current_user.oauth_authorized_tokens @authorized_anonymous_tokens = @authorized_tokens.reject(&:application) @authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?) - @scopes = Doorkeeper.configuration.scopes # Don't overwrite a value possibly set by `create` @application ||= Doorkeeper::Application.new diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index 6fdb04077b6..96677dc1a4d 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -25,6 +25,5 @@ = label_tag "doorkeeper_application_scopes_#{scope}", scope %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" - .prepend-top-default = f.submit 'Save application', class: "btn btn-create" diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index a18e133c8de..5473a8e0ddc 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -34,7 +34,6 @@ %span.scope-name= scope = "(#{t(scope, scope: [:doorkeeper, :scopes])})" - .form-actions = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left' = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10' diff --git a/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb b/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb index ab7f0365603..91479de840b 100644 --- a/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb +++ b/db/migrate/20160823083941_add_column_scopes_to_personal_access_tokens.rb @@ -1,32 +1,15 @@ -# See http://doc.gitlab.com/ce/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. +# The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. +# It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to +# `[]`. class AddColumnScopesToPersonalAccessTokens < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers - # Set this constant to true if this migration requires downtime. DOWNTIME = false - # When a migration requires downtime you **must** uncomment the following - # constant and define a short and easy to understand explanation as to why the - # migration requires downtime. - # DOWNTIME_REASON = '' - - # When using the methods "add_concurrent_index" or "add_column_with_default" - # you must disable the use of transactions as these methods can not run in an - # existing transaction. When using "add_concurrent_index" make sure that this - # method is the _only_ method called in the migration, any other changes - # should go in a separate migration. This ensures that upon failure _only_ the - # index creation fails and can be retried or reverted easily. - # - # To disable transactions uncomment the following line and remove these - # comments: disable_ddl_transaction! def up - # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. - # It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to - # `[]`. add_column_with_default :personal_access_tokens, :scopes, :string, default: ['api'].to_yaml end diff --git a/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb b/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb index 018cc3d4747..c8ceb116b8a 100644 --- a/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb +++ b/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb @@ -1,39 +1,17 @@ -# See http://doc.gitlab.com/ce/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. +# The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. +# It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to +# `[]`. class ChangePersonalAccessTokensDefaultBackToEmptyArray < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers - # Set this constant to true if this migration requires downtime. DOWNTIME = false - # When a migration requires downtime you **must** uncomment the following - # constant and define a short and easy to understand explanation as to why the - # migration requires downtime. - # DOWNTIME_REASON = '' - - # When using the methods "add_concurrent_index" or "add_column_with_default" - # you must disable the use of transactions as these methods can not run in an - # existing transaction. When using "add_concurrent_index" make sure that this - # method is the _only_ method called in the migration, any other changes - # should go in a separate migration. This ensures that upon failure _only_ the - # index creation fails and can be retried or reverted easily. - # - # To disable transactions uncomment the following line and remove these - # comments: - # disable_ddl_transaction! - def up - # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. - # It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to - # `[]`. change_column_default :personal_access_tokens, :scopes, [].to_yaml end def down - # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. - # It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to - # `[]`. change_column_default :personal_access_tokens, :scopes, ['api'].to_yaml end end diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index cd266669b1e..563224a580f 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -44,17 +44,21 @@ module API # Defaults to empty array. # def doorkeeper_guard(scopes: []) - if access_token = find_access_token - case AccessTokenValidationService.validate(access_token, scopes: scopes) - when AccessTokenValidationService::INSUFFICIENT_SCOPE - raise InsufficientScopeError.new(scopes) - when AccessTokenValidationService::EXPIRED - raise ExpiredError - when AccessTokenValidationService::REVOKED - raise RevokedError - when AccessTokenValidationService::VALID - @current_user = User.find(access_token.resource_owner_id) - end + access_token = find_access_token + return nil unless access_token + + case AccessTokenValidationService.validate(access_token, scopes: scopes) + when AccessTokenValidationService::INSUFFICIENT_SCOPE + raise InsufficientScopeError.new(scopes) + + when AccessTokenValidationService::EXPIRED + raise ExpiredError + + when AccessTokenValidationService::REVOKED + raise RevokedError + + when AccessTokenValidationService::VALID + @current_user = User.find(access_token.resource_owner_id) end end diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index c6a23aa2bdf..c425702fd75 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -107,7 +107,6 @@ module Gitlab if token && token.user == validation && token_has_scope?(token) Gitlab::Auth::Result.new(validation, nil, :personal_token, full_authentication_abilities) end - end end diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb index 8808934fa24..332e745aa36 100644 --- a/spec/services/access_token_validation_service_spec.rb +++ b/spec/services/access_token_validation_service_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe AccessTokenValidationService, services: true do - describe ".sufficient_scope?" do it "returns true if the required scope is present in the token's scopes" do token = double("token", scopes: [:api, :read_user]) From 990ae6b8e5f2797a6c168f9c16a725a159570058 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 24 Nov 2016 14:22:03 +0530 Subject: [PATCH 190/386] Move the scopes form/list view into a partial. - The list of scopes that's displayed while creating a personal access token is identical to the list that's displayed while creating an OAuth application. Extract these into a partial. - The list of scopes that's displayed while in the show page for an OAuth token in the profile settings and admin settings are identical. Extract these into a partial. --- app/views/admin/applications/show.html.haml | 11 +---------- app/views/doorkeeper/applications/_form.html.haml | 6 +----- app/views/doorkeeper/applications/show.html.haml | 11 +---------- .../profiles/personal_access_tokens/_form.html.haml | 6 +----- app/views/shared/tokens/_scopes_form.html.haml | 5 +++++ app/views/shared/tokens/_scopes_list.html.haml | 10 ++++++++++ 6 files changed, 19 insertions(+), 30 deletions(-) create mode 100644 app/views/shared/tokens/_scopes_form.html.haml create mode 100644 app/views/shared/tokens/_scopes_list.html.haml diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 3418dc96496..6e7b7003ac5 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -23,16 +23,7 @@ %div %span.monospace= uri - - if @application.scopes.present? - %tr - %td - Scopes - %td - %ul.scopes-list.append-bottom-0 - - @application.scopes.each do |scope| - %li - %span.scope-name= scope - = "(#{t(scope, scope: [:doorkeeper, :scopes])})" + = render partial: "shared/tokens/scopes_list" .form-actions = link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left' diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index 96677dc1a4d..a6ad0bb8d1b 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -19,11 +19,7 @@ .form-group = f.label :scopes, class: 'label-light' - - @scopes.each do |scope| - %fieldset - = check_box_tag 'doorkeeper_application[scopes][]', scope, application.scopes.include?(scope), id: "doorkeeper_application_scopes_#{scope}" - = label_tag "doorkeeper_application_scopes_#{scope}", scope - %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" + = render partial: 'shared/tokens/scopes_form', locals: { prefix: 'doorkeeper_application', token: application } .prepend-top-default = f.submit 'Save application', class: "btn btn-create" diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index 5473a8e0ddc..e528cb825f5 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -23,16 +23,7 @@ %div %span.monospace= uri - - if @application.scopes.present? - %tr - %td - Scopes - %td - %ul.scopes-list.append-bottom-0 - - @application.scopes.each do |scope| - %li - %span.scope-name= scope - = "(#{t(scope, scope: [:doorkeeper, :scopes])})" + = render partial: "shared/tokens/scopes_list" .form-actions = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left' diff --git a/app/views/profiles/personal_access_tokens/_form.html.haml b/app/views/profiles/personal_access_tokens/_form.html.haml index 6083fdaa31d..5651b242129 100644 --- a/app/views/profiles/personal_access_tokens/_form.html.haml +++ b/app/views/profiles/personal_access_tokens/_form.html.haml @@ -12,11 +12,7 @@ .form-group = f.label :scopes, class: 'label-light' - - @scopes.each do |scope| - %fieldset - = check_box_tag 'personal_access_token[scopes][]', scope, @personal_access_token.scopes.include?(scope), id: "personal_access_token_scopes_#{scope}" - = label_tag "personal_access_token_scopes_#{scope}", scope - %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" + = render partial: 'shared/tokens/scopes_form', locals: { prefix: 'personal_access_token', token: @personal_access_token } .prepend-top-default = f.submit 'Create Personal Access Token', class: "btn btn-create" diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml new file mode 100644 index 00000000000..5dbbd9e4808 --- /dev/null +++ b/app/views/shared/tokens/_scopes_form.html.haml @@ -0,0 +1,5 @@ +- @scopes.each do |scope| + %fieldset + = check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}" + = label_tag "#{prefix}_scopes_#{scope}", scope + %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" diff --git a/app/views/shared/tokens/_scopes_list.html.haml b/app/views/shared/tokens/_scopes_list.html.haml new file mode 100644 index 00000000000..9e3b562f0f5 --- /dev/null +++ b/app/views/shared/tokens/_scopes_list.html.haml @@ -0,0 +1,10 @@ +- if @application.scopes.present? + %tr + %td + Scopes + %td + %ul.scopes-list.append-bottom-0 + - @application.scopes.each do |scope| + %li + %span.scope-name= scope + = "(#{t(scope, scope: [:doorkeeper, :scopes])})" From dc95bcbb165289d9754e6bf66288c8d4350f6e57 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 24 Nov 2016 14:39:12 +0530 Subject: [PATCH 191/386] Refactor access token validation in `Gitlab::Auth` - Based on @dbalexandre's review - Extract token validity conditions into two separate methods, for personal access tokens and OAuth tokens. --- lib/gitlab/auth.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index c425702fd75..c21afaa1551 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -92,7 +92,7 @@ module Gitlab def oauth_access_token_check(login, password) if login == "oauth2" && password.present? token = Doorkeeper::AccessToken.by_token(password) - if token && token.accessible? && token_has_scope?(token) + if valid_oauth_token?(token) user = User.find_by(id: token.resource_owner_id) Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities) end @@ -104,12 +104,20 @@ module Gitlab token = PersonalAccessToken.active.find_by_token(password) validation = User.by_login(login) - if token && token.user == validation && token_has_scope?(token) + if valid_personal_access_token?(token, validation) Gitlab::Auth::Result.new(validation, nil, :personal_token, full_authentication_abilities) end end end + def valid_oauth_token?(token) + token && token.accessible? && token_has_scope?(token) + end + + def valid_personal_access_token?(token, user) + token && token.user == user && token_has_scope?(token) + end + def token_has_scope?(token) AccessTokenValidationService.sufficient_scope?(token, ['api']) end From fc7a5a3806c7c7317731ea305715fe0573b90d88 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 28 Nov 2016 12:30:41 +0530 Subject: [PATCH 192/386] Modify `ApiHelpers` spec to adhere to the Four-Phase test style. - Use whitespace to separate the setup, expectation and teardown phases. --- spec/requests/api/helpers_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 15b93118ee4..c3d7ac3eef8 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -97,20 +97,26 @@ describe API::Helpers, api: true do it "returns nil for an invalid token" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token' allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } + expect(current_user).to be_nil end it "returns nil for a user without access" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false) + expect(current_user).to be_nil end it "leaves user as is when sudo not specified" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token + expect(current_user).to eq(user) + clear_env + params[API::APIGuard::PRIVATE_TOKEN_PARAM] = user.private_token + expect(current_user).to eq(user) end end @@ -124,12 +130,14 @@ describe API::Helpers, api: true do it "returns nil for an invalid token" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token' + expect(current_user).to be_nil end it "returns nil for a user without access" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false) + expect(current_user).to be_nil end @@ -137,6 +145,7 @@ describe API::Helpers, api: true do personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token allow_access_with_scope('write_user') + expect(current_user).to be_nil end @@ -145,18 +154,21 @@ describe API::Helpers, api: true do expect(current_user).to eq(user) clear_env params[API::APIGuard::PRIVATE_TOKEN_PARAM] = personal_access_token.token + expect(current_user).to eq(user) end it 'does not allow revoked tokens' do personal_access_token.revoke! env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token + expect(current_user).to be_nil end it 'does not allow expired tokens' do personal_access_token.update_attributes!(expires_at: 1.day.ago) env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token + expect(current_user).to be_nil end end From f14d423dc7c9ec2d97c83f0c8893661922df4360 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 28 Nov 2016 13:13:53 +0530 Subject: [PATCH 193/386] Add a controller spec for personal access tokens. Split the existing feature spec into both feature and controller specs. Feature specs assert on browser DOM, and controller specs assert on database state. --- .../profiles/personal_access_tokens_spec.rb | 49 ++++++++++++++++++ .../profiles/personal_access_tokens_spec.rb | 51 ++++--------------- 2 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 spec/controllers/profiles/personal_access_tokens_spec.rb diff --git a/spec/controllers/profiles/personal_access_tokens_spec.rb b/spec/controllers/profiles/personal_access_tokens_spec.rb new file mode 100644 index 00000000000..45534a3a587 --- /dev/null +++ b/spec/controllers/profiles/personal_access_tokens_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe Profiles::PersonalAccessTokensController do + let(:user) { create(:user) } + + describe '#create' do + def created_token + PersonalAccessToken.order(:created_at).last + end + + before { sign_in(user) } + + it "allows creation of a token" do + name = FFaker::Product.brand + + post :create, personal_access_token: { name: name } + + expect(created_token).not_to be_nil + expect(created_token.name).to eq(name) + expect(created_token.expires_at).to be_nil + expect(PersonalAccessToken.active).to include(created_token) + end + + it "allows creation of a token with an expiry date" do + expires_at = 5.days.from_now + + post :create, personal_access_token: { name: FFaker::Product.brand, expires_at: expires_at } + + expect(created_token).not_to be_nil + expect(created_token.expires_at.to_i).to eq(expires_at.to_i) + end + + context "scopes" do + it "allows creation of a token with scopes" do + post :create, personal_access_token: { name: FFaker::Product.brand, scopes: ['api', 'read_user'] } + + expect(created_token).not_to be_nil + expect(created_token.scopes).to eq(['api', 'read_user']) + end + + it "allows creation of a token with no scopes" do + post :create, personal_access_token: { name: FFaker::Product.brand, scopes: [] } + + expect(created_token).not_to be_nil + expect(created_token.scopes).to eq([]) + end + end + end +end diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index 0ffeeff0921..55a01057c83 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -27,54 +27,25 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do describe "token creation" do it "allows creation of a token" do - visit profile_personal_access_tokens_path - fill_in "Name", with: FFaker::Product.brand + name = FFaker::Product.brand - expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1) - expect(created_personal_access_token).to eq(PersonalAccessToken.last.token) - expect(active_personal_access_tokens).to have_text(PersonalAccessToken.last.name) - expect(active_personal_access_tokens).to have_text("Never") - end - - it "allows creation of a token with an expiry date" do visit profile_personal_access_tokens_path - fill_in "Name", with: FFaker::Product.brand + fill_in "Name", with: name # Set date to 1st of next month find_field("Expires at").trigger('focus') find("a[title='Next']").click click_on "1" - expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1) - expect(created_personal_access_token).to eq(PersonalAccessToken.last.token) - expect(active_personal_access_tokens).to have_text(PersonalAccessToken.last.name) + # Scopes + check "api" + check "read_user" + + click_on "Create Personal Access Token" + expect(active_personal_access_tokens).to have_text(name) expect(active_personal_access_tokens).to have_text(Date.today.next_month.at_beginning_of_month.to_s(:medium)) - end - - context "scopes" do - it "allows creation of a token with scopes" do - visit profile_personal_access_tokens_path - fill_in "Name", with: FFaker::Product.brand - - check "api" - check "read_user" - - expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1) - expect(created_personal_access_token).to eq(PersonalAccessToken.last.token) - expect(PersonalAccessToken.last.scopes).to match_array(['api', 'read_user']) - expect(active_personal_access_tokens).to have_text('api') - expect(active_personal_access_tokens).to have_text('read_user') - end - - it "allows creation of a token with no scopes" do - visit profile_personal_access_tokens_path - fill_in "Name", with: FFaker::Product.brand - - expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1) - expect(created_personal_access_token).to eq(PersonalAccessToken.last.token) - expect(PersonalAccessToken.last.scopes).to eq([]) - expect(active_personal_access_tokens).to have_text('no scopes') - end + expect(active_personal_access_tokens).to have_text('api') + expect(active_personal_access_tokens).to have_text('read_user') end context "when creation fails" do @@ -111,7 +82,7 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do disallow_personal_access_token_saves! visit profile_personal_access_tokens_path - expect { click_on "Revoke" }.not_to change { PersonalAccessToken.inactive.count } + click_on "Revoke" expect(active_personal_access_tokens).to have_text(personal_access_token.name) expect(page).to have_content("Could not revoke") end From f706a973c26f9de9a1f1599d532b33e9e66a80bb Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 5 Dec 2016 09:49:51 +0530 Subject: [PATCH 194/386] View-related (and other minor) changes to !5951 based on @rymai's review. - The `scopes_form` partial can be used in the `admin/applications` view as well - Don't allow partials to access instance variables directly. Instead, pass in the instance variables as local variables, and use `local_assigns.fetch` to assert that the variables are passed in as expected. - Change a few instances of `render :partial` to `render` - Remove an instance of `required: false` in a view, since this is the default - Inline many instances of a local variable (`ip = 'ip'`) in `auth_spec` --- app/views/admin/applications/_form.html.haml | 6 +-- app/views/admin/applications/show.html.haml | 2 +- .../doorkeeper/applications/_form.html.haml | 2 +- .../doorkeeper/applications/show.html.haml | 2 +- .../personal_access_tokens/_form.html.haml | 11 +++-- .../personal_access_tokens/index.html.haml | 2 +- .../shared/tokens/_scopes_form.html.haml | 6 ++- .../shared/tokens/_scopes_list.html.haml | 23 ++++++---- spec/lib/gitlab/auth_spec.rb | 46 ++++++++----------- 9 files changed, 48 insertions(+), 52 deletions(-) diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index 36d2f415a05..c689b26d6e6 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -22,11 +22,7 @@ .form-group = f.label :scopes, class: 'col-sm-2 control-label' .col-sm-10 - - @scopes.each do |scope| - %fieldset - = check_box_tag 'doorkeeper_application[scopes][]', scope, application.scopes.include?(scope), id: "doorkeeper_application_scopes_#{scope}" - = label_tag "doorkeeper_application_scopes_#{scope}", scope - %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})" + = render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes .form-actions = f.submit 'Submit', class: "btn btn-save wide" diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 6e7b7003ac5..14683cc66e9 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -23,7 +23,7 @@ %div %span.monospace= uri - = render partial: "shared/tokens/scopes_list" + = render "shared/tokens/scopes_list", token: @application .form-actions = link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left' diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index a6ad0bb8d1b..b3313c7c985 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -19,7 +19,7 @@ .form-group = f.label :scopes, class: 'label-light' - = render partial: 'shared/tokens/scopes_form', locals: { prefix: 'doorkeeper_application', token: application } + = render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes .prepend-top-default = f.submit 'Save application', class: "btn btn-create" diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index e528cb825f5..559de63d96d 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -23,7 +23,7 @@ %div %span.monospace= uri - = render partial: "shared/tokens/scopes_list" + = render "shared/tokens/scopes_list", token: @application .form-actions = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left' diff --git a/app/views/profiles/personal_access_tokens/_form.html.haml b/app/views/profiles/personal_access_tokens/_form.html.haml index 5651b242129..3f6efa33953 100644 --- a/app/views/profiles/personal_access_tokens/_form.html.haml +++ b/app/views/profiles/personal_access_tokens/_form.html.haml @@ -1,6 +1,9 @@ -= form_for [:profile, @personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f| +- personal_access_token = local_assigns.fetch(:personal_access_token) +- scopes = local_assigns.fetch(:scopes) - = form_errors(@personal_access_token) += form_for [:profile, personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f| + + = form_errors(personal_access_token) .form-group = f.label :name, class: 'label-light' @@ -8,11 +11,11 @@ .form-group = f.label :expires_at, class: 'label-light' - = f.text_field :expires_at, class: "datepicker form-control", required: false + = f.text_field :expires_at, class: "datepicker form-control" .form-group = f.label :scopes, class: 'label-light' - = render partial: 'shared/tokens/scopes_form', locals: { prefix: 'personal_access_token', token: @personal_access_token } + = render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: personal_access_token, scopes: scopes .prepend-top-default = f.submit 'Create Personal Access Token', class: "btn btn-create" diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index 39eef0f6baf..bb4effeeeb1 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -29,7 +29,7 @@ %p.profile-settings-content Pick a name for the application, and we'll give you a unique token. - = render "form" + = render "form", personal_access_token: @personal_access_token, scopes: @scopes %hr diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml index 5dbbd9e4808..5074afb63a1 100644 --- a/app/views/shared/tokens/_scopes_form.html.haml +++ b/app/views/shared/tokens/_scopes_form.html.haml @@ -1,4 +1,8 @@ -- @scopes.each do |scope| +- scopes = local_assigns.fetch(:scopes) +- prefix = local_assigns.fetch(:prefix) +- token = local_assigns.fetch(:token) + +- scopes.each do |scope| %fieldset = check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}" = label_tag "#{prefix}_scopes_#{scope}", scope diff --git a/app/views/shared/tokens/_scopes_list.html.haml b/app/views/shared/tokens/_scopes_list.html.haml index 9e3b562f0f5..f99e905e95c 100644 --- a/app/views/shared/tokens/_scopes_list.html.haml +++ b/app/views/shared/tokens/_scopes_list.html.haml @@ -1,10 +1,13 @@ -- if @application.scopes.present? - %tr - %td - Scopes - %td - %ul.scopes-list.append-bottom-0 - - @application.scopes.each do |scope| - %li - %span.scope-name= scope - = "(#{t(scope, scope: [:doorkeeper, :scopes])})" +- token = local_assigns.fetch(:token) + +- return unless token.scopes.present? + +%tr + %td + Scopes + %td + %ul.scopes-list.append-bottom-0 + - token.scopes.each do |scope| + %li + %span.scope-name= scope + = "(#{t(scope, scope: [:doorkeeper, :scopes])})" diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index b64413cda12..f251c0dd25a 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -47,36 +47,31 @@ describe Gitlab::Auth, lib: true do project.create_drone_ci_service(active: true) project.drone_ci_service.update(token: 'token') - ip = 'ip' - - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'drone-ci-token') - expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'drone-ci-token') + expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities)) end it 'recognizes master passwords' do user = create(:user, password: 'password') - ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username) - expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username) + expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)) end it 'recognizes user lfs tokens' do user = create(:user) - ip = 'ip' token = Gitlab::LfsToken.new(user).token - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username) - expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username) + expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities)) end it 'recognizes deploy key lfs tokens' do key = create(:deploy_key) - ip = 'ip' token = Gitlab::LfsToken.new(key).token - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: "lfs+deploy-key-#{key.id}") - expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}") + expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities)) end context "while using OAuth tokens as passwords" do @@ -84,20 +79,18 @@ describe Gitlab::Auth, lib: true do user = create(:user) application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") - ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2') - expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'oauth2') + expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities)) end it 'fails for OAuth tokens with other scopes' do user = create(:user) application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "read_user") - ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: 'oauth2') - expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, nil)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'oauth2') + expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil)) end end @@ -105,28 +98,25 @@ describe Gitlab::Auth, lib: true do it 'succeeds for personal access tokens with the `api` scope' do user = create(:user) personal_access_token = create(:personal_access_token, user: user, scopes: ['api']) - ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.email) - expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :personal_token, full_authentication_abilities)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.email) + expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :personal_token, full_authentication_abilities)) end it 'fails for personal access tokens with other scopes' do user = create(:user) personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) - ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: user.email) - expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, nil)) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: user.email) + expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil)) end end it 'returns double nil for invalid credentials' do login = 'foo' - ip = 'ip' - expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: login) - expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new) end end From b303948ff549ce57d3b6985c2c366dfcdc5a2ca3 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 5 Dec 2016 22:55:53 +0530 Subject: [PATCH 195/386] Convert AccessTokenValidationService into a class. - Previously, AccessTokenValidationService was a module, and all its public methods accepted a token. It makes sense to convert it to a class which accepts a token during initialization. - Also rename the `sufficient_scope?` method to `include_any_scope?` - Based on feedback from @rymai --- .../access_token_validation_service.rb | 38 +++++++++---------- lib/api/api_guard.rb | 4 +- lib/gitlab/auth.rb | 2 +- .../access_token_validation_service_spec.rb | 14 +++---- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb index 69449f3a445..ddaaed90e5b 100644 --- a/app/services/access_token_validation_service.rb +++ b/app/services/access_token_validation_service.rb @@ -1,34 +1,32 @@ -module AccessTokenValidationService +AccessTokenValidationService = Struct.new(:token) do # Results: VALID = :valid EXPIRED = :expired REVOKED = :revoked INSUFFICIENT_SCOPE = :insufficient_scope - class << self - def validate(token, scopes: []) - if token.expired? - return EXPIRED + def validate(scopes: []) + if token.expired? + return EXPIRED - elsif token.revoked? - return REVOKED + elsif token.revoked? + return REVOKED - elsif !self.sufficient_scope?(token, scopes) - return INSUFFICIENT_SCOPE + elsif !self.include_any_scope?(scopes) + return INSUFFICIENT_SCOPE - else - return VALID - end + else + return VALID end + end - # True if the token's scope contains any of the required scopes. - def sufficient_scope?(token, required_scopes) - if required_scopes.blank? - true - else - # Check whether the token is allowed access to any of the required scopes. - Set.new(required_scopes).intersection(Set.new(token.scopes)).present? - end + # True if the token's scope contains any of the passed scopes. + def include_any_scope?(scopes) + if scopes.blank? + true + else + # Check whether the token is allowed access to any of the required scopes. + Set.new(scopes).intersection(Set.new(token.scopes)).present? end end end diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index 563224a580f..df6db140d0e 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -47,7 +47,7 @@ module API access_token = find_access_token return nil unless access_token - case AccessTokenValidationService.validate(access_token, scopes: scopes) + case AccessTokenValidationService.new(access_token).validate(scopes: scopes) when AccessTokenValidationService::INSUFFICIENT_SCOPE raise InsufficientScopeError.new(scopes) @@ -96,7 +96,7 @@ module API access_token = PersonalAccessToken.active.find_by_token(token_string) return unless access_token - if AccessTokenValidationService.sufficient_scope?(access_token, scopes) + if AccessTokenValidationService.new(access_token).include_any_scope?(scopes) User.find(access_token.user_id) end end diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index c21afaa1551..2879a4d2f5d 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -119,7 +119,7 @@ module Gitlab end def token_has_scope?(token) - AccessTokenValidationService.sufficient_scope?(token, ['api']) + AccessTokenValidationService.new(token).include_any_scope?(['api']) end def lfs_token_check(login, password) diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb index 332e745aa36..87f093ee8ce 100644 --- a/spec/services/access_token_validation_service_spec.rb +++ b/spec/services/access_token_validation_service_spec.rb @@ -1,41 +1,41 @@ require 'spec_helper' describe AccessTokenValidationService, services: true do - describe ".sufficient_scope?" do + describe ".include_any_scope?" do it "returns true if the required scope is present in the token's scopes" do token = double("token", scopes: [:api, :read_user]) - expect(described_class.sufficient_scope?(token, [:api])).to be(true) + expect(described_class.new(token).include_any_scope?([:api])).to be(true) end it "returns true if more than one of the required scopes is present in the token's scopes" do token = double("token", scopes: [:api, :read_user, :other_scope]) - expect(described_class.sufficient_scope?(token, [:api, :other_scope])).to be(true) + expect(described_class.new(token).include_any_scope?([:api, :other_scope])).to be(true) end it "returns true if the list of required scopes is an exact match for the token's scopes" do token = double("token", scopes: [:api, :read_user, :other_scope]) - expect(described_class.sufficient_scope?(token, [:api, :read_user, :other_scope])).to be(true) + expect(described_class.new(token).include_any_scope?([:api, :read_user, :other_scope])).to be(true) end it "returns true if the list of required scopes contains all of the token's scopes, in addition to others" do token = double("token", scopes: [:api, :read_user]) - expect(described_class.sufficient_scope?(token, [:api, :read_user, :other_scope])).to be(true) + expect(described_class.new(token).include_any_scope?([:api, :read_user, :other_scope])).to be(true) end it 'returns true if the list of required scopes is blank' do token = double("token", scopes: []) - expect(described_class.sufficient_scope?(token, [])).to be(true) + expect(described_class.new(token).include_any_scope?([])).to be(true) end it "returns false if there are no scopes in common between the required scopes and the token scopes" do token = double("token", scopes: [:api, :read_user]) - expect(described_class.sufficient_scope?(token, [:other_scope])).to be(false) + expect(described_class.new(token).include_any_scope?([:other_scope])).to be(false) end end end From 5becbe2495850923604c71b4c807666ea94819b3 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 5 Dec 2016 22:58:19 +0530 Subject: [PATCH 196/386] Rename the `token_has_scope?` method. `valid_api_token?` is a better name. Scopes are just (potentially) one facet of a "valid" token. --- lib/gitlab/auth.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 2879a4d2f5d..8dda65c71ef 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -111,14 +111,14 @@ module Gitlab end def valid_oauth_token?(token) - token && token.accessible? && token_has_scope?(token) + token && token.accessible? && valid_api_token?(token) end def valid_personal_access_token?(token, user) - token && token.user == user && token_has_scope?(token) + token && token.user == user && valid_api_token?(token) end - def token_has_scope?(token) + def valid_api_token?(token) AccessTokenValidationService.new(token).include_any_scope?(['api']) end From eb434b15ebbc7d0b7ed79bb2daa45601e3c918ca Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 16 Dec 2016 14:57:09 +0530 Subject: [PATCH 197/386] Make `ChangePersonalAccessTokensDefaultBackToEmptyArray` a "post" migration. If we leave this as a regular migration, we could have the following flow: 1. Application knows nothing about scopes. 2. First migration runs, all existing personal access tokens have `api` scope 3. Application still knows nothing about scopes. 4. Second migration runs, all tokens created after this point have no scope 5. Application still knows nothing about scopes. 6. Tokens created at this time _should have the API scope, but instead have no scope_ 7. Application code is reloaded, application knows about scopes 8. Tokens created after this point only have no scope if the user deliberately chooses to have no scopes. Point #6 is the problem here. To avoid this, we move the second migration to a "post" migration, which runs after the application code is deployed/reloaded. --- ...ge_personal_access_tokens_default_back_to_empty_array.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename db/{migrate => post_migrate}/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb (72%) diff --git a/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb b/db/post_migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb similarity index 72% rename from db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb rename to db/post_migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb index c8ceb116b8a..7df561d82dd 100644 --- a/db/migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb +++ b/db/post_migrate/20160824121037_change_personal_access_tokens_default_back_to_empty_array.rb @@ -1,6 +1,8 @@ # The default needs to be `[]`, but all existing access tokens need to have `scopes` set to `['api']`. -# It's easier to achieve this by adding the column with the `['api']` default, and then changing the default to -# `[]`. +# It's easier to achieve this by adding the column with the `['api']` default (regular migration), and +# then changing the default to `[]` (in this post-migration). +# +# Details: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951#note_19721973 class ChangePersonalAccessTokensDefaultBackToEmptyArray < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers From 731bf34223efc9e0f28b1c3e77e1dbbf8bd5e601 Mon Sep 17 00:00:00 2001 From: Munken Date: Fri, 16 Dec 2016 11:02:36 +0000 Subject: [PATCH 198/386] Clearer comment as to why the procedure is needed --- vendor/assets/javascripts/katex.js | 2 +- vendor/assets/stylesheets/katex.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/assets/javascripts/katex.js b/vendor/assets/javascripts/katex.js index 2ce3a5e8d4d..beb31ca6a7e 100644 --- a/vendor/assets/javascripts/katex.js +++ b/vendor/assets/javascripts/katex.js @@ -31,7 +31,7 @@ /* Here is how to build a version of KaTeX that works with gitlab. - The problem is that the procedure for changing font location doesn't work for ''. + The problem is that the standard procedure for changing font location doesn't work for the empty string. 1. Clone KaTeX. Anything later than 4fb9445a9 (is merged into master) will do. 2. make (requires node) diff --git a/vendor/assets/stylesheets/katex.css b/vendor/assets/stylesheets/katex.css index 2d0b4635ccf..3e62df2329c 100644 --- a/vendor/assets/stylesheets/katex.css +++ b/vendor/assets/stylesheets/katex.css @@ -31,7 +31,7 @@ SOFTWARE. /* Here is how to build a version of KaTeX that works with gitlab. - The problem is that the procedure for changing font location doesn't work for ''. + The problem is that the standard procedure for changing font location doesn't work for the empty string. 1. Clone KaTeX. Anything later than 4fb9445a9 (is merged into master) will do. 2. make (requires node) From 3b4e81eed50dac796de5720b9975125dc8de609b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 16 Dec 2016 12:12:53 +0200 Subject: [PATCH 199/386] BB importer: Milestone importer --- lib/bitbucket/representation/issue.rb | 4 ++++ lib/gitlab/bitbucket_import/importer.rb | 2 ++ spec/lib/bitbucket/representation/issue_spec.rb | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index ffe8a65d839..3af731753d1 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -27,6 +27,10 @@ module Bitbucket raw['title'] end + def milestone + raw.dig('milestone', 'name') + end + def created_at raw['created_on'] end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 567f2b314aa..53c95ea4079 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -67,6 +67,7 @@ module Gitlab description += issue.description label_name = issue.kind + milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil issue = project.issues.create!( iid: issue.iid, @@ -74,6 +75,7 @@ module Gitlab description: description, state: issue.state, author_id: gitlab_user_id(project, issue.author), + milestone: milestone, created_at: issue.created_at, updated_at: issue.updated_at ) diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb index e1f3419c77e..9a195bebd31 100644 --- a/spec/lib/bitbucket/representation/issue_spec.rb +++ b/spec/lib/bitbucket/representation/issue_spec.rb @@ -9,6 +9,12 @@ describe Bitbucket::Representation::Issue do it { expect(described_class.new('kind' => 'bug').kind).to eq('bug') } end + describe '#milestone' do + it { expect(described_class.new({ 'milestone' => { 'name' => '1.0' } }).milestone).to eq('1.0') } + it { expect(described_class.new({}).milestone).to be_nil } + end + + describe '#author' do it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' } }).author).to eq('Ben') } it { expect(described_class.new({}).author).to be_nil } From 8feba01708f10d0ea6f9bb4d49b4cbf05b47ab9b Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 16 Dec 2016 11:29:16 +0000 Subject: [PATCH 200/386] Fix specs in Ruby 2.1 Ruby 2.1 requires a basename argument to `Tempfile.open`, so just call it something that makes sense in context for the spec. --- spec/lib/gitlab/middleware/multipart_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb index c79c6494576..ab1ab22795c 100644 --- a/spec/lib/gitlab/middleware/multipart_spec.rb +++ b/spec/lib/gitlab/middleware/multipart_spec.rb @@ -7,7 +7,7 @@ describe Gitlab::Middleware::Multipart do let(:middleware) { described_class.new(app) } it 'opens top-level files' do - Tempfile.open do |tempfile| + Tempfile.open('top-level') do |tempfile| env = post_env({ 'file' => tempfile.path }, { 'file.name' => 'filename' }, Gitlab::Workhorse.secret, 'gitlab-workhorse') expect(app).to receive(:call) do |env| @@ -33,7 +33,7 @@ describe Gitlab::Middleware::Multipart do end it 'opens files one level deep' do - Tempfile.open do |tempfile| + Tempfile.open('one-level') do |tempfile| in_params = { 'user' => { 'avatar' => { '.name' => 'filename' } } } env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') @@ -48,7 +48,7 @@ describe Gitlab::Middleware::Multipart do end it 'opens files two levels deep' do - Tempfile.open do |tempfile| + Tempfile.open('two-levels') do |tempfile| in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => 'filename' } } } } env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') From 18b65cb8e07548c67056fe7994f1cee6da4de08e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 12:29:29 +0100 Subject: [PATCH 201/386] Fix rubocop --- spec/models/project_services/chat_notification_service_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb index b4fb1cd9ed9..c98e7ee14fd 100644 --- a/spec/models/project_services/chat_notification_service_spec.rb +++ b/spec/models/project_services/chat_notification_service_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe ChatNotificationService, models: true do describe "Associations" do - before do allow(subject).to receive(:activated?).and_return(true) end From ca16a6bdf8feb92ae2b24cff86143fdaf668ce7d Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 16 Dec 2016 11:30:22 +0000 Subject: [PATCH 202/386] Fix broken test --- spec/features/commits_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 23a504ff965..8f561c8f90b 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -107,7 +107,7 @@ describe 'Commits' do describe 'Cancel build' do it 'cancels build' do visit ci_status_path(pipeline) - click_on 'Cancel' + find('a.btn[title="Cancel"]').click expect(page).to have_content 'canceled' end end From 595da33d738041cf0b2c28a87989aa271f5019cc Mon Sep 17 00:00:00 2001 From: Dimitrie Hoekstra Date: Fri, 16 Dec 2016 13:05:44 +0100 Subject: [PATCH 203/386] fixed scss linting issue --- app/assets/stylesheets/pages/labels.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 703a429d63c..d129eb12a45 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -98,7 +98,7 @@ } .label { - padding: 8px 9px 9px 9px; + padding: 8px 9px 9px; font-size: 14px; } } From c945a0a7141ddf80e58e821178195cc48b8143f0 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 16 Dec 2016 13:24:03 +0100 Subject: [PATCH 204/386] Pass variables from deployment project services to CI runner This commit introduces the concept of deployment variables - variables that are collected from deployment services and passed to CI runner during a deployment build. Deployment services specify the variables by overriding "predefined_variables" method. This commit also configures variables for KubernetesService --- app/models/ci/build.rb | 3 +- app/models/project.rb | 6 ++++ .../project_services/deployment_service.rb | 4 +++ .../project_services/kubernetes_service.rb | 10 ++++++ .../expose-deployment-variables.yml | 4 +++ doc/ci/variables/README.md | 15 +++++++++ doc/project_services/kubernetes.md | 11 +++++++ spec/models/build_spec.rb | 11 +++++++ .../kubernetes_service_spec.rb | 33 +++++++++++++++++++ spec/models/project_spec.rb | 20 +++++++++++ 10 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/expose-deployment-variables.yml diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index fdbf28a1d68..591aba6bdc9 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -155,7 +155,7 @@ module Ci end def has_environment? - self.environment.present? + environment.present? end def starts_environment? @@ -221,6 +221,7 @@ module Ci variables += pipeline.predefined_variables variables += runner.predefined_variables if runner variables += project.container_registry_variables + variables += project.deployment_variables if has_environment? variables += yaml_variables variables += user_variables variables += project.secret_variables diff --git a/app/models/project.rb b/app/models/project.rb index 2c726cfc5df..5f8058dac60 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1229,6 +1229,12 @@ class Project < ActiveRecord::Base end end + def deployment_variables + return [] unless deployment_service + + deployment_service.predefined_variables + end + def append_or_update_attribute(name, value) old_values = public_send(name.to_s) diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb index 55e98c31251..da6be9dd7b7 100644 --- a/app/models/project_services/deployment_service.rb +++ b/app/models/project_services/deployment_service.rb @@ -8,4 +8,8 @@ class DeploymentService < Service def supported_events [] end + + def predefined_variables + [] + end end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 80ae1191108..f5fbf8b353b 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -83,6 +83,16 @@ class KubernetesService < DeploymentService { success: false, result: err } end + def predefined_variables + variables = [ + { key: 'KUBE_URL', value: api_url, public: true }, + { key: 'KUBE_TOKEN', value: token, public: false }, + { key: 'KUBE_NAMESPACE', value: namespace, public: true } + ] + variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true } if ca_pem.present? + variables + end + private def build_kubeclient(api_path = '/api', api_version = 'v1') diff --git a/changelogs/unreleased/expose-deployment-variables.yml b/changelogs/unreleased/expose-deployment-variables.yml new file mode 100644 index 00000000000..7663d5b6ae5 --- /dev/null +++ b/changelogs/unreleased/expose-deployment-variables.yml @@ -0,0 +1,4 @@ +--- +title: Pass variables from deployment project services to CI runner +merge_request: 8107 +author: diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index eb540a50606..baa5fc67816 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -13,6 +13,7 @@ this order: 1. [Secret variables](#secret-variables) 1. YAML-defined [job-level variables](../yaml/README.md#job-variables) 1. YAML-defined [global variables](../yaml/README.md#variables) +1. [Deployment variables](#deployment-variables) 1. [Predefined variables](#predefined-variables-environment-variables) (are the lowest in the chain) @@ -148,6 +149,20 @@ Secret variables can be added by going to your project's Once you set them, they will be available for all subsequent builds. +## Deployment variables + +>**Note:** +This feature requires GitLab CI 8.15 or higher. + +[Project services](../../project_services/project_services.md) that are +responsible for deployment configuration may define their own variables that +are set in the build environment. These variables are only defined for +[deployment builds](../environments.md). Please consult the documentation of +the project services that you are using to learn which variables they define. + +An example project service that defines deployment variables is +[Kubernetes Service](../../project_services/kubernetes.md). + ## Debug tracing > Introduced in GitLab Runner 1.7. diff --git a/doc/project_services/kubernetes.md b/doc/project_services/kubernetes.md index cb577b608b4..fda364b864e 100644 --- a/doc/project_services/kubernetes.md +++ b/doc/project_services/kubernetes.md @@ -36,3 +36,14 @@ to create one. You can also view or create service tokens in the Fill in the service token and namespace according to the values you just got. If the API is using a self-signed TLS certificate, you'll also need to include the `ca.crt` contents as the `Custom CA bundle`. + +## Deployment variables + +The Kubernetes service exposes following +[deployment variables](../ci/variables/README.md#deployment-variables) in the +GitLab CI build environment: + +- `KUBE_URL` - equal to the API URL +- `KUBE_TOKEN` +- `KUBE_NAMESPACE` +- `KUBE_CA_PEM` - only if a custom CA bundle was specified diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index d5f2ffcff59..6f1c2ae0fd8 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -506,6 +506,17 @@ describe Ci::Build, models: true do it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true }) } end + context 'when build is for a deployment' do + let(:deployment_variable) { { key: 'KUBERNETES_TOKEN', value: 'TOKEN', public: false } } + + before do + build.environment = 'production' + allow(project).to receive(:deployment_variables).and_return([deployment_variable]) + end + + it { is_expected.to include(deployment_variable) } + end + context 'returns variables in valid order' do before do allow(build).to receive(:predefined_variables) { ['predefined'] } diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index ffb92012b89..3603602e41d 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -123,4 +123,37 @@ describe KubernetesService, models: true do end end end + + describe '#predefined_variables' do + before do + subject.api_url = 'https://kube.domain.com' + subject.token = 'token' + subject.namespace = 'my-project' + subject.ca_pem = 'CA PEM DATA' + end + + it 'sets KUBE_URL' do + expect(subject.predefined_variables).to include( + { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true } + ) + end + + it 'sets KUBE_TOKEN' do + expect(subject.predefined_variables).to include( + { key: 'KUBE_TOKEN', value: 'token', public: false } + ) + end + + it 'sets KUBE_NAMESPACE' do + expect(subject.predefined_variables).to include( + { key: 'KUBE_NAMESPACE', value: 'my-project', public: true } + ) + end + + it 'sets KUBE_CA_PEM' do + expect(subject.predefined_variables).to include( + { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true } + ) + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 21ff238841e..c7d914a81f9 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1696,6 +1696,26 @@ describe Project, models: true do end end + describe '#deployment_variables' do + context 'when project has no deployment service' do + let(:project) { create(:empty_project) } + + it 'returns an empty array' do + expect(project.deployment_variables).to eq [] + end + end + + context 'when project has a deployment service' do + let(:project) { create(:kubernetes_project) } + + it 'returns variables from this service' do + expect(project.deployment_variables).to include( + { key: 'KUBE_TOKEN', value: project.kubernetes_service.token, public: false } + ) + end + end + end + def enable_lfs allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) end From df6c1b84a842d6dc54b27e396b60ffd4d7723c4a Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 16 Dec 2016 12:29:58 +0000 Subject: [PATCH 205/386] Prevent enviroment table to overflow when name has underscores Fix error in linter Add changelog entry --- .../components/environment_item.js.es6 | 2 +- app/assets/stylesheets/pages/environments.scss | 14 ++++++++++---- .../unreleased/25207-text-overflow-env-table.yml | 4 ++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/25207-text-overflow-env-table.yml diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 2e046a60146..4674d5202e6 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -449,7 +449,7 @@ - + diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 92dd9885ab8..3d60426de01 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -30,19 +30,25 @@ display: table-cell; } + .environments-name, .environments-commit, .environments-actions { width: 20%; } - .environments-deploy, - .environments-build, .environments-date { width: 10%; } - .environments-name { - width: 30%; + .environments-deploy, + .environments-build { + width: 15%; + } + + .environment-name, + .environments-build-cell, + .deployment-column { + word-break: break-all; } .deployment-column { diff --git a/changelogs/unreleased/25207-text-overflow-env-table.yml b/changelogs/unreleased/25207-text-overflow-env-table.yml new file mode 100644 index 00000000000..69348281a50 --- /dev/null +++ b/changelogs/unreleased/25207-text-overflow-env-table.yml @@ -0,0 +1,4 @@ +--- +title: Prevent enviroment table to overflow when name has underscores +merge_request: 8142 +author: From 5dcd01aa96f26ff98311d0415d659d0b14e6c632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 14:31:07 +0100 Subject: [PATCH 206/386] Bump gitlab-shell to 4.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a compatibility issue with Git 2.11: - Issue: gitlab-org/gitlab-ce#25301 - gitlab-shell MR: gitlab-org/gitlab-shell!112 - CE MR: gitlab-org/gitlab-ce!7967 - EE MR: gitlab-org/gitlab-ee!964 Signed-off-by: Rémy Coutable --- GITLAB_SHELL_VERSION | 2 +- doc/update/8.14-to-8.15.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index ee74734aa22..627a3f43a64 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -4.1.0 +4.1.1 diff --git a/doc/update/8.14-to-8.15.md b/doc/update/8.14-to-8.15.md index 4eacab0c890..8d4bfd913bd 100644 --- a/doc/update/8.14-to-8.15.md +++ b/doc/update/8.14-to-8.15.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-15-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v4.1.0 +sudo -u git -H git checkout v4.1.1 ``` ### 6. Update gitlab-workhorse From b0501c34c478a528f2aa7633dfa6d13e9c61af64 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 16 Dec 2016 15:40:38 +0200 Subject: [PATCH 207/386] BB importer: address review comment --- .../importing/import_projects_from_bitbucket.md | 2 +- lib/gitlab/bitbucket_import/importer.rb | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md index 935d6288f3b..9e1e3c7ba08 100644 --- a/doc/workflow/importing/import_projects_from_bitbucket.md +++ b/doc/workflow/importing/import_projects_from_bitbucket.md @@ -20,7 +20,7 @@ It takes just a few steps to import your existing Bitbucket projects to GitLab. ![Import projects](bitbucket_importer/bitbucket_import_select_project.png) -A new GitLab project will be created with your imported data. Keep in mind that if you want to Bitbucket users +A new GitLab project will be created with your imported data. Keep in mind that if you want Bitbucket users to be linked to GitLab user you have to have all of them in GitLab in advance. They will be matched by their BitBucket username. ### Note diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 53c95ea4079..63a4407cb78 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -28,6 +28,7 @@ module Gitlab def handle_errors return unless errors.any? + project.update_column(:import_error, { message: 'The remote data could not be fully imported.', errors: errors @@ -35,15 +36,12 @@ module Gitlab end def gitlab_user_id(project, username) - if username - user = find_user(username) - (user && user.id) || project.creator_id - else - project.creator_id - end + user = find_user(username) + user.try(:id) || project.creator_id end def find_user(username) + return nil unless username User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username) end From 27f271ee1ed977f8070f528f1d5e21ad577f5409 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 16 Dec 2016 14:54:23 +0100 Subject: [PATCH 208/386] Refactor Bitbucket import docs --- doc/README.md | 2 +- doc/integration/bitbucket.md | 18 ++--- .../img/bitbucket_oauth_settings_page.png | Bin 30275 -> 5607 bytes .../bitbucket_import_grant_access.png | Bin 30083 -> 0 bytes .../bitbucket_import_new_project.png | Bin 16502 -> 0 bytes .../bitbucket_import_select_bitbucket.png | Bin 46606 -> 0 bytes .../bitbucket_import_select_project.png | Bin 15288 -> 0 bytes .../img/bitbucket_import_grant_access.png | Bin 0 -> 7248 bytes .../img/bitbucket_import_new_project.png | Bin 0 -> 1316 bytes .../img/bitbucket_import_select_project.png | Bin 0 -> 8688 bytes ...rojects_from_github_select_auth_method.png | Bin 17613 -> 17612 bytes .../import_projects_from_bitbucket.md | 76 +++++++++++++----- 12 files changed, 65 insertions(+), 31 deletions(-) delete mode 100644 doc/workflow/importing/bitbucket_importer/bitbucket_import_grant_access.png delete mode 100644 doc/workflow/importing/bitbucket_importer/bitbucket_import_new_project.png delete mode 100644 doc/workflow/importing/bitbucket_importer/bitbucket_import_select_bitbucket.png delete mode 100644 doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png create mode 100644 doc/workflow/importing/img/bitbucket_import_grant_access.png create mode 100644 doc/workflow/importing/img/bitbucket_import_new_project.png create mode 100644 doc/workflow/importing/img/bitbucket_import_select_project.png diff --git a/doc/README.md b/doc/README.md index eba1e9845b1..a60a5359540 100644 --- a/doc/README.md +++ b/doc/README.md @@ -8,7 +8,7 @@ - [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab. - [Container Registry](user/project/container_registry.md) Learn how to use GitLab Container Registry. - [GitLab basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab. -- [Importing to GitLab](workflow/importing/README.md). +- [Importing to GitLab](workflow/importing/README.md) Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab. - [Importing and exporting projects between instances](user/project/settings/import_export.md). - [Markdown](user/markdown.md) GitLab's advanced formatting system. - [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab. diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 9cdb101f457..5df6e103f42 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -18,8 +18,10 @@ Bitbucket.org. ## Bitbucket OmniAuth provider > **Note:** -Make sure to first follow the [Initial OmniAuth configuration][init-oauth] -before proceeding with setting up the Bitbucket integration. +GitLab 8.15 significantly simplified the way to integrate Bitbucket.org with +GitLab. You are encouraged to upgrade your GitLab instance if you haven't done +already. If you're using GitLab 8.14 and below, [use the previous integration +docs][bb-old]. To enable the Bitbucket OmniAuth provider you must register your application with Bitbucket.org. Bitbucket will generate an application ID and secret key for @@ -111,16 +113,12 @@ well, the user will be returned to GitLab and will be signed in. ## Bitbucket project import -You should be able to see the "Import projects from Bitbucket" option on the New Project page -enabled. - -## Acknowledgements - -Special thanks to the writer behind the following article: - -- http://stratus3d.com/blog/2015/09/06/migrating-from-bitbucket-to-local-gitlab-server/ +Once the above configuration is set up, you can use Bitbucket to sign into +GitLab and [start importing your projects][bb-import]. [init-oauth]: omniauth.md#initial-omniauth-configuration +[bb-import]: ../workflow/importing/import_projects_from_bitbucket.md +[bb-old]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/doc/integration/bitbucket.md [bitbucket-docs]: https://confluence.atlassian.com/bitbucket/use-the-ssh-protocol-with-bitbucket-cloud-221449711.html#UsetheSSHprotocolwithBitbucketCloud-KnownhostorBitbucket%27spublickeyfingerprints [reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure [restart GitLab]: ../administration/restart_gitlab.md#installations-from-source diff --git a/doc/integration/img/bitbucket_oauth_settings_page.png b/doc/integration/img/bitbucket_oauth_settings_page.png index 24acc6e1f5acee975aba3fcff8df9a4bbfb8f5dd..21ce82a6074e2a5b1d52331a5567634a8c5c7914 100644 GIT binary patch literal 5607 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 literal 30275 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 E0sRnX_5c6? diff --git a/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png deleted file mode 100644 index 1a5661de75d556dea016fe86a67ca043a7c2cd93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15288 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ⅈsCu>v!UGJEX5#;mOeh$3qFC_3x%oU$!|p+ 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 aEn<9j5s)&ia`UGUM_xunx>V9M=)V90=XCM_ literal 0 HcmV?d00001 diff --git a/doc/workflow/importing/img/bitbucket_import_new_project.png b/doc/workflow/importing/img/bitbucket_import_new_project.png new file mode 100644 index 0000000000000000000000000000000000000000..8ed528c2f09bb5d89d7f9c8e0dc34999e25f6906 GIT binary patch literal 1316 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 Z#Q*Fcy(J-O&-vQ}WhKE9MPi2E{tsBHW1Rp1 literal 0 HcmV?d00001 diff --git a/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png b/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png index f50d92669916e0a250083ee0bf792fa16fd9fe50..1ccb38a815e486a44a06d249b0f332982068fc3a 100644 GIT binary patch delta 16807 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 delta 16730 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 diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md index 935d6288f3b..fbbbc7f4a72 100644 --- a/doc/workflow/importing/import_projects_from_bitbucket.md +++ b/doc/workflow/importing/import_projects_from_bitbucket.md @@ -1,27 +1,63 @@ # Import your project from Bitbucket to GitLab -It takes just a few steps to import your existing Bitbucket projects to GitLab. But keep in mind that it is possible only if Bitbucket support is enabled on your GitLab instance. You can read more about Bitbucket support [here](../../integration/bitbucket.md). +Import your projects from Bitbucket to GitLab with minimal effort. -* Sign in to GitLab.com and go to your dashboard +## Overview -* Click on "New project" +>**Note:** +The [Bitbucket integration][bb-import] must be first enabled in order to be +able to import your projects from Bitbucket. Ask your GitLab administrator +to enable this if not already. -![New project in GitLab](bitbucket_importer/bitbucket_import_new_project.png) +- At its current state, the Bitbucket importer can import: + - the repository description (GitLab 7.7+) + - the Git repository data (GitLab 7.7+) + - the issues (GitLab 7.7+) + - the pull requests (GitLab 8.4+) + - the wiki pages (GitLab 8.4+) + - the milestones (GitLab 8.7+) + - the labels (GitLab 8.7+) + - the release note descriptions (GitLab 8.12+) +- References to pull requests and issues are preserved (GitLab 8.7+) +- Repository public access is retained. If a repository is private in Bitbucket + it will be created as private in GitLab as well. -* Click on the "Bitbucket" button - -![Bitbucket](bitbucket_importer/bitbucket_import_select_bitbucket.png) - -* Grant GitLab access to your Bitbucket account - -![Grant access](bitbucket_importer/bitbucket_import_grant_access.png) - -* Click on the projects that you'd like to import or "Import all projects" - -![Import projects](bitbucket_importer/bitbucket_import_select_project.png) - -A new GitLab project will be created with your imported data. Keep in mind that if you want to Bitbucket users -to be linked to GitLab user you have to have all of them in GitLab in advance. They will be matched by their BitBucket username. - -### Note Milestones and wiki pages are not imported from Bitbucket. + +## How it works + +When issues/pull requests are being imported, the Bitbucket importer tries to find +the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this +to work, the Bitbucket author/assignee should have signed in beforehand in GitLab +and [**associated their Bitbucket account**][social sign-in]. If the user is not +found in GitLab's database, the project creator (most of the times the current +user that started the import process) is set as the author, but a reference on +the issue about the original Bitbucket author is kept. + +The importer will create any new namespaces (groups) if they don't exist or in +the case the namespace is taken, the repository will be imported under the user's +namespace that started the import process. + +## Importing your Bitbucket repositories + +1. Sign in to GitLab and go to your dashboard. +1. Click on **New project**. + + ![New project in GitLab](img/bitbucket_import_new_project.png) + +1. Click on the "Bitbucket" button + + ![Bitbucket](img/import_projects_from_github_new_project_page.png) + +1. Grant GitLab access to your Bitbucket account + + ![Grant access](img/bitbucket_import_grant_access.png) + +1. Click on the projects that you'd like to import or **Import all projects**. + You can also select the namespace under which each project will be + imported. + + ![Import projects](img/bitbucket_import_select_project.png) + +[bb-import]: ../../integration/bitbucket.md +[social sign-in]: ../../user/profile/account/social_sign_in.md From 55f224e4e785d0e1515ac4a840e689cb6d9c7d24 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 17 Oct 2016 09:39:14 -0500 Subject: [PATCH 209/386] Add GitLab host to 2FA QR and manual info The two factor authentication account string only had the user's email address. This led to ambiguous entries in two factor code generating apps. This adds the GitLab host to the account string in the standard format (according to Google). No matter the code generator this change disambiguates the entry. --- app/controllers/profiles/two_factor_auths_controller.rb | 8 ++++++-- app/views/profiles/two_factor_auths/show.html.haml | 2 +- changelogs/unreleased/add_info_to_qr.yml | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/add_info_to_qr.yml diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 9eb75bb3891..18044ca78e2 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -22,6 +22,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController end @qr_code = build_qr_code + @account_string = account_string setup_u2f_registration end @@ -78,11 +79,14 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController private def build_qr_code - issuer = "#{issuer_host} | #{current_user.email}" - uri = current_user.otp_provisioning_uri(current_user.email, issuer: issuer) + uri = current_user.otp_provisioning_uri(account_string, issuer: issuer_host) RQRCode::render_qrcode(uri, :svg, level: :m, unit: 3) end + def account_string + "#{issuer_host}:#{current_user.email}" + end + def issuer_host Gitlab.config.gitlab.host end diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 03ac739ade5..558a1d56151 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -30,7 +30,7 @@ To add the entry manually, provide the following details to the application on your phone. %p.prepend-top-0.append-bottom-0 Account: - = current_user.email + = @account_string %p.prepend-top-0.append-bottom-0 Key: = current_user.otp_secret.scan(/.{4}/).join(' ') diff --git a/changelogs/unreleased/add_info_to_qr.yml b/changelogs/unreleased/add_info_to_qr.yml new file mode 100644 index 00000000000..a4b0354a9c9 --- /dev/null +++ b/changelogs/unreleased/add_info_to_qr.yml @@ -0,0 +1,4 @@ +--- +title: Add GitLab host to 2FA QR code and manual info +merge_request: 6941 +author: From cc1eb7fec5a225342992377495e4969fdf8adf45 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 16 Dec 2016 22:49:33 +0800 Subject: [PATCH 210/386] Use described_class Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20133505 --- spec/lib/gitlab/serialize/yaml_variables_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/lib/gitlab/serialize/yaml_variables_spec.rb b/spec/lib/gitlab/serialize/yaml_variables_spec.rb index 6d74f8c44d6..2691c3dc22f 100644 --- a/spec/lib/gitlab/serialize/yaml_variables_spec.rb +++ b/spec/lib/gitlab/serialize/yaml_variables_spec.rb @@ -2,8 +2,7 @@ require 'spec_helper' describe Gitlab::Serialize::YamlVariables do subject do - Gitlab::Serialize::YamlVariables.load( - Gitlab::Serialize::YamlVariables.dump(object)) + described_class.load(described_class.dump(object)) end let(:object) do From 1b4a244dbc8d1e5b79feb4f111ec6183afa1b413 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 16 Dec 2016 23:04:01 +0800 Subject: [PATCH 211/386] Rename to Gitlab::Serialize::Ci::Variables Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8088#note_20133176 --- app/models/ci/build.rb | 2 +- lib/gitlab/serialize/ci/variables.rb | 32 +++++++++++++++++++ lib/gitlab/serialize/yaml_variables.rb | 31 ------------------ .../variables_spec.rb} | 2 +- 4 files changed, 34 insertions(+), 33 deletions(-) create mode 100644 lib/gitlab/serialize/ci/variables.rb delete mode 100644 lib/gitlab/serialize/yaml_variables.rb rename spec/lib/gitlab/serialize/{yaml_variables_spec.rb => ci/variables_spec.rb} (89%) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 99f3f4711aa..0e30755b870 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -10,7 +10,7 @@ module Ci has_many :deployments, as: :deployable serialize :options - serialize :yaml_variables, Gitlab::Serialize::YamlVariables + serialize :yaml_variables, Gitlab::Serialize::Ci::Variables validates :coverage, numericality: true, allow_blank: true validates_presence_of :ref diff --git a/lib/gitlab/serialize/ci/variables.rb b/lib/gitlab/serialize/ci/variables.rb new file mode 100644 index 00000000000..0ca060cd95e --- /dev/null +++ b/lib/gitlab/serialize/ci/variables.rb @@ -0,0 +1,32 @@ +module Gitlab + module Serialize + module Ci + # This serializer could make sure our YAML variables' keys and values + # are always strings. This is more for legacy build data because + # from now on we convert them into strings before saving to database. + module Variables + extend self + + def load(string) + return unless string + + object = YAML.safe_load(string, [Symbol]) + + object.map(&Variables.method(:convert_key_value_to_string)) + end + + def dump(object) + YAML.dump(object) + end + + private + + def convert_key_value_to_string(variable) + variable[:key] = variable[:key].to_s + variable[:value] = variable[:value].to_s + variable + end + end + end + end +end diff --git a/lib/gitlab/serialize/yaml_variables.rb b/lib/gitlab/serialize/yaml_variables.rb deleted file mode 100644 index db1e7641c74..00000000000 --- a/lib/gitlab/serialize/yaml_variables.rb +++ /dev/null @@ -1,31 +0,0 @@ - -module Gitlab - module Serialize - # This serializer could make sure our YAML variables' keys and values - # are always strings. This is more for legacy build data because - # from now on we convert them into strings before saving to database. - module YamlVariables - extend self - - def load(string) - return unless string - - object = YAML.safe_load(string, [Symbol]) - - object.map(&YamlVariables.method(:convert_key_value_to_string)) - end - - def dump(object) - YAML.dump(object) - end - - private - - def convert_key_value_to_string(variable) - variable[:key] = variable[:key].to_s - variable[:value] = variable[:value].to_s - variable - end - end - end -end diff --git a/spec/lib/gitlab/serialize/yaml_variables_spec.rb b/spec/lib/gitlab/serialize/ci/variables_spec.rb similarity index 89% rename from spec/lib/gitlab/serialize/yaml_variables_spec.rb rename to spec/lib/gitlab/serialize/ci/variables_spec.rb index 2691c3dc22f..797baef640c 100644 --- a/spec/lib/gitlab/serialize/yaml_variables_spec.rb +++ b/spec/lib/gitlab/serialize/ci/variables_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Serialize::YamlVariables do +describe Gitlab::Serialize::Ci::Variables do subject do described_class.load(described_class.dump(object)) end From 445e83ebee1a224bd576db738f4bf597b37a5f6c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 16 Dec 2016 16:21:33 +0100 Subject: [PATCH 212/386] Use new generic image for project import --- ...rt_projects_from_github_new_project_page.png | Bin 11047 -> 0 bytes .../import_projects_from_new_project_page.png | Bin 0 -> 36821 bytes .../importing/import_projects_from_bitbucket.md | 2 +- .../importing/import_projects_from_github.md | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 doc/workflow/importing/img/import_projects_from_github_new_project_page.png create mode 100644 doc/workflow/importing/img/import_projects_from_new_project_page.png diff --git a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png deleted file mode 100644 index b23ade4480c4ee2ddd1a9f81cd03a6ba735a8e53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11047 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 diff --git a/doc/workflow/importing/img/import_projects_from_new_project_page.png b/doc/workflow/importing/img/import_projects_from_new_project_page.png new file mode 100644 index 0000000000000000000000000000000000000000..97ca30b20874d405b64fb181a54c5395da203542 GIT binary patch literal 36821 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 Date: Fri, 16 Dec 2016 16:34:58 +0100 Subject: [PATCH 213/386] Add detailed docs in yaml README [ci skip] --- doc/ci/yaml/README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 5e8d888e555..430952705e0 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1036,6 +1036,31 @@ variables: GIT_STRATEGY: none ``` +## Build stages attempts + +> Introduced in GitLab, it requires GitLab Runner v1.9+. + +You can set the number for attempts the running build will try to execute each +of the following stages: + +| Variable | Description | +|-------------------------|-------------| +| **GET_SOURCES_ATTEMPTS** | Number of attempts to fetch sources running a build | +| **ARTIFACT_DOWNLOAD_ATTEMPTS** | Number of attempts to download artifacts running a build | +| **RESTORE_CACHE_ATTEMPTS** | Number of attempts to restore the cache running a build | + +The default is one single attempt. + +Example: + +``` +variables: + GET_SOURCES_ATTEMPTS: "3" +``` + +You can set the them in the global [`variables`](#variables) section or the [`variables`](#job-variables) +section for individual jobs. + ## Shallow cloning > Introduced in GitLab 8.9 as an experimental feature. May change in future From 7985b52286237b3801fc112b8bf3841599931c23 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 16 Dec 2016 17:35:31 +0200 Subject: [PATCH 214/386] BB importer: Adressed more review comments --- changelogs/unreleased/bitbucket-oauth2.yml | 4 +++ lib/gitlab/bitbucket_import/importer.rb | 34 ++++++++++++---------- 2 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 changelogs/unreleased/bitbucket-oauth2.yml diff --git a/changelogs/unreleased/bitbucket-oauth2.yml b/changelogs/unreleased/bitbucket-oauth2.yml new file mode 100644 index 00000000000..97d82518b7b --- /dev/null +++ b/changelogs/unreleased/bitbucket-oauth2.yml @@ -0,0 +1,4 @@ +--- +title: Refactor Bitbucket importer to use BitBucket API Version 2 +merge_request: +author: diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 63a4407cb78..f3760640655 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -6,7 +6,7 @@ module Gitlab { title: 'proposal', color: '#69D100' }, { title: 'task', color: '#7F8C8D' }].freeze - attr_reader :project, :client, :errors + attr_reader :project, :client, :errors, :users def initialize(project) @project = project @@ -14,6 +14,7 @@ module Gitlab @formatter = Gitlab::ImportFormatter.new @labels = {} @errors = [] + @users = {} end def execute @@ -36,17 +37,18 @@ module Gitlab end def gitlab_user_id(project, username) - user = find_user(username) - user.try(:id) || project.creator_id + find_user_id(username) || project.creator_id end - def find_user(username) + def find_user_id(username) return nil unless username - User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username) - end - def existing_gitlab_user?(username) - username && find_user(username) + return users[username] if users.key?(username) + + users[username] = User.select(:id) + .joins(:identities) + .find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username) + .try(:id) end def repo @@ -58,16 +60,18 @@ module Gitlab create_labels + gitlab_issue = nil + client.issues(repo).each do |issue| begin description = '' - description += @formatter.author_line(issue.author) unless existing_gitlab_user?(issue.author) + description += @formatter.author_line(issue.author) unless find_user_id(issue.author) description += issue.description label_name = issue.kind milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil - issue = project.issues.create!( + gitlab_issue = project.issues.create!( iid: issue.iid, title: issue.title, description: description, @@ -81,9 +85,9 @@ module Gitlab errors << { type: :issue, iid: issue.iid, errors: e.message } end - issue.labels << @labels[label_name] + gitlab_issue.labels << @labels[label_name] - if issue.persisted? + if gitlab_issue.persisted? client.issue_comments(repo, issue.iid).each do |comment| # The note can be blank for issue service messages like "Changed title: ..." # We would like to import those comments as well but there is no any @@ -93,11 +97,11 @@ module Gitlab next unless comment.note.present? note = '' - note += @formatter.author_line(comment.author) unless existing_gitlab_user?(comment.author) + note += @formatter.author_line(comment.author) unless find_user_id(comment.author) note += comment.note begin - issue.notes.create!( + gitlab_issue.notes.create!( project: project, note: note, author_id: gitlab_user_id(project, comment.author), @@ -124,7 +128,7 @@ module Gitlab pull_requests.each do |pull_request| begin description = '' - description += @formatter.author_line(pull_request.author) unless existing_gitlab_user?(pull_request.author) + description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) description += pull_request.description merge_request = project.merge_requests.create( From 20e472d946d7cc4a2b9dd91264458b1c4ceb5ab6 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 16 Dec 2016 17:40:29 +0200 Subject: [PATCH 215/386] BB importer: Fix documantation --- doc/workflow/importing/import_projects_from_bitbucket.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md index f0b73ccbcd2..b6d47e5afa2 100644 --- a/doc/workflow/importing/import_projects_from_bitbucket.md +++ b/doc/workflow/importing/import_projects_from_bitbucket.md @@ -13,16 +13,14 @@ to enable this if not already. - the repository description (GitLab 7.7+) - the Git repository data (GitLab 7.7+) - the issues (GitLab 7.7+) + - the issue comments (GitLab 8.15+) - the pull requests (GitLab 8.4+) - - the wiki pages (GitLab 8.4+) - - the milestones (GitLab 8.7+) - - the labels (GitLab 8.7+) - - the release note descriptions (GitLab 8.12+) + - the pull request comments (GitLab 8.15+) + - the milestones (GitLab 8.15+) - References to pull requests and issues are preserved (GitLab 8.7+) - Repository public access is retained. If a repository is private in Bitbucket it will be created as private in GitLab as well. -Milestones and wiki pages are not imported from Bitbucket. ## How it works From e0eb86ee809aaad86be4ca1d985a5e67c0657a6f Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Tue, 22 Dec 2015 15:09:35 -0600 Subject: [PATCH 216/386] Add LDAP task to rename a provider Sometimes admins will change the LDAP configuration, not realizing that problems will occur if the user's LDAP identities are not also updated to use the new provider name. This task will give admins a single command to run to update identities and will prevent having to run multiple Rails console queries. --- changelogs/unreleased/ldap_maint_task.yml | 4 + doc/administration/raketasks/check.md | 23 +---- doc/administration/raketasks/ldap.md | 120 ++++++++++++++++++++++ doc/raketasks/README.md | 3 +- lib/tasks/gitlab/ldap.rake | 40 ++++++++ spec/tasks/gitlab/ldap_rake_spec.rb | 13 +++ 6 files changed, 181 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/ldap_maint_task.yml create mode 100644 doc/administration/raketasks/ldap.md create mode 100644 lib/tasks/gitlab/ldap.rake create mode 100644 spec/tasks/gitlab/ldap_rake_spec.rb diff --git a/changelogs/unreleased/ldap_maint_task.yml b/changelogs/unreleased/ldap_maint_task.yml new file mode 100644 index 00000000000..8acffba0ce5 --- /dev/null +++ b/changelogs/unreleased/ldap_maint_task.yml @@ -0,0 +1,4 @@ +--- +title: Add LDAP Rake task to rename a provider +merge_request: 2181 +author: diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md index d1d2fed4861..c8b5434c068 100644 --- a/doc/administration/raketasks/check.md +++ b/doc/administration/raketasks/check.md @@ -74,24 +74,5 @@ Example output: The LDAP check Rake task will test the bind_dn and password credentials (if configured) and will list a sample of LDAP users. This task is also -executed as part of the `gitlab:check` task, but can run independently -using the command below. - -**Omnibus Installation** - -``` -sudo gitlab-rake gitlab:ldap:check -``` - -**Source Installation** - -```bash -sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production -``` - -By default, the task will return a sample of 100 LDAP users. Change this -limit by passing a number to the check task: - -```bash -rake gitlab:ldap:check[50] -``` +executed as part of the `gitlab:check` task, but can run independently. +See [LDAP Rake Tasks - LDAP Check](ldap.md#check) for details. diff --git a/doc/administration/raketasks/ldap.md b/doc/administration/raketasks/ldap.md new file mode 100644 index 00000000000..91fc0133d56 --- /dev/null +++ b/doc/administration/raketasks/ldap.md @@ -0,0 +1,120 @@ +# LDAP Rake Tasks + +## Check + +The LDAP check Rake task will test the `bind_dn` and `password` credentials +(if configured) and will list a sample of LDAP users. This task is also +executed as part of the `gitlab:check` task, but can run independently +using the command below. + +**Omnibus Installation** + +``` +sudo gitlab-rake gitlab:ldap:check +``` + +**Source Installation** + +```bash +sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production +``` + +------ + +By default, the task will return a sample of 100 LDAP users. Change this +limit by passing a number to the check task: + +```bash +rake gitlab:ldap:check[50] +``` + +## Rename a provider + +If you change the LDAP server ID in `gitlab.yml` or `gitlab.rb` you will need +to update all user identities or users will be unable to sign in. Input the +old and new provider and this task will update all matching identities in the +database. + +`old_provider` and `new_provider` are derived from the prefix `ldap` plus the +LDAP server ID from the configuration file. For example, in `gitlab.yml` or +`gitlab.rb` you may see LDAP configuration like this: + +```yaml +main: + label: 'LDAP' + host: '_your_ldap_server' + port: 389 + uid: 'sAMAccountName' + ... +``` + +`main` is the LDAP server ID. Together, the unique provider is `ldapmain`. + +> **Warning**: If you input an incorrect new provider users will be unable +to sign in. If this happens, run the task again with the incorrect provider +as the `old_provider` and the correct provider as the `new_provider`. + +**Omnibus Installation** + +```bash +sudo gitlab-rake gitlab:ldap:rename_provider[old_provider,new_provider] +``` + +**Source Installation** + +```bash +bundle exec rake gitlab:ldap:rename_provider[old_provider,new_provider] RAILS_ENV=production +``` + +### Example + +Consider beginning with the default server ID `main` (full provider `ldapmain`). +If we change `main` to `mycompany`, the `new_provider` is `ldapmycompany`. +To rename all user identities run the following command: + +```bash +sudo gitlab-rake gitlab:ldap:rename_provider[ldapmain,ldapmycompany] +``` + +Example output: + +``` +100 users with provider 'ldapmain' will be updated to 'ldapmycompany'. +If the new provider is incorrect, users will be unable to sign in. +Do you want to continue (yes/no)? yes + +User identities were successfully updated +``` + +### Other options + +If you do not specify an `old_provider` and `new_provider` you will be prompted +for them: + +**Omnibus Installation** + +```bash +sudo gitlab-rake gitlab:ldap:rename_provider +``` + +**Source Installation** + +```bash +bundle exec rake gitlab:ldap:rename_provider RAILS_ENV=production +``` + +**Example output:** + +``` +What is the old provider? Ex. 'ldapmain': ldapmain +What is the new provider? Ex. 'ldapcustom': ldapmycompany +``` + +------ + +This tasks also accepts the `force` environment variable which will skip the +confirmation dialog: + +```bash +sudo gitlab-rake gitlab:ldap:rename_provider[old_provider,new_provider] force=yes +``` diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md index a49c43b8ef2..2b81ebc9c59 100644 --- a/doc/raketasks/README.md +++ b/doc/raketasks/README.md @@ -4,7 +4,8 @@ - [Check](check.md) - [Cleanup](cleanup.md) - [Features](features.md) -- [Maintenance](maintenance.md) and self-checks +- [LDAP Maintenance](../administration/raketasks/ldap.md) +- [General Maintenance](maintenance.md) and self-checks - [User management](user_management.md) - [Webhooks](web_hooks.md) - [Import](import.md) of git repositories in bulk diff --git a/lib/tasks/gitlab/ldap.rake b/lib/tasks/gitlab/ldap.rake new file mode 100644 index 00000000000..c66a2a263dc --- /dev/null +++ b/lib/tasks/gitlab/ldap.rake @@ -0,0 +1,40 @@ +namespace :gitlab do + namespace :ldap do + desc 'GitLab | LDAP | Rename provider' + task :rename_provider, [:old_provider, :new_provider] => :environment do |_, args| + old_provider = args[:old_provider] || + prompt('What is the old provider? Ex. \'ldapmain\': '.color(:blue)) + new_provider = args[:new_provider] || + prompt('What is the new provider ID? Ex. \'ldapcustom\': '.color(:blue)) + puts '' # Add some separation in the output + + identities = Identity.where(provider: old_provider) + identity_count = identities.count + + if identities.empty? + puts "Found no user identities with '#{old_provider}' provider." + puts 'Please check the provider name and try again.' + exit 1 + end + + plural_id_count = ActionController::Base.helpers.pluralize(identity_count, 'user') + + unless ENV['force'] == 'yes' + puts "#{plural_id_count} with provider '#{old_provider}' will be updated to '#{new_provider}'" + puts 'If the new provider is incorrect, users will be unable to sign in' + ask_to_continue + puts '' + end + + updated_count = identities.update_all(provider: new_provider) + + if updated_count == identity_count + puts 'User identities were successfully updated'.color(:green) + else + plural_updated_count = ActionController::Base.helpers.pluralize(updated_count, 'user') + puts 'Some user identities could not be updated'.color(:red) + puts "Successfully updated #{plural_updated_count} out of #{plural_id_count} total" + end + end + end +end diff --git a/spec/tasks/gitlab/ldap_rake_spec.rb b/spec/tasks/gitlab/ldap_rake_spec.rb new file mode 100644 index 00000000000..12d442b9820 --- /dev/null +++ b/spec/tasks/gitlab/ldap_rake_spec.rb @@ -0,0 +1,13 @@ +require 'rake_helper' + +describe 'gitlab:ldap:rename_provider rake task' do + it 'completes without error' do + Rake.application.rake_require 'tasks/gitlab/ldap' + stub_warn_user_is_not_gitlab + ENV['force'] = 'yes' + + create(:identity) # Necessary to prevent `exit 1` from the task. + + run_rake_task('gitlab:ldap:rename_provider', 'ldapmain', 'ldapfoo') + end +end From 0d3e24358b88ce41848c97f3ac37bc813074f260 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 30 Nov 2016 15:51:48 +0100 Subject: [PATCH 217/386] Create Slack Slash command service --- Gemfile | 2 +- Gemfile.lock | 4 +- app/models/project.rb | 1 + .../slack_slash_commands_service.rb | 49 +++++++++++++++++++ app/models/service.rb | 3 +- .../mattermost_slash_commands/_help.html.haml | 2 +- .../unreleased/zj-slack-slash-commands.yml | 4 ++ lib/gitlab/chat_commands/deploy.rb | 2 +- lib/gitlab/chat_commands/help.rb | 28 +++++++++++ .../chat_commands/presenters/mattermost.rb} | 0 spec/lib/gitlab/chat_commands/command_spec.rb | 26 +++++++++- .../chat_message/build_message_spec.rb | 8 +-- .../chat_message/issue_message_spec.rb | 10 ++-- .../chat_message/merge_message_spec.rb | 12 ++--- .../chat_message/note_message_spec.rb | 22 ++++----- .../chat_message/push_message_spec.rb | 26 +++++----- .../chat_message/wiki_page_message_spec.rb | 8 +-- 17 files changed, 156 insertions(+), 51 deletions(-) create mode 100644 app/models/project_services/slack_slash_commands_service.rb create mode 100644 changelogs/unreleased/zj-slack-slash-commands.yml create mode 100644 lib/gitlab/chat_commands/help.rb rename lib/{mattermost/presenter.rb => gitlab/chat_commands/presenters/mattermost.rb} (100%) diff --git a/Gemfile b/Gemfile index 5eb8c32b168..a59b874248b 100644 --- a/Gemfile +++ b/Gemfile @@ -170,7 +170,7 @@ gem 'gitlab-flowdock-git-hook', '~> 1.0.1' gem 'gemnasium-gitlab-service', '~> 0.2' # Slack integration -gem 'slack-notifier', '~> 1.2.0' +gem 'slack-notifier', '~> 1.5.1' # Asana integration gem 'asana', '~> 0.4.0' diff --git a/Gemfile.lock b/Gemfile.lock index 23e45ddc16f..f33b171e1d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -687,7 +687,7 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) - slack-notifier (1.2.1) + slack-notifier (1.5.1) slop (3.6.0) spinach (0.8.10) colorize @@ -957,7 +957,7 @@ DEPENDENCIES sidekiq-cron (~> 0.4.4) sidekiq-limit_fetch (~> 3.4) simplecov (= 0.12.0) - slack-notifier (~> 1.2.0) + slack-notifier (~> 1.5.1) spinach-rails (~> 0.2.1) spinach-rerun-reporter (~> 0.0.2) spring (~> 1.7.0) diff --git a/app/models/project.rb b/app/models/project.rb index 5d092ca42c2..7f3a4debfc6 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -96,6 +96,7 @@ class Project < ActiveRecord::Base has_one :gemnasium_service, dependent: :destroy has_one :mattermost_slash_commands_service, dependent: :destroy has_one :mattermost_notification_service, dependent: :destroy + has_one :slack_slash_commands_service, dependent: :destroy has_one :slack_notification_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb new file mode 100644 index 00000000000..9c66f95eef9 --- /dev/null +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -0,0 +1,49 @@ +class SlackSlashCommandsService < ChatService + include TriggersHelper + + prop_accessor :token + + def can_test? + false + end + + def title + 'Slack Slash Command' + end + + def description + "Perform common operations on GitLab in Slack" + end + + def to_param + 'slack_slash_commands' + end + + def fields + [ + { type: 'text', name: 'token', placeholder: '' } + ] + end + + def trigger(params) + return nil unless valid_token?(params[:token]) + + user = find_chat_user(params) + unless user + url = authorize_chat_name_url(params) + return Gitlab::ChatCommands::Presenters::Access.new(url).authorize + end + + Gitlab::ChatCommands::Command.new(project, user, params).execute + end + + private + + def find_chat_user(params) + ChatNames::FindUserService.new(self, params).execute + end + + def authorize_chat_name_url(params) + ChatNames::AuthorizeUserService.new(self, params).execute + end +end diff --git a/app/models/service.rb b/app/models/service.rb index 0bbab078cf6..8abd8e73e43 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -216,11 +216,12 @@ class Service < ActiveRecord::Base jira kubernetes mattermost_slash_commands + mattermost_notification pipelines_email pivotaltracker pushover redmine - mattermost_notification + slack_slash_commands slack_notification teamcity ] diff --git a/app/views/projects/services/mattermost_slash_commands/_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_help.html.haml index a676c0290a0..2bdd4cd148c 100644 --- a/app/views/projects/services/mattermost_slash_commands/_help.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_help.html.haml @@ -1,4 +1,4 @@ -- pretty_path_with_namespace = "#{@project ? @project.namespace.name : 'namespace'} / #{@project ? @project.name : 'name'}" +- pretty_path_with_namespace = "#{@project.namespace ? @project.namespace.name : 'namespace'} / #{@project ? @project.name : 'name'}" - run_actions_text = "Perform common operations on this project: #{pretty_path_with_namespace}" .well diff --git a/changelogs/unreleased/zj-slack-slash-commands.yml b/changelogs/unreleased/zj-slack-slash-commands.yml new file mode 100644 index 00000000000..9f4c8681ad0 --- /dev/null +++ b/changelogs/unreleased/zj-slack-slash-commands.yml @@ -0,0 +1,4 @@ +--- +title: Refactor presenters ChatCommands +merge_request: 7846 +author: diff --git a/lib/gitlab/chat_commands/deploy.rb b/lib/gitlab/chat_commands/deploy.rb index 0eed1fce0dc..6bb854dc080 100644 --- a/lib/gitlab/chat_commands/deploy.rb +++ b/lib/gitlab/chat_commands/deploy.rb @@ -4,7 +4,7 @@ module Gitlab include Gitlab::Routing.url_helpers def self.match(text) - /\Adeploy\s+(?.*)\s+to+\s+(?.*)\z/.match(text) + /\Adeploy\s+(?\S+.*)\s+to+\s+(?\S+.*)\z/.match(text) end def self.help_message diff --git a/lib/gitlab/chat_commands/help.rb b/lib/gitlab/chat_commands/help.rb new file mode 100644 index 00000000000..e76733f5445 --- /dev/null +++ b/lib/gitlab/chat_commands/help.rb @@ -0,0 +1,28 @@ +module Gitlab + module ChatCommands + class Help < BaseCommand + # This class has to be used last, as it always matches. It has to match + # because other commands were not triggered and we want to show the help + # command + def self.match(_text) + true + end + + def self.help_message + 'help' + end + + def self.allowed?(_project, _user) + true + end + + def execute(commands) + Gitlab::ChatCommands::Presenters::Help.new(commands).present(trigger) + end + + def trigger + params[:command] + end + end + end +end diff --git a/lib/mattermost/presenter.rb b/lib/gitlab/chat_commands/presenters/mattermost.rb similarity index 100% rename from lib/mattermost/presenter.rb rename to lib/gitlab/chat_commands/presenters/mattermost.rb diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb index bfc6818ac08..ed8d25a526a 100644 --- a/spec/lib/gitlab/chat_commands/command_spec.rb +++ b/spec/lib/gitlab/chat_commands/command_spec.rb @@ -64,7 +64,7 @@ describe Gitlab::ChatCommands::Command, service: true do context 'and user can not create deployment' do it 'returns action' do expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to start_with('Whoops! That action is not allowed') + expect(subject[:text]).to start_with('Whoops! This action is not allowed') end end @@ -74,7 +74,7 @@ describe Gitlab::ChatCommands::Command, service: true do end it 'returns action' do - expect(subject[:text]).to include('Deployment from staging to production started') + expect(subject[:text]).to include('Deployment started from staging to production') expect(subject[:response_type]).to be(:in_channel) end @@ -91,4 +91,26 @@ describe Gitlab::ChatCommands::Command, service: true do end end end + + describe '#match_command' do + subject { described_class.new(project, user, params).match_command.first } + + context 'IssueShow is triggered' do + let(:params) { { text: 'issue show 123' } } + + it { is_expected.to eq(Gitlab::ChatCommands::IssueShow) } + end + + context 'IssueCreate is triggered' do + let(:params) { { text: 'issue create my title' } } + + it { is_expected.to eq(Gitlab::ChatCommands::IssueCreate) } + end + + context 'IssueSearch is triggered' do + let(:params) { { text: 'issue search my query' } } + + it { is_expected.to eq(Gitlab::ChatCommands::IssueSearch) } + end + end end diff --git a/spec/models/project_services/chat_message/build_message_spec.rb b/spec/models/project_services/chat_message/build_message_spec.rb index b71d153f814..50ad5013df9 100644 --- a/spec/models/project_services/chat_message/build_message_spec.rb +++ b/spec/models/project_services/chat_message/build_message_spec.rb @@ -10,7 +10,7 @@ describe ChatMessage::BuildMessage do tag: false, project_name: 'project_name', - project_url: 'example.gitlab.com', + project_url: 'http://example.gitlab.com', commit: { status: status, @@ -48,10 +48,10 @@ describe ChatMessage::BuildMessage do end def build_message(status_text = status) - ":" \ - " Commit :" \ + " Commit " \ - " of branch" \ + " of branch" \ " by hacker #{status_text} in #{duration} #{'second'.pluralize(duration)}" end end diff --git a/spec/models/project_services/chat_message/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb index ebe0ead4408..190ff4c535d 100644 --- a/spec/models/project_services/chat_message/issue_message_spec.rb +++ b/spec/models/project_services/chat_message/issue_message_spec.rb @@ -10,14 +10,14 @@ describe ChatMessage::IssueMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', object_attributes: { title: 'Issue title', id: 10, iid: 100, assignee_id: 1, - url: 'url', + url: 'http://url.com', action: 'open', state: 'opened', description: 'issue description' @@ -40,11 +40,11 @@ describe ChatMessage::IssueMessage, models: true do context 'open' do it 'returns a message regarding opening of issues' do expect(subject.pretext).to eq( - '] Issue opened by test.user') + '[] Issue opened by test.user') expect(subject.attachments).to eq([ { title: "#100 Issue title", - title_link: "url", + title_link: "http://url.com", text: "issue description", color: color, } @@ -60,7 +60,7 @@ describe ChatMessage::IssueMessage, models: true do it 'returns a message regarding closing of issues' do expect(subject.pretext). to eq( - '] Issue closed by test.user') + '[] Issue closed by test.user') expect(subject.attachments).to be_empty end end diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb index 07c414c6ca4..cc154112e90 100644 --- a/spec/models/project_services/chat_message/merge_message_spec.rb +++ b/spec/models/project_services/chat_message/merge_message_spec.rb @@ -10,14 +10,14 @@ describe ChatMessage::MergeMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', object_attributes: { title: "Issue title\nSecond line", id: 10, iid: 100, assignee_id: 1, - url: 'url', + url: 'http://url.com', state: 'opened', description: 'issue description', source_branch: 'source_branch', @@ -31,8 +31,8 @@ describe ChatMessage::MergeMessage, models: true do context 'open' do it 'returns a message regarding opening of merge requests' do expect(subject.pretext).to eq( - 'test.user opened '\ - 'in : *Issue title*') + 'test.user opened '\ + 'in : *Issue title*') expect(subject.attachments).to be_empty end end @@ -43,8 +43,8 @@ describe ChatMessage::MergeMessage, models: true do end it 'returns a message regarding closing of merge requests' do expect(subject.pretext).to eq( - 'test.user closed '\ - 'in : *Issue title*') + 'test.user closed '\ + 'in : *Issue title*') expect(subject.attachments).to be_empty end end diff --git a/spec/models/project_services/chat_message/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb index 31936da40a2..da700a08e57 100644 --- a/spec/models/project_services/chat_message/note_message_spec.rb +++ b/spec/models/project_services/chat_message/note_message_spec.rb @@ -11,15 +11,15 @@ describe ChatMessage::NoteMessage, models: true do avatar_url: 'http://fakeavatar' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', repository: { name: 'project_name', - url: 'somewhere.com', + url: 'http://somewhere.com', }, object_attributes: { id: 10, note: 'comment on a commit', - url: 'url', + url: 'http://url.com', noteable_type: 'Commit' } } @@ -37,8 +37,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on commits' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*Added a commit message*") expected_attachments = [ { @@ -63,8 +63,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on a merge request' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*merge request title*") expected_attachments = [ { @@ -90,8 +90,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on an issue' do message = described_class.new(@args) expect(message.pretext).to eq( - "test.user in : " \ + "test.user in : " \ "*issue title*") expected_attachments = [ { @@ -115,8 +115,8 @@ describe ChatMessage::NoteMessage, models: true do it 'returns a message regarding notes on a project snippet' do message = described_class.new(@args) - expect(message.pretext).to eq("test.user in : " \ + expect(message.pretext).to eq("test.user in : " \ "*snippet title*") expected_attachments = [ { diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb index b781c4505db..24928873bad 100644 --- a/spec/models/project_services/chat_message/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -10,7 +10,7 @@ describe ChatMessage::PushMessage, models: true do project_name: 'project_name', ref: 'refs/heads/master', user_name: 'test.user', - project_url: 'url' + project_url: 'http://url.com' } end @@ -19,20 +19,20 @@ describe ChatMessage::PushMessage, models: true do context 'push' do before do args[:commits] = [ - { message: 'message1', url: 'url1', id: 'abcdefghijkl', author: { name: 'author1' } }, - { message: 'message2', url: 'url2', id: '123456789012', author: { name: 'author2' } }, + { message: 'message1', url: 'http://url1.com', id: 'abcdefghijkl', author: { name: 'author1' } }, + { message: 'message2', url: 'http://url2.com', id: '123456789012', author: { name: 'author2' } }, ] end it 'returns a message regarding pushes' do expect(subject.pretext).to eq( - 'test.user pushed to branch of '\ - ' ()' + 'test.user pushed to branch of '\ + ' ()' ) expect(subject.attachments).to eq([ { - text: ": message1 - author1\n"\ - ": message2 - author2", + text: ": message1 - author1\n"\ + ": message2 - author2", color: color, } ]) @@ -47,14 +47,14 @@ describe ChatMessage::PushMessage, models: true do project_name: 'project_name', ref: 'refs/tags/new_tag', user_name: 'test.user', - project_url: 'url' + project_url: 'http://url.com' } end it 'returns a message regarding pushes' do expect(subject.pretext).to eq('test.user pushed new tag ' \ - ' to ' \ - '') + ' to ' \ + '') expect(subject.attachments).to be_empty end end @@ -66,8 +66,8 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a new branch' do expect(subject.pretext).to eq( - 'test.user pushed new branch to '\ - '' + 'test.user pushed new branch to '\ + '' ) expect(subject.attachments).to be_empty end @@ -80,7 +80,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a removed branch' do expect(subject.pretext).to eq( - 'test.user removed branch master from ' + 'test.user removed branch master from ' ) expect(subject.attachments).to be_empty end diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb index 94c04dc0865..a2ad61e38e7 100644 --- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -10,10 +10,10 @@ describe ChatMessage::WikiPageMessage, models: true do username: 'test.user' }, project_name: 'project_name', - project_url: 'somewhere.com', + project_url: 'http://somewhere.com', object_attributes: { title: 'Wiki page title', - url: 'url', + url: 'http://url.com', content: 'Wiki page description' } } @@ -25,7 +25,7 @@ describe ChatMessage::WikiPageMessage, models: true do it 'returns a message that a new wiki page was created' do expect(subject.pretext).to eq( - 'test.user created in : '\ + 'test.user created in : '\ '*Wiki page title*') end end @@ -35,7 +35,7 @@ describe ChatMessage::WikiPageMessage, models: true do it 'returns a message that a wiki page was updated' do expect(subject.pretext).to eq( - 'test.user edited in : '\ + 'test.user edited in : '\ '*Wiki page title*') end end From ed880e4954803f9753cfafe0dd8106852245d10d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 15 Dec 2016 23:45:10 +0100 Subject: [PATCH 218/386] Rename ChatService into ChatSlashCommandsService --- app/models/project.rb | 1 - app/models/project_services/chat_service.rb | 21 ------- .../chat_slash_commands_service.rb | 55 +++++++++++++++++++ .../mattermost_slash_commands_service.rb | 28 +--------- .../slack_slash_commands_service.rb | 36 +----------- 5 files changed, 60 insertions(+), 81 deletions(-) delete mode 100644 app/models/project_services/chat_service.rb create mode 100644 app/models/project_services/chat_slash_commands_service.rb diff --git a/app/models/project.rb b/app/models/project.rb index 7f3a4debfc6..9d8351399f9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -79,7 +79,6 @@ class Project < ActiveRecord::Base has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event' has_many :boards, before_add: :validate_board_limit, dependent: :destroy - has_many :chat_services # Project services has_one :campfire_service, dependent: :destroy diff --git a/app/models/project_services/chat_service.rb b/app/models/project_services/chat_service.rb deleted file mode 100644 index 574788462de..00000000000 --- a/app/models/project_services/chat_service.rb +++ /dev/null @@ -1,21 +0,0 @@ -# Base class for Chat services -# This class is not meant to be used directly, but only to inherit from. -class ChatService < Service - default_value_for :category, 'chat' - - has_many :chat_names, foreign_key: :service_id - - def valid_token?(token) - self.respond_to?(:token) && - self.token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) - end - - def supported_events - [] - end - - def trigger(params) - raise NotImplementedError - end -end diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/chat_slash_commands_service.rb new file mode 100644 index 00000000000..b21a1730285 --- /dev/null +++ b/app/models/project_services/chat_slash_commands_service.rb @@ -0,0 +1,55 @@ +# Base class for Chat services +# This class is not meant to be used directly, but only to inherrit from. +class ChatSlashCommandsService < Service + default_value_for :category, 'chat' + + prop_accessor :token + + has_many :chat_names, foreign_key: :service_id + + def valid_token?(token) + self.respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + end + + def supported_events + [] + end + + def can_test? + false + end + + def fields + [ + { type: 'text', name: 'token', placeholder: '' } + ] + end + + def trigger(params) + return nil unless valid_token?(params[:token]) + + user = find_chat_user(params) + unless user + url = authorize_chat_name_url(params) + return presenter.authorize_chat_name(url) + end + + Gitlab::ChatCommands::Command.new(presenter, project, user, params).execute + end + + private + + def find_chat_user(params) + ChatNames::FindUserService.new(self, params).execute + end + + def authorize_chat_name_url(params) + ChatNames::AuthorizeUserService.new(self, params).execute + end + + def presenter + throw NotImplementedError + end +end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 33431f41dc2..4c1e27cafbb 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -19,31 +19,7 @@ class MattermostSlashCommandsService < ChatService 'mattermost_slash_commands' end - def fields - [ - { type: 'text', name: 'token', placeholder: '' } - ] - end - - def trigger(params) - return nil unless valid_token?(params[:token]) - - user = find_chat_user(params) - unless user - url = authorize_chat_name_url(params) - return Mattermost::Presenter.authorize_chat_name(url) - end - - Gitlab::ChatCommands::Command.new(project, user, params).execute - end - - private - - def find_chat_user(params) - ChatNames::FindUserService.new(self, params).execute - end - - def authorize_chat_name_url(params) - ChatNames::AuthorizeUserService.new(self, params).execute + def presenter + Gitlab::ChatCommands::Presenters::Mattermost.new end end diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb index 9c66f95eef9..978d48e8c18 100644 --- a/app/models/project_services/slack_slash_commands_service.rb +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -1,12 +1,6 @@ -class SlackSlashCommandsService < ChatService +class SlackSlashCommandsService < ChatSlashCommandsService include TriggersHelper - prop_accessor :token - - def can_test? - false - end - def title 'Slack Slash Command' end @@ -19,31 +13,7 @@ class SlackSlashCommandsService < ChatService 'slack_slash_commands' end - def fields - [ - { type: 'text', name: 'token', placeholder: '' } - ] - end - - def trigger(params) - return nil unless valid_token?(params[:token]) - - user = find_chat_user(params) - unless user - url = authorize_chat_name_url(params) - return Gitlab::ChatCommands::Presenters::Access.new(url).authorize - end - - Gitlab::ChatCommands::Command.new(project, user, params).execute - end - - private - - def find_chat_user(params) - ChatNames::FindUserService.new(self, params).execute - end - - def authorize_chat_name_url(params) - ChatNames::AuthorizeUserService.new(self, params).execute + def presenter + Gitlab::ChatCommands::Presenters::Mattermost.new end end From 37057870a6b4bf4bf42cc7210a6ae17d68ae5448 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 15 Dec 2016 23:45:46 +0100 Subject: [PATCH 219/386] Rename Mattermost::Presenter to Presenter --- lib/gitlab/chat_commands/presenter.rb | 133 ++++++++++++++++++ .../chat_commands/presenters/mattermost.rb | 131 ----------------- 2 files changed, 133 insertions(+), 131 deletions(-) create mode 100644 lib/gitlab/chat_commands/presenter.rb delete mode 100644 lib/gitlab/chat_commands/presenters/mattermost.rb diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb new file mode 100644 index 00000000000..3143dc092a1 --- /dev/null +++ b/lib/gitlab/chat_commands/presenter.rb @@ -0,0 +1,133 @@ +module Gitlab + class ChatCommands + class Presenter + class << self + include Gitlab::Routing.url_helpers + + def authorize_chat_name(url) + message = if url + ":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{url})." + else + ":sweat_smile: Couldn't identify you, nor can I autorize you!" + end + + ephemeral_response(message) + end + + def help(commands, trigger) + if commands.none? + ephemeral_response("No commands configured") + else + commands.map! { |command| "#{trigger} #{command}" } + message = header_with_list("Available commands", commands) + + ephemeral_response(message) + end + end + + def present(subject) + return not_found unless subject + + if subject.is_a?(Gitlab::ChatCommands::Result) + show_result(subject) + elsif subject.respond_to?(:count) + if subject.many? + multiple_resources(subject) + elsif subject.none? + not_found + else + single_resource(subject) + end + else + single_resource(subject) + end + end + + def access_denied + ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).") + end + + private + + def show_result(result) + case result.type + when :success + in_channel_response(result.message) + else + ephemeral_response(result.message) + end + end + + def not_found + ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:") + end + + def single_resource(resource) + return error(resource) if resource.errors.any? || !resource.persisted? + + message = "### #{title(resource)}" + message << "\n\n#{resource.description}" if resource.try(:description) + + in_channel_response(message) + end + + def multiple_resources(resources) + resources.map! { |resource| title(resource) } + + message = header_with_list("Multiple results were found:", resources) + + ephemeral_response(message) + end + + def error(resource) + message = header_with_list("The action was not successful, because:", resource.errors.messages) + + ephemeral_response(message) + end + + def title(resource) + reference = resource.try(:to_reference) || resource.try(:id) + title = resource.try(:title) || resource.try(:name) + + "[#{reference} #{title}](#{url(resource)})" + end + + def header_with_list(header, items) + message = [header] + + items.each do |item| + message << "- #{item}" + end + + message.join("\n") + end + + def url(resource) + url_for( + [ + resource.project.namespace.becomes(Namespace), + resource.project, + resource + ] + ) + end + + def ephemeral_response(message) + { + response_type: :ephemeral, + text: message, + status: 200 + } + end + + def in_channel_response(message) + { + response_type: :in_channel, + text: message, + status: 200 + } + end + end + end + end +end diff --git a/lib/gitlab/chat_commands/presenters/mattermost.rb b/lib/gitlab/chat_commands/presenters/mattermost.rb deleted file mode 100644 index 67eda983a74..00000000000 --- a/lib/gitlab/chat_commands/presenters/mattermost.rb +++ /dev/null @@ -1,131 +0,0 @@ -module Mattermost - class Presenter - class << self - include Gitlab::Routing.url_helpers - - def authorize_chat_name(url) - message = if url - ":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{url})." - else - ":sweat_smile: Couldn't identify you, nor can I autorize you!" - end - - ephemeral_response(message) - end - - def help(commands, trigger) - if commands.none? - ephemeral_response("No commands configured") - else - commands.map! { |command| "#{trigger} #{command}" } - message = header_with_list("Available commands", commands) - - ephemeral_response(message) - end - end - - def present(subject) - return not_found unless subject - - if subject.is_a?(Gitlab::ChatCommands::Result) - show_result(subject) - elsif subject.respond_to?(:count) - if subject.many? - multiple_resources(subject) - elsif subject.none? - not_found - else - single_resource(subject) - end - else - single_resource(subject) - end - end - - def access_denied - ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).") - end - - private - - def show_result(result) - case result.type - when :success - in_channel_response(result.message) - else - ephemeral_response(result.message) - end - end - - def not_found - ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:") - end - - def single_resource(resource) - return error(resource) if resource.errors.any? || !resource.persisted? - - message = "### #{title(resource)}" - message << "\n\n#{resource.description}" if resource.try(:description) - - in_channel_response(message) - end - - def multiple_resources(resources) - resources.map! { |resource| title(resource) } - - message = header_with_list("Multiple results were found:", resources) - - ephemeral_response(message) - end - - def error(resource) - message = header_with_list("The action was not successful, because:", resource.errors.messages) - - ephemeral_response(message) - end - - def title(resource) - reference = resource.try(:to_reference) || resource.try(:id) - title = resource.try(:title) || resource.try(:name) - - "[#{reference} #{title}](#{url(resource)})" - end - - def header_with_list(header, items) - message = [header] - - items.each do |item| - message << "- #{item}" - end - - message.join("\n") - end - - def url(resource) - url_for( - [ - resource.project.namespace.becomes(Namespace), - resource.project, - resource - ] - ) - end - - def ephemeral_response(message) - { - response_type: :ephemeral, - text: message, - status: 200 - } - end - - def in_channel_response(message) - { - response_type: :in_channel, - text: message, - status: 200 - } - end - end - end -end From 8ca5fef9d472e11b4a9ff5d5ab47bbba3e7e670d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 15 Dec 2016 23:48:30 +0100 Subject: [PATCH 220/386] Use single presenter for everything --- .../project_services/chat_slash_commands_service.rb | 8 ++------ .../project_services/mattermost_slash_commands_service.rb | 4 ---- .../project_services/slack_slash_commands_service.rb | 4 ---- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/chat_slash_commands_service.rb index b21a1730285..7ff80447a1c 100644 --- a/app/models/project_services/chat_slash_commands_service.rb +++ b/app/models/project_services/chat_slash_commands_service.rb @@ -33,10 +33,10 @@ class ChatSlashCommandsService < Service user = find_chat_user(params) unless user url = authorize_chat_name_url(params) - return presenter.authorize_chat_name(url) + return Gitlab::ChatCommands::Presenter.authorize_chat_name(url) end - Gitlab::ChatCommands::Command.new(presenter, project, user, params).execute + Gitlab::ChatCommands::Command.new(project, user, params).execute end private @@ -48,8 +48,4 @@ class ChatSlashCommandsService < Service def authorize_chat_name_url(params) ChatNames::AuthorizeUserService.new(self, params).execute end - - def presenter - throw NotImplementedError - end end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 4c1e27cafbb..6aac7c2788b 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -18,8 +18,4 @@ class MattermostSlashCommandsService < ChatService def to_param 'mattermost_slash_commands' end - - def presenter - Gitlab::ChatCommands::Presenters::Mattermost.new - end end diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb index 978d48e8c18..197d8eb7bca 100644 --- a/app/models/project_services/slack_slash_commands_service.rb +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -12,8 +12,4 @@ class SlackSlashCommandsService < ChatSlashCommandsService def to_param 'slack_slash_commands' end - - def presenter - Gitlab::ChatCommands::Presenters::Mattermost.new - end end From dc995daf90e67b5531dd7fbb8a958507f46e4eb6 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 15 Dec 2016 23:51:57 +0100 Subject: [PATCH 221/386] Use Slack compatible syntax --- lib/gitlab/chat_commands/presenter.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index 3143dc092a1..14c78ac39aa 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -6,7 +6,7 @@ module Gitlab def authorize_chat_name(url) message = if url - ":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{url})." + ":wave: Hi there! Before I do anything for you, please <#{url}|connect your GitLab account>." else ":sweat_smile: Couldn't identify you, nor can I autorize you!" end @@ -44,7 +44,7 @@ module Gitlab end def access_denied - ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).") + ephemeral_response("Whoops! That action is not allowed. This incident will be .") end private @@ -65,7 +65,7 @@ module Gitlab def single_resource(resource) return error(resource) if resource.errors.any? || !resource.persisted? - message = "### #{title(resource)}" + message = "#{title(resource)}:" message << "\n\n#{resource.description}" if resource.try(:description) in_channel_response(message) @@ -89,7 +89,7 @@ module Gitlab reference = resource.try(:to_reference) || resource.try(:id) title = resource.try(:title) || resource.try(:name) - "[#{reference} #{title}](#{url(resource)})" + "<#{url(resource)}|#{reference} #{title}>" end def header_with_list(header, items) From cc83aded33471a3e2f943bd9a760f689d30f901e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 00:00:54 +0100 Subject: [PATCH 222/386] Render format dependent links --- .../chat_slash_commands_service.rb | 13 +- .../mattermost_slash_commands_service.rb | 4 + .../slack_slash_commands_service.rb | 4 + lib/gitlab/chat_commands/base_command.rb | 4 + lib/gitlab/chat_commands/command.rb | 6 +- lib/gitlab/chat_commands/presenter.rb | 232 +++++++++--------- 6 files changed, 148 insertions(+), 115 deletions(-) diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/chat_slash_commands_service.rb index 7ff80447a1c..f11d257e6c1 100644 --- a/app/models/project_services/chat_slash_commands_service.rb +++ b/app/models/project_services/chat_slash_commands_service.rb @@ -33,10 +33,11 @@ class ChatSlashCommandsService < Service user = find_chat_user(params) unless user url = authorize_chat_name_url(params) - return Gitlab::ChatCommands::Presenter.authorize_chat_name(url) + return presenter.authorize_chat_name(url) end - Gitlab::ChatCommands::Command.new(project, user, params).execute + Gitlab::ChatCommands::Command.new(project, user, + params.merge(presenter_format: presenter_format)).execute end private @@ -48,4 +49,12 @@ class ChatSlashCommandsService < Service def authorize_chat_name_url(params) ChatNames::AuthorizeUserService.new(self, params).execute end + + def presenter + Gitlab::ChatCommands::Presenter.new(presenter_format) + end + + def presenter_format + throw NotImplementedError + end end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 6aac7c2788b..f9d4b29f4ea 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -18,4 +18,8 @@ class MattermostSlashCommandsService < ChatService def to_param 'mattermost_slash_commands' end + + def presenter_format + 'mattermost' + end end diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb index 197d8eb7bca..6bf10ff6572 100644 --- a/app/models/project_services/slack_slash_commands_service.rb +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -12,4 +12,8 @@ class SlackSlashCommandsService < ChatSlashCommandsService def to_param 'slack_slash_commands' end + + def presenter_format + 'slack' + end end diff --git a/lib/gitlab/chat_commands/base_command.rb b/lib/gitlab/chat_commands/base_command.rb index 25da8474e95..156bb826f86 100644 --- a/lib/gitlab/chat_commands/base_command.rb +++ b/lib/gitlab/chat_commands/base_command.rb @@ -42,6 +42,10 @@ module Gitlab def find_by_iid(iid) collection.find_by(iid: iid) end + + def presenter + Gitlab::ChatCommands::Presenter.new(params[:presenter_format]) + end end end end diff --git a/lib/gitlab/chat_commands/command.rb b/lib/gitlab/chat_commands/command.rb index b0d3fdbc48a..c5c54cf7cfc 100644 --- a/lib/gitlab/chat_commands/command.rb +++ b/lib/gitlab/chat_commands/command.rb @@ -48,15 +48,15 @@ module Gitlab end def help(messages) - Mattermost::Presenter.help(messages, params[:command]) + presenter.help(messages, params[:command]) end def access_denied - Mattermost::Presenter.access_denied + presenter.access_denied end def present(resource) - Mattermost::Presenter.present(resource) + presenter.present(resource) end end end diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index 14c78ac39aa..e151513cbd5 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -1,131 +1,143 @@ module Gitlab class ChatCommands class Presenter - class << self - include Gitlab::Routing.url_helpers + include Gitlab::Routing.url_helpers - def authorize_chat_name(url) - message = if url - ":wave: Hi there! Before I do anything for you, please <#{url}|connect your GitLab account>." - else - ":sweat_smile: Couldn't identify you, nor can I autorize you!" - end + attr_reader :format + + def initialize(format) + @format = format + end + + def authorize_chat_name(url) + message = if url + ":wave: Hi there! Before I do anything for you, please #{link(url, 'connect your GitLab account')}." + else + ":sweat_smile: Couldn't identify you, nor can I autorize you!" + end + + ephemeral_response(message) + end + + def help(commands, trigger) + if commands.none? + ephemeral_response("No commands configured") + else + commands.map! { |command| "#{trigger} #{command}" } + message = header_with_list("Available commands", commands) ephemeral_response(message) end + end - def help(commands, trigger) - if commands.none? - ephemeral_response("No commands configured") - else - commands.map! { |command| "#{trigger} #{command}" } - message = header_with_list("Available commands", commands) + def present(subject) + return not_found unless subject - ephemeral_response(message) - end - end - - def present(subject) - return not_found unless subject - - if subject.is_a?(Gitlab::ChatCommands::Result) - show_result(subject) - elsif subject.respond_to?(:count) - if subject.many? - multiple_resources(subject) - elsif subject.none? - not_found - else - single_resource(subject) - end + if subject.is_a?(Gitlab::ChatCommands::Result) + show_result(subject) + elsif subject.respond_to?(:count) + if subject.many? + multiple_resources(subject) + elsif subject.none? + not_found else single_resource(subject) end + else + single_resource(subject) + end + end + + def access_denied + ephemeral_response("Whoops! That action is not allowed. This incident will be #{link('https://xkcd.com/838/', 'reported')}.") + end + + private + + def show_result(result) + case result.type + when :success + in_channel_response(result.message) + else + ephemeral_response(result.message) + end + end + + def not_found + ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:") + end + + def single_resource(resource) + return error(resource) if resource.errors.any? || !resource.persisted? + + message = "#{title(resource)}:" + message << "\n\n#{resource.description}" if resource.try(:description) + + in_channel_response(message) + end + + def multiple_resources(resources) + resources.map! { |resource| title(resource) } + + message = header_with_list("Multiple results were found:", resources) + + ephemeral_response(message) + end + + def error(resource) + message = header_with_list("The action was not successful, because:", resource.errors.messages) + + ephemeral_response(message) + end + + def title(resource) + reference = resource.try(:to_reference) || resource.try(:id) + title = resource.try(:title) || resource.try(:name) + + link(url(resource), "#{reference} #{title}") + end + + def header_with_list(header, items) + message = [header] + + items.each do |item| + message << "- #{item}" end - def access_denied - ephemeral_response("Whoops! That action is not allowed. This incident will be .") - end + message.join("\n") + end - private + def url(resource) + url_for( + [ + resource.project.namespace.becomes(Namespace), + resource.project, + resource + ] + ) + end - def show_result(result) - case result.type - when :success - in_channel_response(result.message) - else - ephemeral_response(result.message) - end - end + def ephemeral_response(message) + { + response_type: :ephemeral, + text: message, + status: 200 + } + end - def not_found - ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:") - end + def in_channel_response(message) + { + response_type: :in_channel, + text: message, + status: 200 + } + end - def single_resource(resource) - return error(resource) if resource.errors.any? || !resource.persisted? - - message = "#{title(resource)}:" - message << "\n\n#{resource.description}" if resource.try(:description) - - in_channel_response(message) - end - - def multiple_resources(resources) - resources.map! { |resource| title(resource) } - - message = header_with_list("Multiple results were found:", resources) - - ephemeral_response(message) - end - - def error(resource) - message = header_with_list("The action was not successful, because:", resource.errors.messages) - - ephemeral_response(message) - end - - def title(resource) - reference = resource.try(:to_reference) || resource.try(:id) - title = resource.try(:title) || resource.try(:name) - - "<#{url(resource)}|#{reference} #{title}>" - end - - def header_with_list(header, items) - message = [header] - - items.each do |item| - message << "- #{item}" - end - - message.join("\n") - end - - def url(resource) - url_for( - [ - resource.project.namespace.becomes(Namespace), - resource.project, - resource - ] - ) - end - - def ephemeral_response(message) - { - response_type: :ephemeral, - text: message, - status: 200 - } - end - - def in_channel_response(message) - { - response_type: :in_channel, - text: message, - status: 200 - } + def link(url, title) + case format + when 'slack' then "<#{url}|#{title}>" + when 'mattermost' then "[#{title}](#{url})" + else then title end end end From ebc3f62be52e386b4550e01ce589744e880ad443 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 13:32:05 +0100 Subject: [PATCH 223/386] Fix specs --- .../mattermost_slash_commands/_help.html.haml | 5 ++--- lib/gitlab/chat_commands/command.rb | 4 ++-- lib/gitlab/chat_commands/presenter.rb | 11 ++++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/views/projects/services/mattermost_slash_commands/_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_help.html.haml index 2bdd4cd148c..01a77a952d1 100644 --- a/app/views/projects/services/mattermost_slash_commands/_help.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_help.html.haml @@ -1,5 +1,4 @@ -- pretty_path_with_namespace = "#{@project.namespace ? @project.namespace.name : 'namespace'} / #{@project ? @project.name : 'name'}" -- run_actions_text = "Perform common operations on this project: #{pretty_path_with_namespace}" +- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}" .well This service allows GitLab users to perform common operations on this @@ -27,7 +26,7 @@ .form-group = label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label' .col-sm-10.col-xs-12.input-group - = text_field_tag :display_name, "GitLab / #{pretty_path_with_namespace}", class: 'form-control input-sm', readonly: 'readonly' + = text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly' .input-group-btn = clipboard_button(clipboard_target: '#display_name') diff --git a/lib/gitlab/chat_commands/command.rb b/lib/gitlab/chat_commands/command.rb index c5c54cf7cfc..145086755e4 100644 --- a/lib/gitlab/chat_commands/command.rb +++ b/lib/gitlab/chat_commands/command.rb @@ -22,8 +22,6 @@ module Gitlab end end - private - def match_command match = nil service = available_commands.find do |klass| @@ -33,6 +31,8 @@ module Gitlab [service, match] end + private + def help_messages available_commands.map(&:help_message) end diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index e151513cbd5..c86efc0c3f8 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -135,9 +135,14 @@ module Gitlab def link(url, title) case format - when 'slack' then "<#{url}|#{title}>" - when 'mattermost' then "[#{title}](#{url})" - else then title + when 'slack' + "<#{url}|#{title}>" + + when 'mattermost' + "[#{title}](#{url})" + + else + title end end end From f9f1a508c6a4bdb2fcee98d18394e28e1cc663c3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 14:21:06 +0100 Subject: [PATCH 224/386] Fix SlackSlashCommands tests --- .../chat_slash_commands_service.rb | 2 +- .../mattermost_slash_commands_service.rb | 2 +- lib/gitlab/chat_commands/presenter.rb | 2 +- spec/lib/gitlab/chat_commands/command_spec.rb | 20 +++- .../project_services/chat_service_spec.rb | 15 --- .../chat_slash_commands_service_spec.rb | 103 ++++++++++++++++++ .../mattermost_slash_commands_service_spec.rb | 96 +--------------- .../slack_slash_commands_service.rb | 5 + 8 files changed, 127 insertions(+), 118 deletions(-) delete mode 100644 spec/models/project_services/chat_service_spec.rb create mode 100644 spec/models/project_services/chat_slash_commands_service_spec.rb create mode 100644 spec/models/project_services/slack_slash_commands_service.rb diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/chat_slash_commands_service.rb index f11d257e6c1..e58c96f5094 100644 --- a/app/models/project_services/chat_slash_commands_service.rb +++ b/app/models/project_services/chat_slash_commands_service.rb @@ -5,7 +5,7 @@ class ChatSlashCommandsService < Service prop_accessor :token - has_many :chat_names, foreign_key: :service_id + has_many :chat_names, foreign_key: :service_id, dependent: :destroy def valid_token?(token) self.respond_to?(:token) && diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index f9d4b29f4ea..72a7b9c8f3a 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -1,4 +1,4 @@ -class MattermostSlashCommandsService < ChatService +class MattermostSlashCommandsService < ChatSlashCommandsService include TriggersHelper prop_accessor :token diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index c86efc0c3f8..98356ebebb3 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -1,5 +1,5 @@ module Gitlab - class ChatCommands + module ChatCommands class Presenter include Gitlab::Routing.url_helpers diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb index ed8d25a526a..dec98d990b2 100644 --- a/spec/lib/gitlab/chat_commands/command_spec.rb +++ b/spec/lib/gitlab/chat_commands/command_spec.rb @@ -3,9 +3,13 @@ require 'spec_helper' describe Gitlab::ChatCommands::Command, service: true do let(:project) { create(:empty_project) } let(:user) { create(:user) } + let(:format) { nil } describe '#execute' do - subject { described_class.new(project, user, params).execute } + subject do + described_class.new(project, user, + params.merge(presenter_format: format)).execute + end context 'when no command is available' do let(:params) { { text: 'issue show 1' } } @@ -47,8 +51,14 @@ describe Gitlab::ChatCommands::Command, service: true do expect(subject[:text]).to match("my new issue") end - it 'shows a link to the new issue' do - expect(subject[:text]).to match(/\/issues\/\d+/) + %w(slack mattermost).each do |format| + context "for #{format}" do + let(:format) { format } + + it 'shows a link to the new issue' do + expect(subject[:text]).to match(/\/issues\/\d+/) + end + end end end @@ -64,7 +74,7 @@ describe Gitlab::ChatCommands::Command, service: true do context 'and user can not create deployment' do it 'returns action' do expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to start_with('Whoops! This action is not allowed') + expect(subject[:text]).to start_with('Whoops! That action is not allowed') end end @@ -74,7 +84,7 @@ describe Gitlab::ChatCommands::Command, service: true do end it 'returns action' do - expect(subject[:text]).to include('Deployment started from staging to production') + expect(subject[:text]).to include('Deployment from staging to production started.') expect(subject[:response_type]).to be(:in_channel) end diff --git a/spec/models/project_services/chat_service_spec.rb b/spec/models/project_services/chat_service_spec.rb deleted file mode 100644 index c6a45a3e1be..00000000000 --- a/spec/models/project_services/chat_service_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe ChatService, models: true do - describe "Associations" do - it { is_expected.to have_many :chat_names } - end - - describe '#valid_token?' do - subject { described_class.new } - - it 'is false as it has no token' do - expect(subject.valid_token?('wer')).to be_falsey - end - end -end diff --git a/spec/models/project_services/chat_slash_commands_service_spec.rb b/spec/models/project_services/chat_slash_commands_service_spec.rb new file mode 100644 index 00000000000..64fdd4d570b --- /dev/null +++ b/spec/models/project_services/chat_slash_commands_service_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +describe ChatSlashCommandsService, models: true do + describe "Associations" do + it { is_expected.to respond_to :token } + it { is_expected.to have_many :chat_names } + end + + describe '#valid_token?' do + subject { described_class.new } + + context 'when the token is empty' do + it 'is false' do + expect(subject.valid_token?('wer')).to be_falsey + end + end + + context 'when there is a token' do + before do + subject.token = '123' + end + + it 'accepts equal tokens' do + expect(subject.valid_token?('123')).to be_truthy + end + end + end + + describe '#trigger' do + subject { described_class.new } + + before do + allow(subject).to receive(:presenter_format).and_return('unknown') + end + + context 'no token is passed' do + let(:params) { Hash.new } + + it 'returns nil' do + expect(subject.trigger(params)).to be_nil + end + end + + context 'with a token passed' do + let(:project) { create(:empty_project) } + let(:params) { { token: 'token' } } + + before do + allow(subject).to receive(:token).and_return('token') + end + + context 'no user can be found' do + context 'when no url can be generated' do + it 'responds with the authorize url' do + response = subject.trigger(params) + + expect(response[:response_type]).to eq :ephemeral + expect(response[:text]).to start_with ":sweat_smile: Couldn't identify you" + end + end + + context 'when an auth url can be generated' do + let(:params) do + { + team_domain: 'http://domain.tld', + team_id: 'T3423423', + user_id: 'U234234', + user_name: 'mepmep', + token: 'token' + } + end + + let(:service) do + project.create_mattermost_slash_commands_service( + properties: { token: 'token' } + ) + end + + it 'generates the url' do + response = service.trigger(params) + + expect(response[:text]).to start_with(':wave: Hi there!') + end + end + end + + context 'when the user is authenticated' do + let!(:chat_name) { create(:chat_name, service: subject) } + let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } } + + subject do + described_class.create(project: project, properties: { token: 'token' }) + end + + it 'triggers the command' do + expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute) + + subject.trigger(params) + end + end + end + end +end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index 4a1037e950b..b9deb0201e1 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -1,99 +1,5 @@ require 'spec_helper' describe MattermostSlashCommandsService, models: true do - describe "Associations" do - it { is_expected.to respond_to :token } - end - - describe '#valid_token?' do - subject { described_class.new } - - context 'when the token is empty' do - it 'is false' do - expect(subject.valid_token?('wer')).to be_falsey - end - end - - context 'when there is a token' do - before do - subject.token = '123' - end - - it 'accepts equal tokens' do - expect(subject.valid_token?('123')).to be_truthy - end - end - end - - describe '#trigger' do - subject { described_class.new } - - context 'no token is passed' do - let(:params) { Hash.new } - - it 'returns nil' do - expect(subject.trigger(params)).to be_nil - end - end - - context 'with a token passed' do - let(:project) { create(:empty_project) } - let(:params) { { token: 'token' } } - - before do - allow(subject).to receive(:token).and_return('token') - end - - context 'no user can be found' do - context 'when no url can be generated' do - it 'responds with the authorize url' do - response = subject.trigger(params) - - expect(response[:response_type]).to eq :ephemeral - expect(response[:text]).to start_with ":sweat_smile: Couldn't identify you" - end - end - - context 'when an auth url can be generated' do - let(:params) do - { - team_domain: 'http://domain.tld', - team_id: 'T3423423', - user_id: 'U234234', - user_name: 'mepmep', - token: 'token' - } - end - - let(:service) do - project.create_mattermost_slash_commands_service( - properties: { token: 'token' } - ) - end - - it 'generates the url' do - response = service.trigger(params) - - expect(response[:text]).to start_with(':wave: Hi there!') - end - end - end - - context 'when the user is authenticated' do - let!(:chat_name) { create(:chat_name, service: service) } - let(:service) do - project.create_mattermost_slash_commands_service( - properties: { token: 'token' } - ) - end - let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } } - - it 'triggers the command' do - expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute) - - service.trigger(params) - end - end - end - end + it { is_expected.to respond_to :presenter_format } end diff --git a/spec/models/project_services/slack_slash_commands_service.rb b/spec/models/project_services/slack_slash_commands_service.rb new file mode 100644 index 00000000000..5ef97b9a2ed --- /dev/null +++ b/spec/models/project_services/slack_slash_commands_service.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe SlackSlashCommandsService, models: true do + it { is_expected.to respond_to :presenter_format } +end From 0f2776287a7d9b0fde9ff54ef8d9f74e2f844a09 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 16 Dec 2016 15:08:10 +0100 Subject: [PATCH 225/386] Use Slack::Notifier::LinkFormatter to convert markdown links to slack compat --- .../chat_slash_commands_service.rb | 8 ++--- .../mattermost_slash_commands_service.rb | 4 --- .../slack_slash_commands_service.rb | 11 ++++-- lib/gitlab/chat_commands/base_command.rb | 2 +- lib/gitlab/chat_commands/presenter.rb | 25 ++----------- spec/lib/gitlab/chat_commands/command_spec.rb | 14 ++------ .../mattermost_notification_service_spec.rb | 2 +- .../mattermost_slash_commands_service_spec.rb | 2 +- .../slack_notification_service_spec.rb | 2 +- .../slack_slash_commands_service.rb | 35 ++++++++++++++++++- .../chat_slash_commands_shared_examples.rb} | 8 +---- ...ttermost_notifications_shared_examples.rb} | 2 +- 12 files changed, 57 insertions(+), 58 deletions(-) rename spec/{models/project_services/chat_slash_commands_service_spec.rb => support/chat_slash_commands_shared_examples.rb} (93%) rename spec/support/{slack_mattermost_shared_examples.rb => slack_mattermost_notifications_shared_examples.rb} (99%) diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/chat_slash_commands_service.rb index e58c96f5094..12261e9821e 100644 --- a/app/models/project_services/chat_slash_commands_service.rb +++ b/app/models/project_services/chat_slash_commands_service.rb @@ -37,7 +37,7 @@ class ChatSlashCommandsService < Service end Gitlab::ChatCommands::Command.new(project, user, - params.merge(presenter_format: presenter_format)).execute + params).execute end private @@ -51,10 +51,6 @@ class ChatSlashCommandsService < Service end def presenter - Gitlab::ChatCommands::Presenter.new(presenter_format) - end - - def presenter_format - throw NotImplementedError + Gitlab::ChatCommands::Presenter.new end end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 72a7b9c8f3a..10740275669 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -18,8 +18,4 @@ class MattermostSlashCommandsService < ChatSlashCommandsService def to_param 'mattermost_slash_commands' end - - def presenter_format - 'mattermost' - end end diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb index 6bf10ff6572..8413c657099 100644 --- a/app/models/project_services/slack_slash_commands_service.rb +++ b/app/models/project_services/slack_slash_commands_service.rb @@ -13,7 +13,14 @@ class SlackSlashCommandsService < ChatSlashCommandsService 'slack_slash_commands' end - def presenter_format - 'slack' + def trigger(params) + result = super + + # Format messages to be Slack-compatible + if result && result[:text] + result[:text] = Slack::Notifier::LinkFormatter.format(result[:text]) + end + + result end end diff --git a/lib/gitlab/chat_commands/base_command.rb b/lib/gitlab/chat_commands/base_command.rb index 156bb826f86..4fe53ce93a9 100644 --- a/lib/gitlab/chat_commands/base_command.rb +++ b/lib/gitlab/chat_commands/base_command.rb @@ -44,7 +44,7 @@ module Gitlab end def presenter - Gitlab::ChatCommands::Presenter.new(params[:presenter_format]) + Gitlab::ChatCommands::Presenter.new end end end diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index 98356ebebb3..e94d0ce2470 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -3,15 +3,9 @@ module Gitlab class Presenter include Gitlab::Routing.url_helpers - attr_reader :format - - def initialize(format) - @format = format - end - def authorize_chat_name(url) message = if url - ":wave: Hi there! Before I do anything for you, please #{link(url, 'connect your GitLab account')}." + ":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{url})." else ":sweat_smile: Couldn't identify you, nor can I autorize you!" end @@ -49,7 +43,7 @@ module Gitlab end def access_denied - ephemeral_response("Whoops! That action is not allowed. This incident will be #{link('https://xkcd.com/838/', 'reported')}.") + ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).") end private @@ -94,7 +88,7 @@ module Gitlab reference = resource.try(:to_reference) || resource.try(:id) title = resource.try(:title) || resource.try(:name) - link(url(resource), "#{reference} #{title}") + "[#{reference} #{title}](#{url(resource)})" end def header_with_list(header, items) @@ -132,19 +126,6 @@ module Gitlab status: 200 } end - - def link(url, title) - case format - when 'slack' - "<#{url}|#{title}>" - - when 'mattermost' - "[#{title}](#{url})" - - else - title - end - end end end end diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb index dec98d990b2..a0ec8884635 100644 --- a/spec/lib/gitlab/chat_commands/command_spec.rb +++ b/spec/lib/gitlab/chat_commands/command_spec.rb @@ -3,12 +3,10 @@ require 'spec_helper' describe Gitlab::ChatCommands::Command, service: true do let(:project) { create(:empty_project) } let(:user) { create(:user) } - let(:format) { nil } describe '#execute' do subject do - described_class.new(project, user, - params.merge(presenter_format: format)).execute + described_class.new(project, user, params).execute end context 'when no command is available' do @@ -51,14 +49,8 @@ describe Gitlab::ChatCommands::Command, service: true do expect(subject[:text]).to match("my new issue") end - %w(slack mattermost).each do |format| - context "for #{format}" do - let(:format) { format } - - it 'shows a link to the new issue' do - expect(subject[:text]).to match(/\/issues\/\d+/) - end - end + it 'shows a link to the new issue' do + expect(subject[:text]).to match(/\/issues\/\d+/) end end diff --git a/spec/models/project_services/mattermost_notification_service_spec.rb b/spec/models/project_services/mattermost_notification_service_spec.rb index c01e64b4c8e..7832d6f50cf 100644 --- a/spec/models/project_services/mattermost_notification_service_spec.rb +++ b/spec/models/project_services/mattermost_notification_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' describe MattermostNotificationService, models: true do - it_behaves_like "slack or mattermost" + it_behaves_like "slack or mattermost notifications" end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index b9deb0201e1..5c34cb6b4cf 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' describe MattermostSlashCommandsService, models: true do - it { is_expected.to respond_to :presenter_format } + it_behaves_like "chat slash commands" end diff --git a/spec/models/project_services/slack_notification_service_spec.rb b/spec/models/project_services/slack_notification_service_spec.rb index 59ddddf7454..110b5bf2115 100644 --- a/spec/models/project_services/slack_notification_service_spec.rb +++ b/spec/models/project_services/slack_notification_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' describe SlackNotificationService, models: true do - it_behaves_like "slack or mattermost" + it_behaves_like "slack or mattermost notifications" end diff --git a/spec/models/project_services/slack_slash_commands_service.rb b/spec/models/project_services/slack_slash_commands_service.rb index 5ef97b9a2ed..c3fa80caebe 100644 --- a/spec/models/project_services/slack_slash_commands_service.rb +++ b/spec/models/project_services/slack_slash_commands_service.rb @@ -1,5 +1,38 @@ require 'spec_helper' describe SlackSlashCommandsService, models: true do - it { is_expected.to respond_to :presenter_format } + it_behaves_like "chat slash commands" + + describe '#trigger' do + context 'when an auth url is generated' do + let(:project) { create(:empty_project) } + let(:params) do + { + team_domain: 'http://domain.tld', + team_id: 'T3423423', + user_id: 'U234234', + user_name: 'mepmep', + token: 'token' + } + end + let(:service) do + project.create_slack_slash_commands_service( + properties: { token: 'token' } + ) + end + let(:authorize_url) do + 'http://authorize.example.com/' + end + + before do + allow(service).to receive(:authorize_chat_name_url).and_return(authorize_url) + end + + it 'uses slack compatible links' do + response = service.trigger(params) + + expect(response[:text]).to include("<#{authorize_url}|connect your GitLab account>") + end + end + end end diff --git a/spec/models/project_services/chat_slash_commands_service_spec.rb b/spec/support/chat_slash_commands_shared_examples.rb similarity index 93% rename from spec/models/project_services/chat_slash_commands_service_spec.rb rename to spec/support/chat_slash_commands_shared_examples.rb index 64fdd4d570b..96130b45235 100644 --- a/spec/models/project_services/chat_slash_commands_service_spec.rb +++ b/spec/support/chat_slash_commands_shared_examples.rb @@ -1,6 +1,4 @@ -require 'spec_helper' - -describe ChatSlashCommandsService, models: true do +RSpec.shared_examples 'chat slash commands' do describe "Associations" do it { is_expected.to respond_to :token } it { is_expected.to have_many :chat_names } @@ -29,10 +27,6 @@ describe ChatSlashCommandsService, models: true do describe '#trigger' do subject { described_class.new } - before do - allow(subject).to receive(:presenter_format).and_return('unknown') - end - context 'no token is passed' do let(:params) { Hash.new } diff --git a/spec/support/slack_mattermost_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb similarity index 99% rename from spec/support/slack_mattermost_shared_examples.rb rename to spec/support/slack_mattermost_notifications_shared_examples.rb index 56d4965f74d..8582aea5fe5 100644 --- a/spec/support/slack_mattermost_shared_examples.rb +++ b/spec/support/slack_mattermost_notifications_shared_examples.rb @@ -1,6 +1,6 @@ Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f } -RSpec.shared_examples 'slack or mattermost' do +RSpec.shared_examples 'slack or mattermost notifications' do let(:chat_service) { described_class.new } let(:webhook_url) { 'https://example.gitlab.com/' } From c46b758b48400efff5241efafad94c2ddbb8224c Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 16 Dec 2016 17:56:27 +0000 Subject: [PATCH 226/386] Remove unused style for dropdown. Improves animation --- app/assets/stylesheets/pages/pipelines.scss | 48 ++++++++++----------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index a0719b415e5..b3deac3ab75 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -466,6 +466,28 @@ margin-bottom: 10px; white-space: normal; + + .dropdown-menu-toggle { + background-color: transparent; + border: none; + padding: 0; + color: $gl-text-color-light; + flex-grow: 1; + + + &:focus { + outline: none; + } + + &:hover { + color: $gl-text-color; + + .dropdown-counter-badge { + color: $gl-text-color; + } + } + } + &:hover { background-color: $stage-hover-bg; border: 1px solid $stage-hover-border; @@ -635,28 +657,6 @@ font-weight: 100; } - -.dropdown-menu-toggle { - background-color: transparent; - border: none; - padding: 0; - color: $gl-text-color-light; - flex-grow: 1; - - - &:focus { - outline: none; - } - - &:hover { - color: $gl-text-color; - - .dropdown-counter-badge { - color: $gl-text-color; - } - } -} - // Action Icons .ci-action-icon-container .ci-action-icon-wrapper { float: right; @@ -816,7 +816,7 @@ .ci-status-icon { width: 28px; padding: 0 8px 0 0; - transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); + transition: width 0.2s cubic-bezier(0.25, 0, 1, 1); + .dropdown-caret { display: inline-block; @@ -853,7 +853,6 @@ .ci-status-icon-skipped { background-color: rgba($gray-darkest, .1); } - } .mini-pipeline-graph-icon-container { @@ -861,7 +860,6 @@ .ci-status-icon:focus { width: 28px; padding: 0 8px 0 0; - transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); + .dropdown-caret { display: inline-block; From f82d549d26af89cba00005e1a1c9b721c076f7a0 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Wed, 7 Dec 2016 13:25:49 +0530 Subject: [PATCH 227/386] Accept environment variables from the `pre-receive` script. 1. Starting version 2.11, git changed the way the pre-receive flow works. - Previously, the new potential objects would be added to the main repo. If the pre-receive passes, the new objects stay in the repo but are linked up. If the pre-receive fails, the new objects stay orphaned in the repo, and are cleaned up during the next `git gc`. - In 2.11, the new potential objects are added to a temporary "alternate object directory", that git creates for this purpose. If the pre-receive passes, the objects from the alternate object directory are migrated to the main repo. If the pre-receive fails the alternate object directory is simply deleted. 2. In our workflow, the pre-recieve script (in `gitlab-shell) calls the `/allowed` endpoint, which calls out directly to git to perform various checks. These direct calls to git do _not_ have the necessary environment variables set which allow access to the "alternate object directory" (explained above). Therefore these calls to git are not able to access any of the new potential objects to be added during this push. 3. We fix this by accepting the relevant environment variables (GIT_ALTERNATE_OBJECT_DIRECTORIES, GIT_OBJECT_DIRECTORY) on the `/allowed` endpoint, and then include these environment variables while calling out to git. 4. This commit includes (whitelisted) these environment variables while making the "force push" check. A `Gitlab::Git::RevList` module is extracted to prevent `ForcePush` from being littered with these checks. --- lib/api/helpers/internal_helpers.rb | 8 ++++++++ lib/api/internal.rb | 6 +++++- lib/gitlab/checks/change_access.rb | 5 +++-- lib/gitlab/checks/force_push.rb | 4 ++-- lib/gitlab/git/rev_list.rb | 28 ++++++++++++++++++++++++++++ lib/gitlab/git_access.rb | 5 +++-- lib/gitlab/popen.rb | 4 ++-- 7 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 lib/gitlab/git/rev_list.rb diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index eb223c1101d..e8975eb57e0 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -52,6 +52,14 @@ module API :push_code ] end + + def parse_allowed_environment_variables + return if params[:env].blank? + + JSON.parse(params[:env]) + + rescue JSON::ParserError + end end end end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 7087ce11401..db2d18f935d 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -32,7 +32,11 @@ module API if wiki? Gitlab::GitAccessWiki.new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities) else - Gitlab::GitAccess.new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities) + Gitlab::GitAccess.new(actor, + project, + protocol, + authentication_abilities: ssh_authentication_abilities, + env: parse_allowed_environment_variables) end access_status = access.check(params[:action], params[:changes]) diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb index cb1065223d4..3d203017d9f 100644 --- a/lib/gitlab/checks/change_access.rb +++ b/lib/gitlab/checks/change_access.rb @@ -3,11 +3,12 @@ module Gitlab class ChangeAccess attr_reader :user_access, :project - def initialize(change, user_access:, project:) + def initialize(change, user_access:, project:, env: {}) @oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref) @branch_name = Gitlab::Git.branch_name(@ref) @user_access = user_access @project = project + @env = env end def exec @@ -68,7 +69,7 @@ module Gitlab end def forced_push? - Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev) + Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev, env: @env) end def matching_merge_request? diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb index 5fe86553bd0..589525e40ad 100644 --- a/lib/gitlab/checks/force_push.rb +++ b/lib/gitlab/checks/force_push.rb @@ -1,14 +1,14 @@ module Gitlab module Checks class ForcePush - def self.force_push?(project, oldrev, newrev) + def self.force_push?(project, oldrev, newrev, env: {}) return false if project.empty_repo? # Created or deleted branch if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) false else - missed_ref, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list --max-count=1 #{oldrev} ^#{newrev})) + missed_ref, _ = Gitlab::Git::RevList.new(oldrev, newrev, project: project, env: env).execute missed_ref.present? end end diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb new file mode 100644 index 00000000000..ecdb7f07744 --- /dev/null +++ b/lib/gitlab/git/rev_list.rb @@ -0,0 +1,28 @@ +# Call out to the `git rev-list` command + +module Gitlab + module Git + class RevList + def initialize(oldrev, newrev, project:, env: nil) + @args = [Gitlab.config.git.bin_path, + "--git-dir=#{project.repository.path_to_repo}", + "rev-list", + "--max-count=1", + oldrev, + "^#{newrev}"] + + @env = env.slice(*allowed_environment_variables) + end + + def execute + Gitlab::Popen.popen(@args, nil, @env.slice(*allowed_environment_variables)) + end + + private + + def allowed_environment_variables + %w(GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_OBJECT_DIRECTORY) + end + end + end +end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index db07b7c5fcc..c6b6efda360 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -17,12 +17,13 @@ module Gitlab attr_reader :actor, :project, :protocol, :user_access, :authentication_abilities - def initialize(actor, project, protocol, authentication_abilities:) + def initialize(actor, project, protocol, authentication_abilities:, env: {}) @actor = actor @project = project @protocol = protocol @authentication_abilities = authentication_abilities @user_access = UserAccess.new(user, project: project) + @env = env end def check(cmd, changes) @@ -103,7 +104,7 @@ module Gitlab end def change_access_check(change) - Checks::ChangeAccess.new(change, user_access: user_access, project: project).exec + Checks::ChangeAccess.new(change, user_access: user_access, project: project, env: @env).exec end def protocol_allowed? diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb index cc74bb29087..4bc5cda8cb5 100644 --- a/lib/gitlab/popen.rb +++ b/lib/gitlab/popen.rb @@ -5,13 +5,13 @@ module Gitlab module Popen extend self - def popen(cmd, path = nil) + def popen(cmd, path = nil, vars = {}) unless cmd.is_a?(Array) raise "System commands must be given as an array of strings" end path ||= Dir.pwd - vars = { "PWD" => path } + vars['PWD'] = path options = { chdir: path } unless File.directory?(path) From a2b39feb1a3ae6fe2615418bb759bf39125e5d0e Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 9 Dec 2016 15:15:55 +0530 Subject: [PATCH 228/386] Validate environment variables in `Gitlab::Git::RevList` The list of environment variables in `Gitlab::Git::RevList` need to be validate to make sure that they don't reference any other project on disk. This commit mixes in `ActiveModel::Validations` into `Gitlab::Git::RevList`, and validates that the environment variables are on the level (using a custom validator class). If the validations fail, the force push is still executed without any environment variables set. Add specs for the validation using shared examples. --- .../git_environment_variables_validator.rb | 13 ++++ lib/gitlab/git/rev_list.rb | 16 ++++- spec/lib/gitlab/git/rev_list_spec.rb | 36 +++++++++++ ...it_environment_variables_validator_spec.rb | 64 +++++++++++++++++++ 4 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 app/validators/git_environment_variables_validator.rb create mode 100644 spec/lib/gitlab/git/rev_list_spec.rb create mode 100644 spec/validators/git_environment_variables_validator_spec.rb diff --git a/app/validators/git_environment_variables_validator.rb b/app/validators/git_environment_variables_validator.rb new file mode 100644 index 00000000000..92041e0a773 --- /dev/null +++ b/app/validators/git_environment_variables_validator.rb @@ -0,0 +1,13 @@ +class GitEnvironmentVariablesValidator < ActiveModel::EachValidator + def validate_each(record, attribute, env) + variables_to_validate = %w(GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES) + + variables_to_validate.each do |variable_name| + variable_value = env[variable_name] + + if variable_value.present? && !(variable_value =~ /^#{record.project.repository.path_to_repo}/) + record.errors.add(attribute, "The #{variable_name} variable must start with the project repo path") + end + end + end +end diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb index ecdb7f07744..d8c78d806ea 100644 --- a/lib/gitlab/git/rev_list.rb +++ b/lib/gitlab/git/rev_list.rb @@ -3,19 +3,29 @@ module Gitlab module Git class RevList + include ActiveModel::Validations + + validates :env, git_environment_variables: true + + attr_reader :project, :env + def initialize(oldrev, newrev, project:, env: nil) + @project = project + @env = env.presence || {} @args = [Gitlab.config.git.bin_path, "--git-dir=#{project.repository.path_to_repo}", "rev-list", "--max-count=1", oldrev, "^#{newrev}"] - - @env = env.slice(*allowed_environment_variables) end def execute - Gitlab::Popen.popen(@args, nil, @env.slice(*allowed_environment_variables)) + if self.valid? + Gitlab::Popen.popen(@args, nil, @env.slice(*allowed_environment_variables)) + else + Gitlab::Popen.popen(@args) + end end private diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb new file mode 100644 index 00000000000..cdfbff5658c --- /dev/null +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' +require 'validators/git_environment_variables_validator_spec' + +describe Gitlab::Git::RevList, lib: true do + let(:project) { create(:project) } + + context "validations" do + it_behaves_like( + "validated git environment variables", + ->(env, project) { Gitlab::Git::RevList.new('oldrev', 'newrev', project: project, env: env) } + ) + end + + context "#execute" do + let(:env) { { "GIT_OBJECT_DIRECTORY" => project.repository.path_to_repo } } + let(:rev_list) { Gitlab::Git::RevList.new('oldrev', 'newrev', project: project, env: env) } + + it "calls out to `popen` without environment variables if the record is invalid" do + allow(rev_list).to receive(:valid?).and_return(false) + allow(Open3).to receive(:popen3) + + rev_list.execute + + expect(Open3).to have_received(:popen3).with(hash_excluding(env), any_args) + end + + it "calls out to `popen` with environment variables if the record is valid" do + allow(rev_list).to receive(:valid?).and_return(true) + allow(Open3).to receive(:popen3) + + rev_list.execute + + expect(Open3).to have_received(:popen3).with(hash_including(env), any_args) + end + end +end diff --git a/spec/validators/git_environment_variables_validator_spec.rb b/spec/validators/git_environment_variables_validator_spec.rb new file mode 100644 index 00000000000..81b028b6572 --- /dev/null +++ b/spec/validators/git_environment_variables_validator_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +shared_examples_for "validated git environment variables" do |record_fn| + subject { GitEnvironmentVariablesValidator.new(attributes: ['env']) } + let(:project) { create(:project) } + + context "GIT_OBJECT_DIRECTORY" do + it "accepts values starting with the project repo path" do + env = { "GIT_OBJECT_DIRECTORY" => "#{project.repository.path_to_repo}/objects" } + record = record_fn[env, project] + + subject.validate_each(record, 'env', env) + + expect(record).to be_valid, "expected #{project.repository.path_to_repo}" + end + + it "rejects values starting not with the project repo path" do + env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path" } + record = record_fn[env, project] + + subject.validate_each(record, 'env', env) + + expect(record).to be_invalid + end + + it "rejects values containing the project repo path but not starting with it" do + env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path/#{project.repository.path_to_repo}" } + record = record_fn[env, project] + + subject.validate_each(record, 'env', env) + + expect(record).to be_invalid + end + end + + context "GIT_ALTERNATE_OBJECT_DIRECTORIES" do + it "accepts values starting with the project repo path" do + env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => project.repository.path_to_repo } + record = record_fn[env, project] + + subject.validate_each(record, 'env', env) + + expect(record).to be_valid, "expected #{project.repository.path_to_repo}" + end + + it "rejects values starting not with the project repo path" do + env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path" } + record = record_fn[env, project] + + subject.validate_each(record, 'env', env) + + expect(record).to be_invalid + end + + it "rejects values containing the project repo path but not starting with it" do + env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path/#{project.repository.path_to_repo}" } + record = record_fn[env, project] + + subject.validate_each(record, 'env', env) + + expect(record).to be_invalid + end + end +end From c937aec1f7ba1f102995fefaef2141e7ed90f5fd Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 9 Dec 2016 15:52:20 +0530 Subject: [PATCH 229/386] Check the exit code while invoking git in the force push check. Previously, we were calling out to `popen` without asserting on the returned exit-code. Now we raise a `RuntimeError` if the exit code is non-zero. --- lib/gitlab/checks/force_push.rb | 9 +++++++-- spec/lib/gitlab/checks/force_push_spec.rb | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 spec/lib/gitlab/checks/force_push_spec.rb diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb index 589525e40ad..e1c967a1f89 100644 --- a/lib/gitlab/checks/force_push.rb +++ b/lib/gitlab/checks/force_push.rb @@ -8,8 +8,13 @@ module Gitlab if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) false else - missed_ref, _ = Gitlab::Git::RevList.new(oldrev, newrev, project: project, env: env).execute - missed_ref.present? + missed_ref, exit_status = Gitlab::Git::RevList.new(oldrev, newrev, project: project, env: env).execute + + if exit_status == 0 + missed_ref.present? + else + raise RuntimeError, "Got a non-zero exit code while calling out to `git rev-list` in the force-push check." + end end end end diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb new file mode 100644 index 00000000000..f6288011494 --- /dev/null +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Gitlab::Checks::ChangeAccess, lib: true do + let(:project) { create(:project) } + + context "exit code checking" do + it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do + allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0]) + + expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error + end + + it "raises a runtime error if the `popen` call to git returns a non-zero exit code" do + allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1]) + + expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) + end + end +end From a80ccec70687de2d7c53026fa25d7b85098cdb9a Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 13 Dec 2016 18:49:16 +0530 Subject: [PATCH 230/386] Add CHANGELOG entry. --- changelogs/unreleased/25301-git-2-11-force-push-bug.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/25301-git-2-11-force-push-bug.yml diff --git a/changelogs/unreleased/25301-git-2-11-force-push-bug.yml b/changelogs/unreleased/25301-git-2-11-force-push-bug.yml new file mode 100644 index 00000000000..afe57729c48 --- /dev/null +++ b/changelogs/unreleased/25301-git-2-11-force-push-bug.yml @@ -0,0 +1,4 @@ +--- +title: Accept environment variables from the `pre-receive` script +merge_request: 7967 +author: From 3e1442766f3e2327e1e620b3b11623b09c35142b Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 15 Dec 2016 07:53:46 +0530 Subject: [PATCH 231/386] Implement review comments from @dbalexandre. - Don't define "allowed environment variables" in two places. - Dispatch to different arities of `Popen.open` without an if/else block. - Use `described_class` instead of explicitly stating the class name within a - spec. - Remove `git_environment_variables_validator_spec` and keep the validation inline. --- .../git_environment_variables_validator.rb | 13 ---- lib/gitlab/git/rev_list.rb | 22 ++++--- spec/lib/gitlab/git/rev_list_spec.rb | 50 +++++++++++++-- ...it_environment_variables_validator_spec.rb | 64 ------------------- 4 files changed, 57 insertions(+), 92 deletions(-) delete mode 100644 app/validators/git_environment_variables_validator.rb delete mode 100644 spec/validators/git_environment_variables_validator_spec.rb diff --git a/app/validators/git_environment_variables_validator.rb b/app/validators/git_environment_variables_validator.rb deleted file mode 100644 index 92041e0a773..00000000000 --- a/app/validators/git_environment_variables_validator.rb +++ /dev/null @@ -1,13 +0,0 @@ -class GitEnvironmentVariablesValidator < ActiveModel::EachValidator - def validate_each(record, attribute, env) - variables_to_validate = %w(GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES) - - variables_to_validate.each do |variable_name| - variable_value = env[variable_name] - - if variable_value.present? && !(variable_value =~ /^#{record.project.repository.path_to_repo}/) - record.errors.add(attribute, "The #{variable_name} variable must start with the project repo path") - end - end - end -end diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb index d8c78d806ea..ecd038e04df 100644 --- a/lib/gitlab/git/rev_list.rb +++ b/lib/gitlab/git/rev_list.rb @@ -3,12 +3,10 @@ module Gitlab module Git class RevList - include ActiveModel::Validations - - validates :env, git_environment_variables: true - attr_reader :project, :env + ALLOWED_VARIABLES = %w(GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES).freeze + def initialize(oldrev, newrev, project:, env: nil) @project = project @env = env.presence || {} @@ -21,17 +19,21 @@ module Gitlab end def execute - if self.valid? - Gitlab::Popen.popen(@args, nil, @env.slice(*allowed_environment_variables)) - else - Gitlab::Popen.popen(@args) + Gitlab::Popen.popen(@args, nil, parse_environment_variables) + end + + def valid? + env.slice(*ALLOWED_VARIABLES).all? do |(name, value)| + value =~ /^#{project.repository.path_to_repo}/ end end private - def allowed_environment_variables - %w(GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_OBJECT_DIRECTORY) + def parse_environment_variables + return {} unless valid? + + env.slice(*ALLOWED_VARIABLES) end end end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index cdfbff5658c..f76aca29107 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -1,14 +1,54 @@ require 'spec_helper' -require 'validators/git_environment_variables_validator_spec' describe Gitlab::Git::RevList, lib: true do let(:project) { create(:project) } context "validations" do - it_behaves_like( - "validated git environment variables", - ->(env, project) { Gitlab::Git::RevList.new('oldrev', 'newrev', project: project, env: env) } - ) + context "GIT_OBJECT_DIRECTORY" do + it "accepts values starting with the project repo path" do + env = { "GIT_OBJECT_DIRECTORY" => "#{project.repository.path_to_repo}/objects" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).to be_valid + end + + it "rejects values starting not with the project repo path" do + env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).not_to be_valid + end + + it "rejects values containing the project repo path but not starting with it" do + env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path/#{project.repository.path_to_repo}" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).not_to be_valid + end + end + + context "GIT_ALTERNATE_OBJECT_DIRECTORIES" do + it "accepts values starting with the project repo path" do + env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => project.repository.path_to_repo } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).to be_valid + end + + it "rejects values starting not with the project repo path" do + env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).not_to be_valid + end + + it "rejects values containing the project repo path but not starting with it" do + env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path/#{project.repository.path_to_repo}" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + + expect(rev_list).not_to be_valid + end + end end context "#execute" do diff --git a/spec/validators/git_environment_variables_validator_spec.rb b/spec/validators/git_environment_variables_validator_spec.rb deleted file mode 100644 index 81b028b6572..00000000000 --- a/spec/validators/git_environment_variables_validator_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'spec_helper' - -shared_examples_for "validated git environment variables" do |record_fn| - subject { GitEnvironmentVariablesValidator.new(attributes: ['env']) } - let(:project) { create(:project) } - - context "GIT_OBJECT_DIRECTORY" do - it "accepts values starting with the project repo path" do - env = { "GIT_OBJECT_DIRECTORY" => "#{project.repository.path_to_repo}/objects" } - record = record_fn[env, project] - - subject.validate_each(record, 'env', env) - - expect(record).to be_valid, "expected #{project.repository.path_to_repo}" - end - - it "rejects values starting not with the project repo path" do - env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path" } - record = record_fn[env, project] - - subject.validate_each(record, 'env', env) - - expect(record).to be_invalid - end - - it "rejects values containing the project repo path but not starting with it" do - env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path/#{project.repository.path_to_repo}" } - record = record_fn[env, project] - - subject.validate_each(record, 'env', env) - - expect(record).to be_invalid - end - end - - context "GIT_ALTERNATE_OBJECT_DIRECTORIES" do - it "accepts values starting with the project repo path" do - env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => project.repository.path_to_repo } - record = record_fn[env, project] - - subject.validate_each(record, 'env', env) - - expect(record).to be_valid, "expected #{project.repository.path_to_repo}" - end - - it "rejects values starting not with the project repo path" do - env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path" } - record = record_fn[env, project] - - subject.validate_each(record, 'env', env) - - expect(record).to be_invalid - end - - it "rejects values containing the project repo path but not starting with it" do - env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path/#{project.repository.path_to_repo}" } - record = record_fn[env, project] - - subject.validate_each(record, 'env', env) - - expect(record).to be_invalid - end - end -end From e394d2872aa3a95d0e5cd13afe8e0de1ab01213a Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 16 Dec 2016 22:39:52 +0530 Subject: [PATCH 232/386] Implement final review comments from @rymai. - `raise "string"` raises a `RuntimeError` - no need to be explicit - Remove top-level comment in the `RevList` class - Use `%w()` instead of `%w[]` - Extract an `environment_variables` method to cache `env.slice(*ALLOWED_VARIABLES)` - Use `start_with?` for env variable validation instead of regex match - Validation specs for each allowed environment variable were identical. Build them dynamically. - Minor change to `popen3` expectation. --- lib/gitlab/checks/force_push.rb | 2 +- lib/gitlab/git/rev_list.rb | 14 +++--- spec/lib/gitlab/git/rev_list_spec.rb | 65 +++++++++------------------- 3 files changed, 30 insertions(+), 51 deletions(-) diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb index e1c967a1f89..de0c9049ebf 100644 --- a/lib/gitlab/checks/force_push.rb +++ b/lib/gitlab/checks/force_push.rb @@ -13,7 +13,7 @@ module Gitlab if exit_status == 0 missed_ref.present? else - raise RuntimeError, "Got a non-zero exit code while calling out to `git rev-list` in the force-push check." + raise "Got a non-zero exit code while calling out to `git rev-list` in the force-push check." end end end diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb index ecd038e04df..25e9d619697 100644 --- a/lib/gitlab/git/rev_list.rb +++ b/lib/gitlab/git/rev_list.rb @@ -1,11 +1,9 @@ -# Call out to the `git rev-list` command - module Gitlab module Git class RevList attr_reader :project, :env - ALLOWED_VARIABLES = %w(GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES).freeze + ALLOWED_VARIABLES = %w[GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES].freeze def initialize(oldrev, newrev, project:, env: nil) @project = project @@ -23,8 +21,8 @@ module Gitlab end def valid? - env.slice(*ALLOWED_VARIABLES).all? do |(name, value)| - value =~ /^#{project.repository.path_to_repo}/ + environment_variables.all? do |(name, value)| + value.start_with?(project.repository.path_to_repo) end end @@ -33,7 +31,11 @@ module Gitlab def parse_environment_variables return {} unless valid? - env.slice(*ALLOWED_VARIABLES) + environment_variables + end + + def environment_variables + @environment_variables ||= env.slice(*ALLOWED_VARIABLES) end end end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index f76aca29107..444639acbaa 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -4,49 +4,28 @@ describe Gitlab::Git::RevList, lib: true do let(:project) { create(:project) } context "validations" do - context "GIT_OBJECT_DIRECTORY" do - it "accepts values starting with the project repo path" do - env = { "GIT_OBJECT_DIRECTORY" => "#{project.repository.path_to_repo}/objects" } - rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + described_class::ALLOWED_VARIABLES.each do |var| + context var do + it "accepts values starting with the project repo path" do + env = { var => "#{project.repository.path_to_repo}/objects" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) - expect(rev_list).to be_valid - end + expect(rev_list).to be_valid + end - it "rejects values starting not with the project repo path" do - env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path" } - rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + it "rejects values starting not with the project repo path" do + env = { var => "/some/other/path" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) - expect(rev_list).not_to be_valid - end + expect(rev_list).not_to be_valid + end - it "rejects values containing the project repo path but not starting with it" do - env = { "GIT_OBJECT_DIRECTORY" => "/some/other/path/#{project.repository.path_to_repo}" } - rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) + it "rejects values containing the project repo path but not starting with it" do + env = { var => "/some/other/path/#{project.repository.path_to_repo}" } + rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) - expect(rev_list).not_to be_valid - end - end - - context "GIT_ALTERNATE_OBJECT_DIRECTORIES" do - it "accepts values starting with the project repo path" do - env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => project.repository.path_to_repo } - rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) - - expect(rev_list).to be_valid - end - - it "rejects values starting not with the project repo path" do - env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path" } - rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) - - expect(rev_list).not_to be_valid - end - - it "rejects values containing the project repo path but not starting with it" do - env = { "GIT_ALTERNATE_OBJECT_DIRECTORIES" => "/some/other/path/#{project.repository.path_to_repo}" } - rev_list = described_class.new('oldrev', 'newrev', project: project, env: env) - - expect(rev_list).not_to be_valid + expect(rev_list).not_to be_valid + end end end end @@ -57,20 +36,18 @@ describe Gitlab::Git::RevList, lib: true do it "calls out to `popen` without environment variables if the record is invalid" do allow(rev_list).to receive(:valid?).and_return(false) - allow(Open3).to receive(:popen3) + + expect(Open3).to receive(:popen3).with(hash_excluding(env), any_args) rev_list.execute - - expect(Open3).to have_received(:popen3).with(hash_excluding(env), any_args) end it "calls out to `popen` with environment variables if the record is valid" do allow(rev_list).to receive(:valid?).and_return(true) - allow(Open3).to receive(:popen3) + + expect(Open3).to receive(:popen3).with(hash_including(env), any_args) rev_list.execute - - expect(Open3).to have_received(:popen3).with(hash_including(env), any_args) end end end From 1aa3294cd2791ec0386059256560d22f7650620b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 16 Dec 2016 18:07:10 +0000 Subject: [PATCH 233/386] Revert unwanted changes to the db/schema.rb file --- db/schema.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 78e3a356f74..3786a41f9a9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -98,14 +98,14 @@ ActiveRecord::Schema.define(version: 20161213172958) do t.text "help_page_text_html" t.text "shared_runners_text_html" t.text "after_sign_up_text_html" + t.boolean "sidekiq_throttling_enabled", default: false + t.string "sidekiq_throttling_queues" + t.decimal "sidekiq_throttling_factor" t.boolean "housekeeping_enabled", default: true, null: false t.boolean "housekeeping_bitmaps_enabled", default: true, null: false t.integer "housekeeping_incremental_repack_period", default: 10, null: false t.integer "housekeeping_full_repack_period", default: 50, null: false t.integer "housekeeping_gc_period", default: 200, null: false - t.boolean "sidekiq_throttling_enabled", default: false - t.string "sidekiq_throttling_queues" - t.decimal "sidekiq_throttling_factor" t.boolean "html_emails_enabled", default: true end @@ -527,7 +527,6 @@ ActiveRecord::Schema.define(version: 20161213172958) do t.string "type" t.string "fingerprint" t.boolean "public", default: false, null: false - t.boolean "can_push", default: false, null: false end add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree @@ -740,8 +739,8 @@ ActiveRecord::Schema.define(version: 20161213172958) do t.integer "visibility_level", default: 20, null: false t.boolean "request_access_enabled", default: false, null: false t.datetime "deleted_at" - t.text "description_html" t.boolean "lfs_enabled" + t.text "description_html" t.integer "parent_id" end @@ -1222,8 +1221,8 @@ ActiveRecord::Schema.define(version: 20161213172958) do t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false t.boolean "external", default: false - t.string "incoming_email_token" t.string "organization" + t.string "incoming_email_token" t.boolean "authorized_projects_populated" end From 170efaaba273792ddffc2806ef1501f33d87a5a2 Mon Sep 17 00:00:00 2001 From: Rydkin Maxim Date: Fri, 16 Dec 2016 01:14:20 +0300 Subject: [PATCH 234/386] Enable Style/MultilineOperationIndentation in Rubocop, fixes #25741 --- .rubocop.yml | 3 ++- app/controllers/jwt_controller.rb | 2 +- app/controllers/sessions_controller.rb | 2 +- app/helpers/form_helper.rb | 12 ++++++------ app/helpers/nav_helper.rb | 18 +++++++++--------- app/helpers/tab_helper.rb | 6 +++--- app/models/member.rb | 4 ++-- app/models/network/graph.rb | 4 ++-- .../project_services/issue_tracker_service.rb | 4 ++-- app/models/user.rb | 2 +- app/policies/note_policy.rb | 2 +- app/policies/project_policy.rb | 12 ++++++------ app/services/groups/update_service.rb | 2 +- app/services/issues/update_service.rb | 2 +- app/services/merge_requests/build_service.rb | 2 +- app/services/merge_requests/update_service.rb | 2 +- app/services/projects/update_service.rb | 2 +- config/initializers/sidekiq.rb | 2 +- lib/gitlab/email/reply_parser.rb | 2 +- spec/lib/gitlab/gfm/reference_rewriter_spec.rb | 2 +- spec/requests/api/projects_spec.rb | 2 +- 21 files changed, 45 insertions(+), 44 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 13df3f99613..80eb4a5c19e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -292,7 +292,8 @@ Style/MultilineMethodDefinitionBraceLayout: # Checks indentation of binary operations that span more than one line. Style/MultilineOperationIndentation: - Enabled: false + Enabled: true + EnforcedStyle: indented # Avoid multi-line `? :` (the ternary operator), use if/unless instead. Style/MultilineTernaryOperator: diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index c736200a104..c2e4d62b50b 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -26,7 +26,7 @@ class JwtController < ApplicationController @authentication_result = Gitlab::Auth.find_for_git_client(login, password, project: nil, ip: request.ip) render_unauthorized unless @authentication_result.success? && - (@authentication_result.actor.nil? || @authentication_result.actor.is_a?(User)) + (@authentication_result.actor.nil? || @authentication_result.actor.is_a?(User)) end rescue Gitlab::Auth::MissingPersonalTokenError render_missing_personal_token diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 8c698695202..93a180b9036 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -114,7 +114,7 @@ class SessionsController < Devise::SessionsController def valid_otp_attempt?(user) user.validate_and_consume_otp!(user_params[:otp_attempt]) || - user.invalidate_otp_backup_code!(user_params[:otp_attempt]) + user.invalidate_otp_backup_code!(user_params[:otp_attempt]) end def log_audit_event(user, options = {}) diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index 6a43be2cf3e..1182939f656 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -7,12 +7,12 @@ module FormHelper content_tag(:div, class: 'alert alert-danger', id: 'error_explanation') do content_tag(:h4, headline) << - content_tag(:ul) do - model.errors.full_messages. - map { |msg| content_tag(:li, msg) }. - join. - html_safe - end + content_tag(:ul) do + model.errors.full_messages. + map { |msg| content_tag(:li, msg) }. + join. + html_safe + end end end end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index a3331dc80cb..e21178c7377 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -7,12 +7,12 @@ module NavHelper def page_gutter_class if current_path?('merge_requests#show') || - current_path?('merge_requests#diffs') || - current_path?('merge_requests#commits') || - current_path?('merge_requests#builds') || - current_path?('merge_requests#conflicts') || - current_path?('merge_requests#pipelines') || - current_path?('issues#show') + current_path?('merge_requests#diffs') || + current_path?('merge_requests#commits') || + current_path?('merge_requests#builds') || + current_path?('merge_requests#conflicts') || + current_path?('merge_requests#pipelines') || + current_path?('issues#show') if cookies[:collapsed_gutter] == 'true' "page-gutter right-sidebar-collapsed" else @@ -21,9 +21,9 @@ module NavHelper elsif current_path?('builds#show') "page-gutter build-sidebar right-sidebar-expanded" elsif current_path?('wikis#show') || - current_path?('wikis#edit') || - current_path?('wikis#history') || - current_path?('wikis#git_access') + current_path?('wikis#edit') || + current_path?('wikis#history') || + current_path?('wikis#git_access') "page-gutter wiki-sidebar right-sidebar-expanded" end end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 563ddd2a511..547f6258909 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -106,9 +106,9 @@ module TabHelper def branches_tab_class if current_controller?(:protected_branches) || - current_controller?(:branches) || - current_page?(namespace_project_repository_path(@project.namespace, - @project)) + current_controller?(:branches) || + current_page?(namespace_project_repository_path(@project.namespace, + @project)) 'active' end end diff --git a/app/models/member.rb b/app/models/member.rb index 3b65587c66b..d29b3c68cf6 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -89,8 +89,8 @@ class Member < ActiveRecord::Base member = if user.is_a?(User) source.members.find_by(user_id: user.id) || - source.requesters.find_by(user_id: user.id) || - source.members.build(user_id: user.id) + source.requesters.find_by(user_id: user.id) || + source.members.build(user_id: user.id) else source.members.build(invite_email: user) end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index 345041a6ad1..b524ca50ee8 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -161,8 +161,8 @@ module Network def is_overlap?(range, overlap_space) range.each do |i| if i != range.first && - i != range.last && - @commits[i].spaces.include?(overlap_space) + i != range.last && + @commits[i].spaces.include?(overlap_space) return true end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 207bb816ad1..bce2cdd5516 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -85,8 +85,8 @@ class IssueTrackerService < Service def enabled_in_gitlab_config Gitlab.config.issues_tracker && - Gitlab.config.issues_tracker.values.any? && - issues_tracker + Gitlab.config.issues_tracker.values.any? && + issues_tracker end def issues_tracker diff --git a/app/models/user.rb b/app/models/user.rb index 1bd28203523..3f8bbbc425d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -390,7 +390,7 @@ class User < ActiveRecord::Base def namespace_uniq # Return early if username already failed the first uniqueness validation return if errors.key?(:username) && - errors[:username].include?('has already been taken') + errors[:username].include?('has already been taken') existing_namespace = Namespace.by_path(username) if existing_namespace && existing_namespace != namespace diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb index 83847466ee2..5326061bd07 100644 --- a/app/policies/note_policy.rb +++ b/app/policies/note_policy.rb @@ -12,7 +12,7 @@ class NotePolicy < BasePolicy end if @subject.for_merge_request? && - @subject.noteable.author == @user + @subject.noteable.author == @user can! :resolve_note end end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index d5aadfce76a..b5db9c12622 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -3,7 +3,7 @@ class ProjectPolicy < BasePolicy team_access!(user) owner = project.owner == user || - (project.group && project.group.has_owner?(user)) + (project.group && project.group.has_owner?(user)) owner_access! if user.admin? || owner team_member_owner_access! if owner @@ -13,7 +13,7 @@ class ProjectPolicy < BasePolicy public_access! if project.request_access_enabled && - !(owner || user.admin? || project.team.member?(user) || project_group_member?(user)) + !(owner || user.admin? || project.team.member?(user) || project_group_member?(user)) can! :request_access end end @@ -244,10 +244,10 @@ class ProjectPolicy < BasePolicy def project_group_member?(user) project.group && - ( - project.group.members.exists?(user_id: user.id) || - project.group.requesters.exists?(user_id: user.id) - ) + ( + project.group.members.exists?(user_id: user.id) || + project.group.requesters.exists?(user_id: user.id) + ) end def named_abilities(name) diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index 99ad12b1003..fff2273f402 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -5,7 +5,7 @@ module Groups new_visibility = params[:visibility_level] if new_visibility && new_visibility.to_i != group.visibility_level unless can?(current_user, :change_visibility_level, group) && - Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) + Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) deny_visibility_level(group, new_visibility) return group diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index a2111b3806b..78cbf94ec69 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -10,7 +10,7 @@ module Issues end if issue.previous_changes.include?('title') || - issue.previous_changes.include?('description') + issue.previous_changes.include?('description') todo_service.update_issue(issue, current_user) end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index bebfca7537b..6a7393a9921 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -42,7 +42,7 @@ module MergeRequests end if merge_request.source_project == merge_request.target_project && - merge_request.target_branch == merge_request.source_branch + merge_request.target_branch == merge_request.source_branch messages << 'You must select different branches' end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index fda0da19d87..ad16ef8c70f 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -25,7 +25,7 @@ module MergeRequests end if merge_request.previous_changes.include?('title') || - merge_request.previous_changes.include?('description') + merge_request.previous_changes.include?('description') todo_service.update_merge_request(merge_request, current_user) end diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 921ca6748d3..8a6af8d8ada 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -6,7 +6,7 @@ module Projects if new_visibility && new_visibility.to_i != project.visibility_level unless can?(current_user, :change_visibility_level, project) && - Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) + Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) deny_visibility_level(project, new_visibility) return project diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 1d7a3f03ace..5a7365bb0f6 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -34,7 +34,7 @@ Sidekiq.configure_server do |config| # Database pool should be at least `sidekiq_concurrency` + 2 # For more info, see: https://github.com/mperham/sidekiq/blob/master/4.0-Upgrade.md config = ActiveRecord::Base.configurations[Rails.env] || - Rails.application.config.database_configuration[Rails.env] + Rails.application.config.database_configuration[Rails.env] config['pool'] = Sidekiq.options[:concurrency] + 2 ActiveRecord::Base.establish_connection(config) Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}") diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 85402c2a278..f586c5ab062 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -69,7 +69,7 @@ module Gitlab # This one might be controversial but so many reply lines have years, times and end with a colon. # Let's try it and see how well it works. break if (l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) || - (l =~ /On \w+ \d+,? \d+,?.*wrote:/) + (l =~ /On \w+ \d+,? \d+,?.*wrote:/) # Headers on subsequent lines break if (0..2).all? { |off| lines[idx + off] =~ REPLYING_HEADER_REGEX } diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index d619e401897..f4703dc704f 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::Gfm::ReferenceRewriter do context 'description with ignored elements' do let(:text) do "Hi. This references #1, but not `#2`\n" + - '