diff options
30 files changed, 472 insertions, 141 deletions
diff --git a/CHANGELOG b/CHANGELOG index a9102ea4206..e6fb2e9f508 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,21 @@ v 8.5.0 (unreleased) - Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel) - Don't vendor minified JS - Display 404 error on group not found + - Track project import failure + - Fix visibility level text in admin area (Zeger-Jan van de Weg) + - Update the ExternalIssue regex pattern (Blake Hitchcock) + +v 8.4.2 (unreleased) + - Bump required gitlab-workhorse version to bring in a fix for missing + artifacts in the build artifacts browser + - Get rid of those ugly borders on the file tree view + - Bump gitlab_git version to 7.2.24 in order to bring in a performance + improvement when checking if a repository was empty + - Add instrumentation for Gitlab::Git::Repository instance methods so we can + track them in Performance Monitoring. + +v 8.4.2 (unreleased) + - Fix method undefined when using external commit status in builds v 8.4.1 - Apply security updates for Rails (4.2.5.1), rails-html-sanitizer (1.0.3), diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index ee6cdce3c29..b6160487433 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.6.1 +0.6.2 diff --git a/app/assets/javascripts/shortcuts_issuable.coffee b/app/assets/javascripts/shortcuts_issuable.coffee index bb532194682..f717a753cf8 100644 --- a/app/assets/javascripts/shortcuts_issuable.coffee +++ b/app/assets/javascripts/shortcuts_issuable.coffee @@ -5,11 +5,11 @@ class @ShortcutsIssuable extends ShortcutsNavigation constructor: (isMergeRequest) -> super() Mousetrap.bind('a', -> - $('.js-assignee').select2('open') + $('.block.assignee .edit-link').trigger('click') return false ) Mousetrap.bind('m', -> - $('.js-milestone').select2('open') + $('.block.milestone .edit-link').trigger('click') return false ) Mousetrap.bind('r', => diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index a4791cf6b34..00cb756b376 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -7,6 +7,7 @@ border: 1px solid $border-color; &.readme-holder { + margin-top: 10px; border-bottom: 0; } diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index b5134f44ded..75b770ae5a2 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -38,7 +38,7 @@ table { td { border-color: $table-border-color; - border-bottom: 1px solid; + border-bottom: 1px solid $border-color; } } } diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 263993f59a5..fdd86979a36 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -1,5 +1,15 @@ .member-search-form { float: left; + + input[type='search'] { + width: 225px; + vertical-align: bottom; + + @media (max-width: $screen-xs-max) { + width: 100px; + vertical-align: bottom; + } + } } .milestone-row { diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 6a6dd7dfc85..c7411617cb3 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -22,8 +22,6 @@ &:hover { td { background: $hover; - border-top: 1px solid #ADF; - border-bottom: 1px solid #ADF; } cursor: pointer; } diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 3d175a0717e..632145c2b9b 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -2,17 +2,18 @@ class GroupsController < Groups::ApplicationController include IssuesAction include MergeRequestsAction - skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests] respond_to :html - before_action :group, except: [:new, :create] + + skip_before_action :authenticate_user!, only: [:index, :show, :issues, :merge_requests] + before_action :group, except: [:index, :new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:index, :show, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_create_group!, only: [:new, :create] # Load group projects - before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete] + before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete] before_action :event_filter, only: :show layout :determine_layout diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index a2c3d4d2f32..92eac0560bd 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -83,7 +83,11 @@ module LabelsHelper end def text_color_for_bg(bg_color) - r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex) + if bg_color.length == 4 + r, g, b = bg_color[1, 4].scan(/./).map { |v| (v * 2).hex } + else + r, g, b = bg_color[1, 7].scan(/.{2}/).map(&:hex) + end if (r + g + b) > 500 '#333333' diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 49f6c95e045..2ca79df0a29 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -31,7 +31,7 @@ class ExternalIssue # Pattern used to extract `JIRA-123` issue references from text def self.reference_pattern - %r{(?<issue>([A-Z\-]+-)\d+)} + %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)} end def to_reference(_from_project = nil) diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb new file mode 100644 index 00000000000..2015897dd19 --- /dev/null +++ b/app/services/projects/import_service.rb @@ -0,0 +1,67 @@ +module Projects + class ImportService < BaseService + include Gitlab::ShellAdapter + + class Error < StandardError; end + + ALLOWED_TYPES = [ + 'bitbucket', + 'fogbugz', + 'gitlab', + 'github', + 'google_code' + ] + + def execute + if unknown_url? + # In this case, we only want to import issues, not a repository. + create_repository + else + import_repository + end + + import_data + + success + rescue Error => e + error(e.message) + end + + private + + def create_repository + unless project.create_repository + raise Error, 'The repository could not be created.' + end + end + + def import_repository + begin + gitlab_shell.import_repository(project.path_with_namespace, project.import_url) + rescue Gitlab::Shell::Error => e + raise Error, e.message + end + end + + def import_data + return unless has_importer? + + unless importer.execute + raise Error, 'The remote data could not be imported.' + end + end + + def has_importer? + ALLOWED_TYPES.include?(project.import_type) + end + + def importer + class_name = "Gitlab::#{project.import_type.camelize}Import::Importer" + class_name.constantize.new(project) + end + + def unknown_url? + project.import_url == Project::UNKNOWN_IMPORT_URL + end + end +end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index c4020c8273b..baadca09518 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -14,11 +14,11 @@ .form-group.project-visibility-level-holder = f.label :default_project_visibility, class: 'control-label col-sm-2' .col-sm-10 - = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project) + = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new) .form-group.project-visibility-level-holder = f.label :default_snippet_visibility, class: 'control-label col-sm-2' .col-sm-10 - = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: PersonalSnippet) + = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) .form-group = f.label :restricted_visibility_levels, class: 'control-label col-sm-2' .col-sm-10 @@ -268,4 +268,4 @@ = f.text_field :sentry_dsn, class: 'form-control' .form-actions - = f.submit 'Save', class: 'btn btn-primary' + = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index fa4e6335c73..e18f7b499dd 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -22,5 +22,5 @@ %code= Doorkeeper.configuration.native_redirect_uri for local tests .form-actions - = f.submit 'Submit', class: "btn btn-primary wide" + = f.submit 'Submit', class: "btn btn-save wide" = link_to "Cancel", admin_applications_path, class: "btn btn-default" diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 8de2ba74a79..198026a1f75 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -21,6 +21,5 @@ - else .form-actions - = f.submit 'Save changes', class: "btn btn-primary" + = f.submit 'Save changes', class: "btn btn-save" = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel" - diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index 1736dccaf3c..2e3c956ddc4 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -66,7 +66,7 @@ %td .pull-right - - if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts? + - if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts_download_url = link_to commit_status.artifacts_download_url, title: 'Download artifacts' do %i.fa.fa-download - if current_user && can?(current_user, :manage_builds, commit_status.project) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index d18c0706b30..e295a9ddd14 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -4,52 +4,20 @@ class RepositoryImportWorker sidekiq_options queue: :gitlab_shell - def perform(project_id) - project = Project.find(project_id) + attr_accessor :project, :current_user - if project.import_url == Project::UNKNOWN_IMPORT_URL - # In this case, we only want to import issues, not a repository. - unless project.create_repository - project.update(import_error: "The repository could not be created.") - project.import_fail - return - end - else - begin - gitlab_shell.import_repository(project.path_with_namespace, project.import_url) - rescue Gitlab::Shell::Error => e - project.update(import_error: e.message) - project.import_fail - return - end - end + def perform(project_id) + @project = Project.find(project_id) + @current_user = @project.creator - data_import_result = - case project.import_type - when 'github' - Gitlab::GithubImport::Importer.new(project).execute - when 'gitlab' - Gitlab::GitlabImport::Importer.new(project).execute - when 'bitbucket' - Gitlab::BitbucketImport::Importer.new(project).execute - when 'google_code' - Gitlab::GoogleCodeImport::Importer.new(project).execute - when 'fogbugz' - Gitlab::FogbugzImport::Importer.new(project).execute - else - true - end + result = Projects::ImportService.new(project, current_user).execute - unless data_import_result - project.update(import_error: "The remote issue data could not be imported.") + if result[:status] == :error + project.update(import_error: result[:message]) project.import_fail return end - if project.import_type == 'bitbucket' - Gitlab::BitbucketImport::KeyDeleter.new(project).execute - end - project.import_finish end end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 04a7c16ebde..d8170557f7e 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -176,7 +176,7 @@ Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled']. Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil? Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? -Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z]*-\d*))+)' if Settings.gitlab['issue_closing_pattern'].nil? +Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10 diff --git a/config/initializers/monkey_patch.rb b/config/initializers/monkey_patch.rb new file mode 100644 index 00000000000..62b05a55285 --- /dev/null +++ b/config/initializers/monkey_patch.rb @@ -0,0 +1,48 @@ +## This patch is from rails 4.2-stable. Remove it when 4.2.6 is released +## https://github.com/rails/rails/issues/21108 + +module ActiveRecord + module ConnectionAdapters + class AbstractMysqlAdapter < AbstractAdapter + # SHOW VARIABLES LIKE 'name' + def show_variable(name) + variables = select_all("select @@#{name} as 'Value'", 'SCHEMA') + variables.first['Value'] unless variables.empty? + rescue ActiveRecord::StatementInvalid + nil + end + + + # MySQL is too stupid to create a temporary table for use subquery, so we have + # to give it some prompting in the form of a subsubquery. Ugh! + def subquery_for(key, select) + subsubselect = select.clone + subsubselect.projections = [key] + + subselect = Arel::SelectManager.new(select.engine) + subselect.project Arel.sql(key.name) + # Materialized subquery by adding distinct + # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on' + subselect.from subsubselect.distinct.as('__active_record_temp') + end + end + end +end + +module ActiveRecord + module ConnectionAdapters + class MysqlAdapter < AbstractMysqlAdapter + ADAPTER_NAME = 'MySQL'.freeze + + # Get the client encoding for this database + def client_encoding + return @client_encoding if @client_encoding + + result = exec_query( + "select @@character_set_client", + 'SCHEMA') + @client_encoding = ENCODINGS[result.rows.last.last] + end + end + end +end diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md index 58cbe653c34..71db5aa5dc8 100644 --- a/doc/ci/build_artifacts/README.md +++ b/doc/ci/build_artifacts/README.md @@ -13,8 +13,8 @@ ability of downloading the files separately. **Note:** The artifacts browser will be available only for new artifacts that are sent -to GitLab using GitLab Runner version 1.0 and up. You will not be available to -see the browser for old artifacts already uploaded to GitLab. +to GitLab using GitLab Runner version 1.0 and up. It will not be possible to +browse old artifacts already uploaded to GitLab. ## Enabling build artifacts diff --git a/features/project/builds/artifacts.feature b/features/project/builds/artifacts.feature index 1185854453a..52dc15f2eb6 100644 --- a/features/project/builds/artifacts.feature +++ b/features/project/builds/artifacts.feature @@ -51,3 +51,12 @@ Feature: Project Builds Artifacts And I click artifacts browse button And I click a link to file within build artifacts Then download of a file extracted from build artifacts should start + + @javascript + Scenario: I click on a row in an artifacts table + Given recent build has artifacts available + And recent build has artifacts metadata available + When I visit recent build details page + And I click artifacts browse button + And I click a first row within build artifacts table + Then page with a coresponding path is loading diff --git a/features/steps/project/builds/artifacts.rb b/features/steps/project/builds/artifacts.rb index 25f2f4e837c..1bdb57af9d1 100644 --- a/features/steps/project/builds/artifacts.rb +++ b/features/steps/project/builds/artifacts.rb @@ -73,4 +73,14 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps expect(response_json[:archive]).to end_with('build_artifacts.zip') expect(response_json[:entry]).to eq Base64.encode64('ci_artifacts.txt') end + + step 'I click a first row within build artifacts table' do + row = first('tr[data-link]') + @row_path = row['data-link'] + row.click + end + + step 'page with a coresponding path is loading' do + expect(current_path).to eq @row_path + end end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 2355b3c6ddc..3f483847efa 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -13,12 +13,36 @@ module Gitlab end def execute - project_identifier = project.import_source + import_issues if has_issues? - return true unless client.project(project_identifier)["has_issues"] + true + rescue ActiveRecord::RecordInvalid => e + raise Projects::ImportService::Error.new, e.message + ensure + Gitlab::BitbucketImport::KeyDeleter.new(project).execute + end - #Issues && Comments - issues = client.issues(project_identifier) + private + + def gl_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) + (user && user.id) || project.creator_id + else + project.creator_id + end + end + + def identifier + project.import_source + end + + def has_issues? + client.project(identifier)["has_issues"] + end + + def import_issues + issues = client.issues(identifier) issues.each do |issue| body = '' @@ -33,7 +57,7 @@ module Gitlab body = @formatter.author_line(author) body += issue["content"] - comments = client.issue_comments(project_identifier, issue["local_id"]) + comments = client.issue_comments(identifier, issue["local_id"]) if comments.any? body += @formatter.comments_header @@ -56,20 +80,9 @@ module Gitlab author_id: gl_user_id(project, reporter) ) end - - true + rescue ActiveRecord::RecordInvalid => e + raise Projects::ImportService::Error, e.message end - - private - - def gl_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) - (user && user.id) || project.creator_id - else - project.creator_id - end - end end end end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 663402e8197..e2a85f29825 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -35,8 +35,8 @@ module Gitlab end true - rescue ActiveRecord::RecordInvalid - false + rescue ActiveRecord::RecordInvalid => e + raise Projects::ImportService::Error, e.message end def import_pull_requests @@ -53,8 +53,8 @@ module Gitlab end true - rescue ActiveRecord::RecordInvalid - false + rescue ActiveRecord::RecordInvalid => e + raise Projects::ImportService::Error, e.message end def import_comments(issue_number, noteable) @@ -83,10 +83,13 @@ module Gitlab true rescue Gitlab::Shell::Error => e - if e.message =~ /repository not exported/ - true + # GitHub error message when the wiki repo has not been created, + # this means that repo has wiki enabled, but have no pages. So, + # we can skip the import. + if e.message !~ /repository not exported/ + raise Projects::ImportService::Error, e.message else - false + true end end end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb new file mode 100644 index 00000000000..938e97298b6 --- /dev/null +++ b/spec/controllers/groups_controller_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +describe GroupsController do + describe 'GET index' do + context 'as a user' do + it 'redirects to Groups Dashboard' do + sign_in(create(:user)) + + get :index + + expect(response).to redirect_to(dashboard_groups_path) + end + end + + context 'as a guest' do + it 'redirects to Explore Groups' do + get :index + + expect(response).to redirect_to(explore_groups_path) + end + end + end +end diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb index 8898b71e2a3..b7c2b32cb13 100644 --- a/spec/factories/commit_statuses.rb +++ b/spec/factories/commit_statuses.rb @@ -1,11 +1,15 @@ FactoryGirl.define do factory :commit_status, class: CommitStatus do - started_at 'Di 29. Okt 09:51:28 CET 2013' - finished_at 'Di 29. Okt 09:53:28 CET 2013' name 'default' status 'success' description 'commit status' commit factory: :ci_commit_with_one_job + started_at 'Tue, 26 Jan 2016 08:21:42 +0100' + finished_at 'Tue, 26 Jan 2016 08:23:42 +0100' + + after(:build) do |build, evaluator| + build.project = build.commit.project + end factory :generic_commit_status, class: GenericCommitStatus do name 'generic' diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index fe7f07f5b75..5a62da10619 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -16,83 +16,104 @@ describe 'Commits' do FactoryGirl.create :ci_commit, project: project, sha: project.commit.sha end - let!(:build) { FactoryGirl.create :ci_build, commit: commit } + context 'commit status is Generic Commit Status' do + let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit } - describe 'Project commits' do - before do - visit namespace_project_commits_path(project.namespace, project, :master) - end + describe 'Commit builds' do + before do + visit ci_status_path(commit) + end - it 'should show build status' do - page.within("//li[@id='commit-#{commit.short_sha}']") do - expect(page).to have_css(".ci-status-link") + it { expect(page).to have_content commit.sha[0..7] } + + it 'contains generic commit status build' do + page.within('.table-holder') do + expect(page).to have_content "##{status.id}" # build id + expect(page).to have_content 'generic' # build name + end end end end - describe 'Commit builds' do - before do - visit ci_status_path(commit) - end - - it { expect(page).to have_content commit.sha[0..7] } - it { expect(page).to have_content commit.git_commit_message } - it { expect(page).to have_content commit.git_author_name } - end + context 'commit status is Ci Build' do + let!(:build) { FactoryGirl.create :ci_build, commit: commit } - context 'Download artifacts' do - let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + describe 'Project commits' do + before do + visit namespace_project_commits_path(project.namespace, project, :master) + end - before do - build.update_attributes(artifacts_file: artifacts_file) + it 'should show build status' do + page.within("//li[@id='commit-#{commit.short_sha}']") do + expect(page).to have_css(".ci-status-link") + end + end end - it do - visit ci_status_path(commit) - click_on 'Download artifacts' - expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) - end - end + describe 'Commit builds' do + before do + visit ci_status_path(commit) + end - describe 'Cancel all builds' do - it 'cancels commit' do - visit ci_status_path(commit) - click_on 'Cancel running' - expect(page).to have_content 'canceled' + it { expect(page).to have_content commit.sha[0..7] } + it { expect(page).to have_content commit.git_commit_message } + it { expect(page).to have_content commit.git_author_name } end - end - describe 'Cancel build' do - it 'cancels build' do - visit ci_status_path(commit) - click_on 'Cancel' - expect(page).to have_content 'canceled' - end - end + context 'Download artifacts' do + let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + + before do + build.update_attributes(artifacts_file: artifacts_file) + end - describe '.gitlab-ci.yml not found warning' do - context 'ci builds enabled' do - it "does not show warning" do + it do visit ci_status_path(commit) - expect(page).not_to have_content '.gitlab-ci.yml not found in this commit' + click_on 'Download artifacts' + expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) end + end - it 'shows warning' do - stub_ci_commit_yaml_file(nil) + describe 'Cancel all builds' do + it 'cancels commit' do visit ci_status_path(commit) - expect(page).to have_content '.gitlab-ci.yml not found in this commit' + click_on 'Cancel running' + expect(page).to have_content 'canceled' end end - context 'ci builds disabled' do - before do - stub_ci_builds_disabled - stub_ci_commit_yaml_file(nil) + describe 'Cancel build' do + it 'cancels build' do visit ci_status_path(commit) + click_on 'Cancel' + expect(page).to have_content 'canceled' end + end + + describe '.gitlab-ci.yml not found warning' do + context 'ci builds enabled' do + it "does not show warning" do + visit ci_status_path(commit) + expect(page).not_to have_content '.gitlab-ci.yml not found in this commit' + end + + it 'shows warning' do + stub_ci_commit_yaml_file(nil) + visit ci_status_path(commit) + expect(page).to have_content '.gitlab-ci.yml not found in this commit' + end + end + + context 'ci builds disabled' do + before do + stub_ci_builds_disabled + stub_ci_commit_yaml_file(nil) + visit ci_status_path(commit) + end - it 'does not show warning' do - expect(page).not_to have_content '.gitlab-ci.yml not found in this commit' + it 'does not show warning' do + expect(page).not_to have_content '.gitlab-ci.yml not found in this commit' + end end end end diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index 0c8d06b7059..0b9176357bc 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -66,5 +66,10 @@ describe LabelsHelper do it 'uses dark text on light backgrounds' do expect(text_color_for_bg('#EEEEEE')).to eq('#333333') end + + it 'supports RGB triplets' do + expect(text_color_for_bg('#FFF')).to eq '#333333' + expect(text_color_for_bg('#000')).to eq '#FFFFFF' + end end end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 99288da1e43..04cf11fc6f1 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -135,6 +135,17 @@ describe Gitlab::ClosingIssueExtractor, lib: true do message = "resolve #{reference}" expect(subject.closed_by_message(message)).to eq([issue]) end + + context 'with an external issue tracker reference' do + it 'extracts the referenced issue' do + jira_project = create(:jira_project, name: 'JIRA_EXT1') + jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project) + closing_issue_extractor = described_class.new jira_project + message = "Resolve #{jira_issue.to_reference}" + + expect(closing_issue_extractor.closed_by_message(message)).to eq([jira_issue]) + end + end end context "with a cross-project reference" do diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index 6ec6b9037a4..9b144dd1ecc 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -10,6 +10,21 @@ describe ExternalIssue, models: true do it { is_expected.to include_module(Referable) } end + describe '.reference_pattern' do + it 'allows underscores in the project name' do + expect(ExternalIssue.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' + end + + it 'allows numbers in the project name' do + expect(ExternalIssue.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234' + end + + it 'requires the project name to begin with A-Z' do + expect(ExternalIssue.reference_pattern.match('3EXT_EXT-1234')).to eq nil + expect(ExternalIssue.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' + end + end + describe '#to_reference' do it 'returns a String reference to the object' do expect(issue.to_reference).to eq issue.id diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb new file mode 100644 index 00000000000..04f474c736c --- /dev/null +++ b/spec/services/projects/import_service_spec.rb @@ -0,0 +1,106 @@ +require 'spec_helper' + +describe Projects::ImportService, services: true do + let!(:project) { create(:empty_project) } + let(:user) { project.creator } + + subject { described_class.new(project, user) } + + describe '#execute' do + context 'with unknown url' do + before do + project.import_url = Project::UNKNOWN_IMPORT_URL + end + + it 'succeeds if repository is created successfully' do + expect(project).to receive(:create_repository).and_return(true) + + result = subject.execute + + expect(result[:status]).to eq :success + end + + it 'fails if repository creation fails' do + expect(project).to receive(:create_repository).and_return(false) + + result = subject.execute + + expect(result[:status]).to eq :error + expect(result[:message]).to eq 'The repository could not be created.' + end + end + + context 'with known url' do + before do + project.import_url = 'https://github.com/vim/vim.git' + end + + it 'succeeds if repository import is successfully' do + expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true) + + result = subject.execute + + expect(result[:status]).to eq :success + end + + it 'fails if repository import fails' do + expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_raise(Gitlab::Shell::Error.new('Failed to import the repository')) + + result = subject.execute + + expect(result[:status]).to eq :error + expect(result[:message]).to eq 'Failed to import the repository' + end + end + + context 'with valid importer' do + before do + stub_github_omniauth_provider + + project.import_url = 'https://github.com/vim/vim.git' + project.import_type = 'github' + + allow(project).to receive(:import_data).and_return(double.as_null_object) + end + + it 'succeeds if importer succeeds' do + expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true) + expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(true) + + result = subject.execute + + expect(result[:status]).to eq :success + end + + it 'fails if importer fails' do + expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true) + expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(false) + + result = subject.execute + + expect(result[:status]).to eq :error + expect(result[:message]).to eq 'The remote data could not be imported.' + end + + it 'fails if importer raise an error' do + expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true) + expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_raise(Projects::ImportService::Error.new('Github: failed to connect API')) + + result = subject.execute + + expect(result[:status]).to eq :error + expect(result[:message]).to eq 'Github: failed to connect API' + end + end + + def stub_github_omniauth_provider + provider = OpenStruct.new( + name: 'github', + app_id: 'asd123', + app_secret: 'asd123' + ) + + Gitlab.config.omniauth.providers << provider + end + end +end |