diff options
284 files changed, 3005 insertions, 5112 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c614e14e243..a8da3de83f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -117,9 +117,10 @@ flay: - mysql bundler:audit: - script: + script: - "bundle exec bundle-audit update" - "bundle exec bundle-audit check" tags: - ruby - mysql + allow_failure: true diff --git a/.rubocop.yml b/.rubocop.yml index d59edbc8b17..b4ca11c8343 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -735,23 +735,39 @@ Metrics/AbcSize: Description: >- A calculated magnitude based on number of assignments, branches, and conditions. - Enabled: false + Enabled: true + Max: 70 + +Metrics/CyclomaticComplexity: + Description: >- + A complexity metric that is strongly correlated to the number + of test cases needed to validate a method. + Enabled: true + Max: 17 + +Metrics/PerceivedComplexity: + Description: >- + A complexity metric geared towards measuring complexity for a + human reader. + Enabled: true + Max: 17 + +Metrics/ParameterLists: + Description: 'Avoid parameter lists longer than three or four parameters.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' + Enabled: true + Max: 8 Metrics/BlockNesting: Description: 'Avoid excessive block nesting' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' - Enabled: false + Enabled: true + Max: 4 Metrics/ClassLength: Description: 'Avoid classes longer than 100 lines of code.' Enabled: false -Metrics/CyclomaticComplexity: - Description: >- - A complexity metric that is strongly correlated to the number - of test cases needed to validate a method. - Enabled: false - Metrics/LineLength: Description: 'Limit lines to 80 characters.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' @@ -762,17 +778,6 @@ Metrics/MethodLength: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' Enabled: false -Metrics/ParameterLists: - Description: 'Avoid parameter lists longer than three or four parameters.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' - Enabled: false - -Metrics/PerceivedComplexity: - Description: >- - A complexity metric geared towards measuring complexity for a - human reader. - Enabled: false - #################### Lint ################################ ### Warnings diff --git a/CHANGELOG b/CHANGELOG index 585e6c42043..365b9e973fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ v 8.3.0 (unreleased) - Update project repositorize size and commit count during import:repos task (Stan Hu) - Fix API setting of 'public' attribute to false will make a project private (Stan Hu) - Handle and report SSL errors in Web hook test (Stan Hu) + - Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) - Fix 500 error when update group member permission @@ -20,6 +21,7 @@ v 8.3.0 (unreleased) - Fire update hook from GitLab - Style warning about mentioning many people in a comment - Fix: sort milestones by due date once again (Greg Smethells) + - Migrate all CI::Services and CI::WebHooks to Services and WebHooks - Don't show project fork event as "imported" - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info @@ -43,6 +45,12 @@ v 8.3.0 (unreleased) - Prevent possible XSS attack with award-emoji - Upgraded Sidekiq to 4.x - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) + - Fix emoji aliases problem + - Fix award-emojis Flash alert's width + - Fix deleting notes on a merge request diff + - Display referenced merge request statuses in the issue description (Greg Smethells) + - Implement new sidebar for issue and merge request pages + - Emoji picker improvements v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) @@ -93,6 +93,7 @@ gem 'html-pipeline', '~> 1.11.0' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'github-markup', '~> 1.3.1' gem 'redcarpet', '~> 3.3.3' +gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 0db1c6760f8..ff57460f5bb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (2.3.2) + RedCloth (4.2.9) ace-rails-ap (2.0.1) actionmailer (4.2.4) actionpack (= 4.2.4) @@ -826,6 +827,7 @@ PLATFORMS ruby DEPENDENCIES + RedCloth (~> 4.2.9) ace-rails-ap (~> 2.0.1) activerecord-deprecated_finders (~> 1.0.3) activerecord-session_store (~> 0.1.0) diff --git a/README.md b/README.md index c459e67baa1..3ec1d4a776c 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ GitLab is a Ruby on Rails application that runs on the following software: - Ubuntu/Debian/CentOS/RHEL - Ruby (MRI) 2.1 - Git 1.7.10+ -- Redis 2.4+ +- Redis 2.8+ - MySQL or PostgreSQL For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html). diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 96fd8f8773e..3ff9ba77dfc 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -1,11 +1,13 @@ class @AwardsHandler - constructor: (@post_emoji_url, @noteable_type, @noteable_id) -> + constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) -> addAward: (emoji) -> + emoji = @normilizeEmojiName(emoji) @postEmoji emoji, => @addAwardToEmojiBar(emoji) addAwardToEmojiBar: (emoji, custom_path = '') -> + emoji = @normilizeEmojiName(emoji) if @exist(emoji) if @isActive(emoji) @decrementCounter(emoji) @@ -94,3 +96,6 @@ class @AwardsHandler $('body, html').animate({ scrollTop: $('.awards').offset().top - 80 }, 200) + + normilizeEmojiName: (emoji) -> + @aliases[emoji] || emoji diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee index 9b59d4e57f7..5de012e409f 100644 --- a/app/assets/javascripts/flash.js.coffee +++ b/app/assets/javascripts/flash.js.coffee @@ -12,5 +12,5 @@ class @Flash @flash.click -> $(@).fadeOut() @flash.show() - pin: -> - @flash.addClass('flash-pinned flash-raised') + pinTo: (selector) -> + @flash.detach().appendTo(selector) diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee index c4d3e619f5e..01bd515cc02 100644 --- a/app/assets/javascripts/issuable_context.js.coffee +++ b/app/assets/javascripts/issuable_context.js.coffee @@ -5,9 +5,9 @@ class @IssuableContext new UsersSelect() $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}) - $(".context .inline-update").on "change", "select", -> + $(".issuable-sidebar .inline-update").on "change", "select", -> $(this).submit() - $(".context .inline-update").on "change", ".js-assignee", -> + $(".issuable-sidebar .inline-update").on "change", ".js-assignee", -> $(this).submit() $('.issuable-details').waitForImages -> @@ -18,6 +18,12 @@ class @IssuableContext $('.issuable-affix').affix offset: top: -> - @top = ($('.issuable-affix').offset().top - 70) + @top = ($('.issuable-affix').offset().top - 60) bottom: -> @bottom = $('.footer').outerHeight(true) + + $(".edit-link").click (e) -> + block = $(@).parents('.block') + block.find('.selectbox').show() + block.find('.value').hide() + block.find('.js-select2').select2("open") diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b1df56b24fe..35dc7829da2 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -114,7 +114,7 @@ class @Notes unless note.valid if note.award flash = new Flash('You have already used this award emoji!', 'alert') - flash.pin() + flash.pinTo('.header-content') return # render note if it not present in loaded list @@ -350,18 +350,26 @@ class @Notes ### removeNote: -> note = $(this).closest(".note") - notes = note.closest(".notes") + note_id = note.attr('id') - # check if this is the last note for this line - if notes.find(".note").length is 1 + $('.note[id="' + note_id + '"]').each -> + note = $(this) + notes = note.closest(".notes") + count = notes.closest(".notes_holder").find(".discussion-notes-count") - # for discussions - notes.closest(".discussion").remove() + # check if this is the last note for this line + if notes.find(".note").length is 1 - # for diff lines - notes.closest("tr").remove() + # for discussions + notes.closest(".discussion").remove() - note.remove() + # for diff lines + notes.closest("tr").remove() + else + # update notes count + count.get(0).lastChild.nodeValue = " #{notes.children().length - 1}" + + note.remove() ### Called in response to clicking the delete attachment link diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index ec919f0cd67..1f221945c06 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -4,8 +4,11 @@ class @Project $('.js-protocol-switch').click -> return if $(@).hasClass('active') - # Toggle 'active' for both buttons - $('.js-protocol-switch').toggleClass('active') + + # Remove the active class for all buttons (ssh, http, kerberos if shown) + $('.active').not($(@)).removeClass('active'); + # Add the active class for the clicked button + $(@).toggleClass('active') url = $(@).data('clone') diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 2e8515668f6..88da799ee2b 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -461,3 +461,9 @@ table { visibility: hidden; } } + +.content-separator { + margin-left: -$gl-padding; + margin-right: -$gl-padding; + border-top: 1px solid $border-color; +} diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss index 1b723021d76..82eb50ad4be 100644 --- a/app/assets/stylesheets/framework/flash.scss +++ b/app/assets/stylesheets/framework/flash.scss @@ -15,13 +15,3 @@ @extend .alert-danger; } } - -.flash-pinned { - position: fixed; - top: 80px; - width: 80%; -} - -.flash-raised { - z-index: 1000; -} diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index c01e1e32e41..af145191bc8 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -181,4 +181,4 @@ .ajax-users-dropdown { min-width: 250px !important; -}
\ No newline at end of file +} diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss new file mode 100644 index 00000000000..041b811a606 --- /dev/null +++ b/app/assets/stylesheets/pages/awards.scss @@ -0,0 +1,86 @@ +.awards { + @include clearfix; + line-height: 34px; + + .award { + @include border-radius(5px); + + border: 1px solid; + padding: 0px 10px; + float: left; + margin-right: 5px; + border-color: $border-color; + cursor: pointer; + + &:hover { + background-color: #dce0e5; + } + + &.active { + border-color: $border-gray-light; + background-color: $gray-light; + + &:hover { + background-color: #dce0e5; + } + + .counter { + font-weight: bold; + } + } + + .icon { + float: left; + margin-right: 10px; + } + + .counter { + float: left; + } + } + + .awards-controls { + margin-left: 10px; + float: left; + + .add-award { + font-size: 24px; + color: $gl-gray; + position: relative; + top: 2px; + + &:hover, + &:link { + text-decoration: none; + } + } + + .awards-menu { + padding: $gl-padding; + min-width: 214px; + + > li { + cursor: pointer; + width: 30px; + height: 30px; + text-align: center; + @include border-radius(5px); + + img { + margin-bottom: 2px; + } + + &:hover { + background-color: #ccc; + } + } + } + } + + .awards-menu{ + li { + float: left; + margin: 3px; + } + } +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 957da5c182e..797a0af3720 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -18,26 +18,12 @@ &.affix { position: fixed; - top: 70px; + top: 60px; margin-right: 35px; } } } -.issuable-context-title { - margin-bottom: 5px; - - .avatar { - margin-left: 0; - } - - label { - color: $gl-gray; - font-weight: normal; - margin-right: 4px; - } -} - .project-issuable-filter { .controls { float: right; @@ -50,23 +36,6 @@ } .issuable-details { - .page-title { - margin-top: -$gl-padding; - padding: 7px 0; - margin-bottom: 0; - color: #5c5d5e; - font-size: 16px; - line-height: 42px; - - .author { - color: #5c5d5e; - } - - .issue-id { - color: #5c5d5e; - } - } - .issue-title { margin: 0; font-size: 23px; @@ -80,6 +49,21 @@ margin-bottom: 0; } } + + section { + border-right: 1px solid #ECEEF1; + + > .tab-content { + margin-right: 1px; + } + + .issue-discussion > .gray-content-block, + > .gray-content-block { + margin-top: 0; + border-top: none; + margin-right: -15px; + } + } } .issuable-filter-count { @@ -101,84 +85,72 @@ } } -.cross-project-reference { - text-align: center; - width: 100%; - - .slead { - padding: 5px; - } +.issuable-sidebar { + .block { + @include clearfix; + padding: $gl-padding 0; + border-bottom: 1px solid #F0F0F0; - span, button { - background-color: $background-color; + &:last-child { + border: none; + } } -} -.awards { - @include clearfix; - line-height: 34px; - margin: 2px 0; + .title { + color: $gl-text-color; + margin-bottom: 8px; - .award { - @include border-radius(5px); - - border: 1px solid; - padding: 0px 10px; - float: left; - margin: 0 5px; - border-color: $border-color; - cursor: pointer; - - &.active { - border-color: $border-gray-light; - background-color: $gray-light; + .avatar { + margin-left: 0; + } - .counter { - font-weight: bold; - } + label { + font-weight: normal; + margin-right: 4px; } - .icon { - float: left; - margin-right: 10px; + .edit-link { + color: $gl-gray; } + } + + .cross-project-reference { + font-weight: bold; + color: $gl-link-color; - .counter { - float: left; + button { + float: right; } } - .awards-controls { - margin-left: 10px; - float: left; + .selectbox { + display: none + } - .add-award { - font-size: 24px; - color: $gl-gray; - position: relative; - top: 2px; + .btn-clipboard { + color: $gl-gray; + } - &:hover, - &:link { - text-decoration: none; - } - } + .participants .avatar { + margin-top: 6px; + margin-right: 2px; + } +} - .awards-menu { - padding: $gl-padding; - min-width: 214px; +.issuable-title { + margin: -$gl-padding; + padding: 7px $gl-padding; + margin-bottom: 0px; + border-bottom: 1px solid $border-color; + color: #5c5d5e; + font-size: 16px; + line-height: 42px; - > li { - cursor: pointer; - margin: 5px; - } - } + .author { + color: #5c5d5e; } - .awards-menu{ - li { - float: left; - margin: 3px; - } + .issuable-id { + color: #5c5d5e; } } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index f5548c5b88b..a652b65502f 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -60,6 +60,26 @@ form.edit-issue { margin: 0; } +.merge-requests-title { + font-size: 16px; + font-weight: 600; +} + +.merge-request-id { + display: inline-block; + width: 3em; +} + +.merge-request-info { + padding-left: 5px; +} + +.merge-request-status { + color: $gl-gray; + font-size: 15px; + font-weight: bold; +} + .merge-request, .issue { &.today { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index fc8c7161991..502e9552acd 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -121,10 +121,6 @@ } } -.merge-request-details { - margin-bottom: $gl-padding; -} - .mr_source_commit, .mr_target_commit { .commit { diff --git a/app/controllers/admin/builds_controller.rb b/app/controllers/admin/builds_controller.rb new file mode 100644 index 00000000000..83d9684c706 --- /dev/null +++ b/app/controllers/admin/builds_controller.rb @@ -0,0 +1,23 @@ +class Admin::BuildsController < Admin::ApplicationController + def index + @scope = params[:scope] + @all_builds = Ci::Build + @builds = @all_builds.order('created_at DESC') + @builds = + case @scope + when 'all' + @builds + when 'finished' + @builds.finished + else + @builds.running_or_pending.reverse_order + end + @builds = @builds.page(params[:page]).per(30) + end + + def cancel_all + Ci::Build.running_or_pending.each(&:cancel) + + redirect_to admin_builds_path + end +end diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb new file mode 100644 index 00000000000..d25619d94e0 --- /dev/null +++ b/app/controllers/admin/runner_projects_controller.rb @@ -0,0 +1,35 @@ +class Admin::RunnerProjectsController < Admin::ApplicationController + before_action :project, only: [:create] + + def index + @runner_projects = project.runner_projects.all + @runner_project = project.runner_projects.new + end + + def create + @runner = Ci::Runner.find(params[:runner_project][:runner_id]) + + if @runner.assign_to(@project, current_user) + redirect_to admin_runner_path(@runner) + else + redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project' + end + end + + def destroy + rp = Ci::RunnerProject.find(params[:id]) + runner = rp.runner + rp.destroy + + redirect_to admin_runner_path(runner) + end + + private + + def project + @project = Project.find_with_namespace( + [params[:namespace_id], '/', params[:project_id]].join('') + ) + @project || render_404 + end +end diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb new file mode 100644 index 00000000000..a701d49b844 --- /dev/null +++ b/app/controllers/admin/runners_controller.rb @@ -0,0 +1,63 @@ +class Admin::RunnersController < Admin::ApplicationController + before_action :runner, except: :index + + def index + @runners = Ci::Runner.order('id DESC') + @runners = @runners.search(params[:search]) if params[:search].present? + @runners = @runners.page(params[:page]).per(30) + @active_runners_cnt = Ci::Runner.online.count + end + + def show + @builds = @runner.builds.order('id DESC').first(30) + @projects = + if params[:search].present? + ::Project.search(params[:search]) + else + Project.all + end + @projects = @projects.where.not(id: @runner.projects.select(:id)) if @runner.projects.any? + @projects = @projects.page(params[:page]).per(30) + end + + def update + @runner.update_attributes(runner_params) + + respond_to do |format| + format.js + format.html { redirect_to admin_runner_path(@runner) } + end + end + + def destroy + @runner.destroy + + redirect_to admin_runners_path + end + + def resume + if @runner.update_attributes(active: true) + redirect_to admin_runners_path, notice: 'Runner was successfully updated.' + else + redirect_to admin_runners_path, alert: 'Runner was not updated.' + end + end + + def pause + if @runner.update_attributes(active: false) + redirect_to admin_runners_path, notice: 'Runner was successfully updated.' + else + redirect_to admin_runners_path, alert: 'Runner was not updated.' + end + end + + private + + def runner + @runner ||= Ci::Runner.find(params[:id]) + end + + def runner_params + params.require(:runner).permit(:token, :description, :tag_list, :active) + end +end diff --git a/app/controllers/ci/admin/application_controller.rb b/app/controllers/ci/admin/application_controller.rb deleted file mode 100644 index 4ec2dc9c2cf..00000000000 --- a/app/controllers/ci/admin/application_controller.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Ci - module Admin - class ApplicationController < Ci::ApplicationController - before_action :authenticate_user! - before_action :authenticate_admin! - - layout "ci/admin" - end - end -end diff --git a/app/controllers/ci/admin/application_settings_controller.rb b/app/controllers/ci/admin/application_settings_controller.rb deleted file mode 100644 index 71e253fac67..00000000000 --- a/app/controllers/ci/admin/application_settings_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Ci - class Admin::ApplicationSettingsController < Ci::Admin::ApplicationController - before_action :set_application_setting - - def show - end - - def update - if @application_setting.update_attributes(application_setting_params) - redirect_to ci_admin_application_settings_path, - notice: 'Application settings saved successfully' - else - render :show - end - end - - private - - def set_application_setting - @application_setting = Ci::ApplicationSetting.current - @application_setting ||= Ci::ApplicationSetting.create_from_defaults - end - - def application_setting_params - params.require(:application_setting).permit( - :all_broken_builds, - :add_pusher, - ) - end - end -end diff --git a/app/controllers/ci/admin/builds_controller.rb b/app/controllers/ci/admin/builds_controller.rb deleted file mode 100644 index 38abfdeafbf..00000000000 --- a/app/controllers/ci/admin/builds_controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Ci - class Admin::BuildsController < Ci::Admin::ApplicationController - def index - @scope = params[:scope] - @builds = Ci::Build.order('created_at DESC').page(params[:page]).per(30) - - @builds = - case @scope - when "pending" - @builds.pending - when "running" - @builds.running - else - @builds - end - end - end -end diff --git a/app/controllers/ci/admin/events_controller.rb b/app/controllers/ci/admin/events_controller.rb deleted file mode 100644 index 5939efff980..00000000000 --- a/app/controllers/ci/admin/events_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Ci - class Admin::EventsController < Ci::Admin::ApplicationController - EVENTS_PER_PAGE = 50 - - def index - @events = Ci::Event.admin.order('created_at DESC').page(params[:page]).per(EVENTS_PER_PAGE) - end - end -end diff --git a/app/controllers/ci/admin/projects_controller.rb b/app/controllers/ci/admin/projects_controller.rb deleted file mode 100644 index 5bbd0ce7396..00000000000 --- a/app/controllers/ci/admin/projects_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Ci - class Admin::ProjectsController < Ci::Admin::ApplicationController - def index - @projects = Ci::Project.ordered_by_last_commit_date.page(params[:page]).per(30) - end - - def destroy - project.destroy - - redirect_to ci_projects_url - end - - protected - - def project - @project ||= Ci::Project.find(params[:id]) - end - end -end diff --git a/app/controllers/ci/admin/runner_projects_controller.rb b/app/controllers/ci/admin/runner_projects_controller.rb deleted file mode 100644 index e7de6eb12ca..00000000000 --- a/app/controllers/ci/admin/runner_projects_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Ci - class Admin::RunnerProjectsController < Ci::Admin::ApplicationController - layout 'ci/project' - - def index - @runner_projects = project.runner_projects.all - @runner_project = project.runner_projects.new - end - - def create - @runner = Ci::Runner.find(params[:runner_project][:runner_id]) - - if @runner.assign_to(project, current_user) - redirect_to ci_admin_runner_path(@runner) - else - redirect_to ci_admin_runner_path(@runner), alert: 'Failed adding runner to project' - end - end - - def destroy - rp = Ci::RunnerProject.find(params[:id]) - runner = rp.runner - rp.destroy - - redirect_to ci_admin_runner_path(runner) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - end -end diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb deleted file mode 100644 index 0cafad27418..00000000000 --- a/app/controllers/ci/admin/runners_controller.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Ci - class Admin::RunnersController < Ci::Admin::ApplicationController - before_action :runner, except: :index - - def index - @runners = Ci::Runner.order('id DESC') - @runners = @runners.search(params[:search]) if params[:search].present? - @runners = @runners.page(params[:page]).per(30) - @active_runners_cnt = Ci::Runner.online.count - end - - def show - @builds = @runner.builds.order('id DESC').first(30) - @projects = Ci::Project.all - if params[:search].present? - @gl_projects = ::Project.search(params[:search]) - @projects = @projects.where(gitlab_id: @gl_projects.select(:id)) - end - @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? - @projects = @projects.joins(:gl_project) - @projects = @projects.page(params[:page]).per(30) - end - - def update - @runner.update_attributes(runner_params) - - respond_to do |format| - format.js - format.html { redirect_to ci_admin_runner_path(@runner) } - end - end - - def destroy - @runner.destroy - - redirect_to ci_admin_runners_path - end - - def resume - if @runner.update_attributes(active: true) - redirect_to ci_admin_runners_path, notice: 'Runner was successfully updated.' - else - redirect_to ci_admin_runners_path, alert: 'Runner was not updated.' - end - end - - def pause - if @runner.update_attributes(active: false) - redirect_to ci_admin_runners_path, notice: 'Runner was successfully updated.' - else - redirect_to ci_admin_runners_path, alert: 'Runner was not updated.' - end - end - - def assign_all - Ci::Project.unassigned(@runner).all.each do |project| - @runner.assign_to(project, current_user) - end - - redirect_to ci_admin_runner_path(@runner), notice: "Runner was assigned to all projects" - end - - private - - def runner - @runner ||= Ci::Runner.find(params[:id]) - end - - def runner_params - params.require(:runner).permit(:token, :description, :tag_list, :active) - end - end -end diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 848f2b4e314..c420b59c3a2 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -4,24 +4,16 @@ module Ci "app/helpers/ci" end - helper_method :gl_project - private - def authenticate_token! - unless project.valid_token?(params[:token]) - return head(403) - end - end - def authorize_access_project! - unless can?(current_user, :read_project, gl_project) + unless can?(current_user, :read_project, project) return page_404 end end def authorize_manage_builds! - unless can?(current_user, :manage_builds, gl_project) + unless can?(current_user, :manage_builds, project) return page_404 end end @@ -31,7 +23,7 @@ module Ci end def authorize_manage_project! - unless can?(current_user, :admin_project, gl_project) + unless can?(current_user, :admin_project, project) return page_404 end end @@ -58,9 +50,5 @@ module Ci count: count } end - - def gl_project - ::Project.find(@project.gitlab_id) - end end end diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb index a4f6aff49b4..7ed78ff8e98 100644 --- a/app/controllers/ci/lints_controller.rb +++ b/app/controllers/ci/lints_controller.rb @@ -1,5 +1,5 @@ module Ci - class LintsController < Ci::ApplicationController + class LintsController < ApplicationController before_action :authenticate_user! def show diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 8406399fb60..3004c2d27f0 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -3,13 +3,12 @@ module Ci before_action :project, except: [:index] before_action :authenticate_user!, except: [:index, :build, :badge] before_action :authorize_access_project!, except: [:index, :badge] - before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml] before_action :no_cache, only: [:badge] protect_from_forgery def show # Temporary compatibility with CI badges pointing to CI project page - redirect_to namespace_project_path(project.gl_project.namespace, project.gl_project) + redirect_to namespace_project_path(project.namespace, project) end # Project status badge @@ -20,16 +19,10 @@ module Ci send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" end - def toggle_shared_runners - project.toggle!(:shared_runners_enabled) - - redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project) - end - protected def project - @project ||= Ci::Project.find(params[:id]) + @project ||= Project.find_by(ci_id: params[:id].to_i) end def no_cache diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb deleted file mode 100644 index 9d555313369..00000000000 --- a/app/controllers/ci/runner_projects_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Ci - class RunnerProjectsController < Ci::ApplicationController - before_action :authenticate_user! - before_action :project - before_action :authorize_manage_project! - - def create - @runner = Ci::Runner.find(params[:runner_project][:runner_id]) - - return head(403) unless current_user.ci_authorized_runners.include?(@runner) - - path = runners_path(@project.gl_project) - - if @runner.assign_to(project, current_user) - redirect_to path - else - redirect_to path, alert: 'Failed adding runner to project' - end - end - - def destroy - runner_project = project.runner_projects.find(params[:id]) - runner_project.destroy - - redirect_to runners_path(@project.gl_project) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - end -end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 7d0d57858e0..dd32d509191 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -31,8 +31,4 @@ class Projects::ApplicationController < ApplicationController def builds_enabled return render_404 unless @project.builds_enabled? end - - def ci_project - @ci_project ||= @project.ensure_gitlab_ci_project - end end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 4638f77b887..26ba12520c7 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -1,5 +1,4 @@ class Projects::BuildsController < Projects::ApplicationController - before_action :ci_project before_action :build, except: [:index, :cancel_all] before_action :authorize_manage_builds!, except: [:index, :show, :status] @@ -9,7 +8,7 @@ class Projects::BuildsController < Projects::ApplicationController def index @scope = params[:scope] - @all_builds = project.ci_builds + @all_builds = project.builds @builds = @all_builds.order('created_at DESC') @builds = case @scope @@ -24,13 +23,13 @@ class Projects::BuildsController < Projects::ApplicationController end def cancel_all - @project.ci_builds.running_or_pending.each(&:cancel) + @project.builds.running_or_pending.each(&:cancel) redirect_to namespace_project_builds_path(project.namespace, project) end def show - @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC') + @builds = @project.ci_commits.find_by_sha(@build.sha).builds.order('id DESC') @builds = @builds.where("id not in (?)", @build.id) @commit = @build.commit @@ -77,7 +76,7 @@ class Projects::BuildsController < Projects::ApplicationController private def build - @build ||= ci_project.builds.unscoped.find_by!(id: params[:id]) + @build ||= project.builds.unscoped.find_by!(id: params[:id]) end def artifacts_file @@ -85,7 +84,7 @@ class Projects::BuildsController < Projects::ApplicationController end def build_path(build) - namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) + namespace_project_build_path(build.project.namespace, build.project, build) end def authorize_manage_builds! diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb deleted file mode 100644 index 550a019e8e2..00000000000 --- a/app/controllers/projects/ci_services_controller.rb +++ /dev/null @@ -1,49 +0,0 @@ -class Projects::CiServicesController < Projects::ApplicationController - before_action :ci_project - before_action :authorize_admin_project! - - layout "project_settings" - - def index - @ci_project.build_missing_services - @services = @ci_project.services.reload - end - - def edit - service - end - - def update - if service.update_attributes(service_params) - redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) - else - render 'edit' - end - end - - def test - last_build = @project.ci_builds.last - - if service.execute(last_build) - message = { notice: 'We successfully tested the service' } - else - message = { alert: 'We tried to test the service but error occurred' } - end - - redirect_back_or_default(options: message) - end - - private - - def service - @service ||= @ci_project.services.find { |service| service.to_param == params[:id] } - end - - def service_params - params.require(:service).permit( - :type, :active, :webhook, :notify_only_broken_builds, - :email_recipients, :email_only_broken_builds, :email_add_pusher, - :hipchat_token, :hipchat_room, :hipchat_server - ) - end -end diff --git a/app/controllers/projects/ci_settings_controller.rb b/app/controllers/projects/ci_settings_controller.rb deleted file mode 100644 index a263242a850..00000000000 --- a/app/controllers/projects/ci_settings_controller.rb +++ /dev/null @@ -1,36 +0,0 @@ -class Projects::CiSettingsController < Projects::ApplicationController - before_action :ci_project - before_action :authorize_admin_project! - - layout "project_settings" - - def edit - end - - def update - if ci_project.update_attributes(project_params) - Ci::EventService.new.change_project_settings(current_user, ci_project) - - redirect_to edit_namespace_project_ci_settings_path(project.namespace, project), notice: 'Project was successfully updated.' - else - render action: "edit" - end - end - - def destroy - ci_project.destroy - Ci::EventService.new.remove_project(current_user, ci_project) - project.gitlab_ci_service.update_attributes(active: false) - - redirect_to project_path(project), notice: "CI was disabled for this project" - end - - protected - - def project_params - params.require(:project).permit(:path, :timeout, :timeout_in_minutes, :default_ref, :always_build, - :polling_interval, :public, :ssh_url_to_repo, :allow_git_fetch, :email_recipients, - :email_add_pusher, :email_only_broken_builds, :coverage_regex, :shared_runners_enabled, :token, - { variables_attributes: [:id, :key, :value, :_destroy] }) - end -end diff --git a/app/controllers/projects/ci_web_hooks_controller.rb b/app/controllers/projects/ci_web_hooks_controller.rb deleted file mode 100644 index a2d470d4a69..00000000000 --- a/app/controllers/projects/ci_web_hooks_controller.rb +++ /dev/null @@ -1,45 +0,0 @@ -class Projects::CiWebHooksController < Projects::ApplicationController - before_action :ci_project - before_action :authorize_admin_project! - - layout "project_settings" - - def index - @web_hooks = @ci_project.web_hooks - @web_hook = Ci::WebHook.new - end - - def create - @web_hook = @ci_project.web_hooks.new(web_hook_params) - @web_hook.save - - if @web_hook.valid? - redirect_to namespace_project_ci_web_hooks_path(@project.namespace, @project) - else - @web_hooks = @ci_project.web_hooks.select(&:persisted?) - render :index - end - end - - def test - Ci::TestHookService.new.execute(hook, current_user) - - redirect_back_or_default(default: { action: 'index' }) - end - - def destroy - hook.destroy - - redirect_to namespace_project_ci_web_hooks_path(@project.namespace, @project) - end - - private - - def hook - @web_hook ||= @ci_project.web_hooks.find(params[:id]) - end - - def web_hook_params - params.require(:web_hook).permit(:url) - end -end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index e8af205b788..0aaba3792bf 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -31,7 +31,6 @@ class Projects::CommitController < Projects::ApplicationController end def builds - @ci_project = @project.gitlab_ci_project end def cancel_builds diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index a8f47069bb4..d13ea9f34b6 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -25,13 +25,11 @@ class Projects::GraphsController < Projects::ApplicationController end def ci - ci_project = @project.gitlab_ci_project - @charts = {} - @charts[:week] = Ci::Charts::WeekChart.new(ci_project) - @charts[:month] = Ci::Charts::MonthChart.new(ci_project) - @charts[:year] = Ci::Charts::YearChart.new(ci_project) - @charts[:build_times] = Ci::Charts::BuildTime.new(ci_project) + @charts[:week] = Ci::Charts::WeekChart.new(project) + @charts[:month] = Ci::Charts::MonthChart.new(project) + @charts[:year] = Ci::Charts::YearChart.new(project) + @charts[:build_times] = Ci::Charts::BuildTime.new(project) end def languages diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 6a62880cb71..5fd4f855dec 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -53,6 +53,7 @@ class Projects::HooksController < Projects::ApplicationController def hook_params params.require(:hook).permit(:url, :push_events, :issues_events, - :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification) + :merge_requests_events, :tag_push_events, :note_events, + :build_events, :enable_ssl_verification) end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index ae474cf8d68..b59b52291fb 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -58,10 +58,10 @@ class Projects::IssuesController < Projects::ApplicationController end def show - @participants = @issue.participants(current_user) @note = @project.notes.new(noteable: @issue) @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue + @merge_requests = @issue.referenced_merge_requests respond_with(@issue) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 530f3d3dcb8..fffd90d87eb 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -81,8 +81,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def builds - @ci_project = @merge_request.source_project.gitlab_ci_project - respond_to do |format| format.html { render 'show' } format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } } @@ -106,7 +104,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController @first_commit = @merge_request.first_commit @diffs = @merge_request.compare_diffs - @ci_project = @source_project.gitlab_ci_project @ci_commit = @merge_request.ci_commit @statuses = @ci_commit.statuses if @ci_commit @@ -279,8 +276,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def define_show_vars - @participants = @merge_request.participants(current_user) - # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb new file mode 100644 index 00000000000..e2785caa2fb --- /dev/null +++ b/app/controllers/projects/runner_projects_controller.rb @@ -0,0 +1,26 @@ +class Projects::RunnerProjectsController < Projects::ApplicationController + before_action :authorize_admin_project! + + layout 'project_settings' + + def create + @runner = Ci::Runner.find(params[:runner_project][:runner_id]) + + return head(403) unless current_user.ci_authorized_runners.include?(@runner) + + path = runners_path(project) + + if @runner.assign_to(project, current_user) + redirect_to path + else + redirect_to path, alert: 'Failed adding runner to project' + end + end + + def destroy + runner_project = project.runner_projects.find(params[:id]) + runner_project.destroy + + redirect_to runners_path(project) + end +end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index bfbcf2567f3..4993b2648a5 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -1,14 +1,13 @@ class Projects::RunnersController < Projects::ApplicationController - before_action :ci_project before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] before_action :authorize_admin_project! layout 'project_settings' def index - @runners = @ci_project.runners.ordered + @runners = project.runners.ordered @specific_runners = current_user.ci_authorized_runners. - where.not(id: @ci_project.runners). + where.not(id: project.runners). ordered.page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) @@ -26,7 +25,7 @@ class Projects::RunnersController < Projects::ApplicationController end def destroy - if @runner.only_for?(@ci_project) + if @runner.only_for?(project) @runner.destroy end @@ -52,10 +51,16 @@ class Projects::RunnersController < Projects::ApplicationController def show end + def toggle_shared_runners + project.toggle!(:shared_runners_enabled) + + redirect_to namespace_project_runners_path(project.namespace, project) + end + protected def set_runner - @runner ||= @ci_project.runners.find(params[:id]) + @runner ||= project.runners.find(params[:id]) end def runner_params diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 42dbb497e01..6e7590260ff 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -6,7 +6,9 @@ class Projects::ServicesController < Projects::ApplicationController :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, :colorize_messages, :channels, :push_events, :issues_events, :merge_requests_events, :tag_push_events, - :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url, + :note_events, :build_events, + :notify_only_broken_builds, :add_pusher, + :send_from_committer_email, :disable_diffs, :external_wiki_url, :notify, :color, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification] diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index 782ebd01b05..30adfad1daa 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -1,22 +1,21 @@ class Projects::TriggersController < Projects::ApplicationController - before_action :ci_project before_action :authorize_admin_project! layout 'project_settings' def index - @triggers = @ci_project.triggers + @triggers = project.triggers @trigger = Ci::Trigger.new end def create - @trigger = @ci_project.triggers.new + @trigger = project.triggers.new @trigger.save if @trigger.valid? redirect_to namespace_project_triggers_path(@project.namespace, @project) else - @triggers = @ci_project.triggers.select(&:persisted?) + @triggers = project.triggers.select(&:persisted?) render :index end end @@ -30,6 +29,6 @@ class Projects::TriggersController < Projects::ApplicationController private def trigger - @trigger ||= @ci_project.triggers.find(params[:id]) + @trigger ||= project.triggers.find(params[:id]) end end diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index d6561a45a70..10efafea9db 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -1,5 +1,4 @@ class Projects::VariablesController < Projects::ApplicationController - before_action :ci_project before_action :authorize_admin_project! layout 'project_settings' @@ -8,9 +7,7 @@ class Projects::VariablesController < Projects::ApplicationController end def update - if ci_project.update_attributes(project_params) - Ci::EventService.new.change_project_settings(current_user, ci_project) - + if project.update_attributes(project_params) redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variables were successfully updated.' else render action: 'show' diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 10c75370d7b..bf5e25ff895 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -210,10 +210,10 @@ class ProjectsController < ApplicationController def project_params params.require(:project).permit( - :name, :path, :description, :issues_tracker, :tag_list, + :name, :path, :description, :issues_tracker, :tag_list, :runners_token, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, - :builds_enabled + :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, ) end diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb deleted file mode 100644 index e34c8be1dfc..00000000000 --- a/app/helpers/ci/gitlab_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Ci - module GitlabHelper - def no_turbolink - { :"data-no-turbolink" => "data-no-turbolink" } - end - - def yaml_web_editor_link(project) - commits = project.commits - - if commits.any? && commits.last.ci_yaml_file - "#{project.gitlab_url}/edit/master/.gitlab-ci.yml" - else - "#{project.gitlab_url}/new/master" - end - end - end -end diff --git a/app/helpers/ci/projects_helper.rb b/app/helpers/ci/projects_helper.rb deleted file mode 100644 index fd991a4165a..00000000000 --- a/app/helpers/ci/projects_helper.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Ci - module ProjectsHelper - def ref_tab_class ref = nil - 'active' if ref == @ref - end - - def success_ratio(success_builds, failed_builds) - failed_builds = failed_builds.count(:all) - success_builds = success_builds.count(:all) - - return 100 if failed_builds.zero? - - ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100 - ratio.to_i - end - - def markdown_badge_code(project, ref) - url = status_ci_project_url(project, ref: ref, format: 'png') - "[![build status](#{url})](#{ci_project_url(project, ref: ref)})" - end - - def html_badge_code(project, ref) - url = status_ci_project_url(project, ref: ref, format: 'png') - "<a href='#{ci_project_url(project, ref: ref)}'><img src='#{url}' /></a>" - end - - def project_uses_specific_runner?(project) - project.runners.any? - end - - def no_runners_for_project?(project) - project.runners.blank? && - Ci::Runner.shared.blank? - end - end -end diff --git a/app/helpers/ci_badge_helper.rb b/app/helpers/ci_badge_helper.rb new file mode 100644 index 00000000000..27386133e36 --- /dev/null +++ b/app/helpers/ci_badge_helper.rb @@ -0,0 +1,13 @@ +module CiBadgeHelper + def markdown_badge_code(project, ref) + url = status_ci_project_url(project, ref: ref, format: 'png') + link = namespace_project_commits_path(project.namespace, project, ref) + "[![build status](#{url})](#{link})" + end + + def html_badge_code(project, ref) + url = status_ci_project_url(project, ref: ref, format: 'png') + link = namespace_project_commits_path(project.namespace, project, ref) + "<a href='#{link}'><img src='#{url}' /></a>" + end +end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 8e1f8f9ba6d..4fad35fd2fe 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -1,6 +1,6 @@ module CiStatusHelper def ci_status_path(ci_commit) - project = ci_commit.gl_project + project = ci_commit.project builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha) end @@ -52,7 +52,7 @@ module CiStatusHelper 'circle' end - icon(icon_name) + icon(icon_name + ' fw') end def render_ci_status(ci_commit) @@ -63,4 +63,9 @@ module CiStatusHelper ci_status_icon(ci_commit) end end + + def no_runners_for_project?(project) + project.runners.blank? && + Ci::Runner.shared.blank? + end end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index 1e372d5631d..c2ab80f2e0d 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -16,4 +16,14 @@ module GraphHelper ids = parents.map { |p| p.id } ids.zip(parent_spaces) end + + def success_ratio(success_builds, failed_builds) + failed_builds = failed_builds.count(:all) + success_builds = success_builds.count(:all) + + return 100 if failed_builds.zero? + + ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100 + ratio.to_i + end end diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb index 46eb82a354f..9fb42487a75 100644 --- a/app/helpers/runners_helper.rb +++ b/app/helpers/runners_helper.rb @@ -19,7 +19,7 @@ module RunnersHelper id = "\##{runner.id}" if current_user && current_user.admin - link_to ci_admin_runner_path(runner) do + link_to admin_runner_path(runner) do display_name + id end else diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb index 2a3a7e80fca..8cad994d10f 100644 --- a/app/helpers/triggers_helper.rb +++ b/app/helpers/triggers_helper.rb @@ -1,5 +1,5 @@ module TriggersHelper - def ci_build_trigger_url(project_id, ref_name) - "#{Settings.gitlab_ci.url}/ci/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger" + def builds_trigger_url(project_id) + "#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds" end end diff --git a/app/mailers/ci/emails/builds.rb b/app/mailers/ci/emails/builds.rb deleted file mode 100644 index 6fb4fba85e5..00000000000 --- a/app/mailers/ci/emails/builds.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Ci - module Emails - module Builds - def build_fail_email(build_id, to) - @build = Ci::Build.find(build_id) - @project = @build.project - mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha)) - end - - def build_success_email(build_id, to) - @build = Ci::Build.find(build_id) - @project = @build.project - mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha)) - end - end - end -end diff --git a/app/mailers/ci/notify.rb b/app/mailers/ci/notify.rb deleted file mode 100644 index 404842cf213..00000000000 --- a/app/mailers/ci/notify.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Ci - class Notify < ActionMailer::Base - include Ci::Emails::Builds - - add_template_helper Ci::GitlabHelper - - default_url_options[:host] = Gitlab.config.gitlab.host - default_url_options[:protocol] = Gitlab.config.gitlab.protocol - default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port? - default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root - - default from: Gitlab.config.gitlab.email_from - - # Just send email with 3 seconds delay - def self.delay - delay_for(2.seconds) - end - - private - - # Formats arguments into a String suitable for use as an email subject - # - # extra - Extra Strings to be inserted into the subject - # - # Examples - # - # >> subject('Lorem ipsum') - # => "GitLab-CI | Lorem ipsum" - # - # # Automatically inserts Project name when @project is set - # >> @project = Project.last - # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> - # >> subject('Lorem ipsum') - # => "GitLab-CI | Ruby on Rails | Lorem ipsum " - # - # # Accepts multiple arguments - # >> subject('Lorem ipsum', 'Dolor sit amet') - # => "GitLab-CI | Lorem ipsum | Dolor sit amet" - def subject(*extra) - subject = "GitLab-CI" - subject << (@project ? " | #{@project.name}" : "") - subject << " | " + extra.join(' | ') if extra.present? - subject - end - end -end diff --git a/app/mailers/emails/builds.rb b/app/mailers/emails/builds.rb new file mode 100644 index 00000000000..d58609a2de5 --- /dev/null +++ b/app/mailers/emails/builds.rb @@ -0,0 +1,15 @@ +module Emails + module Builds + def build_fail_email(build_id, to) + @build = Ci::Build.find(build_id) + @project = @build.project + mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha)) + end + + def build_success_email(build_id, to) + @build = Ci::Build.find(build_id) + @project = @build.project + mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha)) + end + end +end diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index caba63006da..b96418679bd 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -59,85 +59,17 @@ module Emails subject: subject("Project was moved")) end - def repository_push_email(project_id, recipient, author_id: nil, - ref: nil, - action: nil, - compare: nil, - reverse_compare: false, - send_from_committer_email: false, - disable_diffs: false) - unless author_id && ref && action - raise ArgumentError, "missing keywords: author_id, ref, action" - end + def repository_push_email(project_id, recipient, opts = {}) + @message = + Gitlab::Email::Message::RepositoryPush.new(self, project_id, recipient, opts) - @project = Project.find(project_id) - @current_user = @author = User.find(author_id) - @reverse_compare = reverse_compare - @compare = compare - @ref_name = Gitlab::Git.ref_name(ref) - @ref_type = Gitlab::Git.tag_ref?(ref) ? "tag" : "branch" - @action = action - @disable_diffs = disable_diffs - - if @compare - @commits = Commit.decorate(compare.commits, @project) - @diffs = compare.diffs - end - - @action_name = - case action - when :create - "pushed new" - when :delete - "deleted" - else - "pushed to" - end - - @subject = "[Git]" - @subject << "[#{@project.path_with_namespace}]" - @subject << "[#{@ref_name}]" if action == :push - @subject << " " - - if action == :push - if @commits.length > 1 - @target_url = namespace_project_compare_url(@project.namespace, - @project, - from: Commit.new(@compare.base, @project), - to: Commit.new(@compare.head, @project)) - @subject << "Deleted " if @reverse_compare - @subject << "#{@commits.length} commits: #{@commits.first.title}" - else - @target_url = namespace_project_commit_url(@project.namespace, - @project, @commits.first) - - @subject << "Deleted 1 commit: " if @reverse_compare - @subject << @commits.first.title - end - else - unless action == :delete - @target_url = namespace_project_tree_url(@project.namespace, - @project, @ref_name) - end - - subject_action = @action_name.dup - subject_action[0] = subject_action[0].capitalize - @subject << "#{subject_action} #{@ref_type} #{@ref_name}" - end - - @disable_footer = true - - reply_to = - if send_from_committer_email && can_send_from_user_email?(@author) - @author.email - else - Gitlab.config.gitlab.email_reply_to - end - - mail(from: sender(author_id, send_from_committer_email), - reply_to: reply_to, - to: recipient, - subject: @subject) + # used in notify layout + @target_url = @message.target_url + + mail(from: sender(@message.author_id, @message.send_from_committer_email?), + reply_to: @message.reply_to, + to: @message.recipient, + subject: @message.subject) end end end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 50a409c3754..9beb0de68f3 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -7,6 +7,7 @@ class Notify < BaseMailer include Emails::Projects include Emails::Profile include Emails::Groups + include Emails::Builds add_template_helper MergeRequestsHelper add_template_helper EmailsHelper @@ -33,13 +34,13 @@ class Notify < BaseMailer allowed_domains end - private - def can_send_from_user_email?(sender) sender_domain = sender.email.split("@").last self.class.allowed_email_domains.include?(sender_domain) end + private + # Return an email address that displays the name of the sender. # Only the displayed name changes; the actual email address is always the same. def sender(sender_id, send_from_user_email = false) diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb deleted file mode 100644 index 7f5df8ce6c4..00000000000 --- a/app/models/ci/application_setting.rb +++ /dev/null @@ -1,38 +0,0 @@ -# == Schema Information -# -# Table name: ci_application_settings -# -# id :integer not null, primary key -# all_broken_builds :boolean -# add_pusher :boolean -# created_at :datetime -# updated_at :datetime -# - -module Ci - class ApplicationSetting < ActiveRecord::Base - extend Ci::Model - CACHE_KEY = 'ci_application_setting.last' - - after_commit do - Rails.cache.write(CACHE_KEY, self) - end - - def self.expire - Rails.cache.delete(CACHE_KEY) - end - - def self.current - Rails.cache.fetch(CACHE_KEY) do - Ci::ApplicationSetting.last - end - end - - def self.create_from_defaults - create( - all_broken_builds: Settings.gitlab_ci['all_broken_builds'], - add_pusher: Settings.gitlab_ci['add_pusher'], - ) - end - end -end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 52ce1b920fa..6d9cdb95295 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -84,6 +84,7 @@ module Ci new_build.options = build.options new_build.commands = build.commands new_build.tag_list = build.tag_list + new_build.gl_project_id = build.gl_project_id new_build.commit_id = build.commit_id new_build.name = build.name new_build.allow_failure = build.allow_failure @@ -96,21 +97,16 @@ module Ci end state_machine :status, initial: :pending do - after_transition any => [:success, :failed, :canceled] do |build, transition| - return unless build.gl_project - - project = build.project + after_transition pending: :running do |build, transition| + build.execute_hooks + end - if project.web_hooks? - Ci::WebHookService.new.build_end(build) - end + after_transition any => [:success, :failed, :canceled] do |build, transition| + return unless build.project + build.update_coverage build.commit.create_next_builds(build) - project.execute_services(build) - - if project.coverage_enabled? - build.update_coverage - end + build.execute_hooks end end @@ -119,7 +115,7 @@ module Ci end def retryable? - commands.present? + project.builds_enabled? && commands.present? end def retried? @@ -132,7 +128,7 @@ module Ci end def timeout - project.timeout + project.build_timeout end def variables @@ -151,26 +147,21 @@ module Ci project.name end - def project_recipients - recipients = project.email_recipients.split(' ') - - if project.email_add_pusher? && user.present? && user.notification_email.present? - recipients << user.notification_email - end - - recipients.uniq - end - def repo_url - project.repo_url_with_auth + auth = "gitlab-ci-token:#{token}@" + project.http_url_to_repo.sub(/^https?:\/\//) do |prefix| + prefix + auth + end end def allow_git_fetch - project.allow_git_fetch + project.build_allow_git_fetch end def update_coverage - coverage = extract_coverage(trace, project.coverage_regex) + coverage_regex = project.build_coverage_regex + return unless coverage_regex + coverage = extract_coverage(trace, coverage_regex) if coverage.is_a? Numeric update_attributes(coverage: coverage) @@ -203,7 +194,7 @@ module Ci def trace trace = raw_trace if project && trace.present? - trace.gsub(project.token, 'xxxxxx') + trace.gsub(project.runners_token, 'xxxxxx') else trace end @@ -230,29 +221,29 @@ module Ci end def token - project.token + project.runners_token end def valid_token? token - project.valid_token? token + project.valid_runners_token? token end def target_url Gitlab::Application.routes.url_helpers. - namespace_project_build_url(gl_project.namespace, gl_project, self) + namespace_project_build_url(project.namespace, project, self) end def cancel_url if active? Gitlab::Application.routes.url_helpers. - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self) + cancel_namespace_project_build_path(project.namespace, project, self) end end def retry_url if retryable? Gitlab::Application.routes.url_helpers. - retry_namespace_project_build_path(gl_project.namespace, gl_project, self) + retry_namespace_project_build_path(project.namespace, project, self) end end @@ -271,10 +262,18 @@ module Ci def download_url if artifacts_file.exists? Gitlab::Application.routes.url_helpers. - download_namespace_project_build_path(gl_project.namespace, gl_project, self) + download_namespace_project_build_path(project.namespace, project, self) end end + def execute_hooks + build_data = Gitlab::BuildDataBuilder.build(self) + project.execute_hooks(build_data.dup, :build_hooks) + project.execute_services(build_data.dup, :build_hooks) + end + + + private def yaml_variables diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 75465685e98..d2a29236942 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -20,8 +20,8 @@ module Ci class Commit < ActiveRecord::Base extend Ci::Model - belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id - has_many :statuses, dependent: :destroy, class_name: 'CommitStatus' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id + has_many :statuses, class_name: 'CommitStatus' has_many :builds, class_name: 'Ci::Build' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' @@ -38,10 +38,6 @@ module Ci sha end - def project - @project ||= gl_project.ensure_gitlab_ci_project - end - def project_id project.id end @@ -57,7 +53,7 @@ module Ci end def valid_commit_sha - if self.sha == Ci::Git::BLANK_SHA + if self.sha == Gitlab::Git::BLANK_SHA self.errors.add(:sha, " cant be 00000000 (branch removal)") end end @@ -79,7 +75,7 @@ module Ci end def commit_data - @commit ||= gl_project.commit(sha) + @commit ||= project.commit(sha) rescue nil end @@ -178,16 +174,18 @@ module Ci duration_array.reduce(:+).to_i end + def started_at + @started_at ||= statuses.order('started_at ASC').first.try(:started_at) + end + def finished_at @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at) end def coverage - if project.coverage_enabled? - coverage_array = latest_builds.map(&:coverage).compact - if coverage_array.size >= 1 - '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) - end + coverage_array = latest_builds.map(&:coverage).compact + if coverage_array.size >= 1 + '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) end end @@ -197,7 +195,7 @@ module Ci def config_processor return nil unless ci_yaml_file - @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) + @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e save_yaml_error(e.message) nil @@ -207,7 +205,7 @@ module Ci end def ci_yaml_file - @ci_yaml_file ||= gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data + @ci_yaml_file ||= project.repository.blob_at(sha, '.gitlab-ci.yml').data rescue nil end diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb deleted file mode 100644 index 8c39be42677..00000000000 --- a/app/models/ci/event.rb +++ /dev/null @@ -1,27 +0,0 @@ -# == Schema Information -# -# Table name: ci_events -# -# id :integer not null, primary key -# project_id :integer -# user_id :integer -# is_admin :integer -# description :text -# created_at :datetime -# updated_at :datetime -# - -module Ci - class Event < ActiveRecord::Base - extend Ci::Model - - belongs_to :project, class_name: 'Ci::Project' - - validates :description, - presence: true, - length: { in: 5..200 } - - scope :admin, ->(){ where(is_admin: true) } - scope :project_wide, ->(){ where(is_admin: false) } - end -end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb deleted file mode 100644 index 669ee1cc0d2..00000000000 --- a/app/models/ci/project.rb +++ /dev/null @@ -1,192 +0,0 @@ -# == Schema Information -# -# Table name: ci_projects -# -# id :integer not null, primary key -# name :string(255) -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -module Ci - class Project < ActiveRecord::Base - extend Ci::Model - - include Ci::ProjectStatus - - belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id - - has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' - has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' - has_many :web_hooks, dependent: :destroy, class_name: 'Ci::WebHook' - has_many :events, dependent: :destroy, class_name: 'Ci::Event' - has_many :variables, dependent: :destroy, class_name: 'Ci::Variable' - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' - - # Project services - has_many :services, dependent: :destroy, class_name: 'Ci::Service' - has_one :hip_chat_service, dependent: :destroy, class_name: 'Ci::HipChatService' - has_one :slack_service, dependent: :destroy, class_name: 'Ci::SlackService' - has_one :mail_service, dependent: :destroy, class_name: 'Ci::MailService' - - accepts_nested_attributes_for :variables, allow_destroy: true - - delegate :name_with_namespace, :path_with_namespace, :web_url, :http_url_to_repo, :ssh_url_to_repo, to: :gl_project - - # - # Validations - # - validates_presence_of :timeout, :token, :default_ref, :gitlab_id - - validates_uniqueness_of :gitlab_id - - validates :polling_interval, - presence: true, - if: ->(project) { project.always_build.present? } - - before_validation :set_default_values - - class << self - include Ci::CurrentSettings - - def unassigned(runner) - joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \ - "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}"). - where("#{Ci::RunnerProject.table_name}.project_id" => nil) - end - - def ordered_by_last_commit_date - last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)" - joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id"). - joins(:gl_project). - order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") - end - end - - def name - name_with_namespace - end - - def path - path_with_namespace - end - - def gitlab_url - web_url - end - - def any_runners?(&block) - if runners.active.any?(&block) - return true - end - - shared_runners_enabled && Ci::Runner.shared.active.any?(&block) - end - - def set_default_values - self.token = SecureRandom.hex(15) if self.token.blank? - self.default_ref ||= 'master' - end - - def tracked_refs - @tracked_refs ||= default_ref.split(",").map { |ref| ref.strip } - end - - def valid_token? token - self.token && self.token == token - end - - def no_running_builds? - # Get running builds not later than 3 days ago to ignore hangs - builds.running.where("updated_at > ?", 3.days.ago).empty? - end - - def email_notification? - email_add_pusher || email_recipients.present? - end - - def web_hooks? - web_hooks.any? - end - - def services? - services.any? - end - - def timeout_in_minutes - timeout / 60 - end - - def timeout_in_minutes=(value) - self.timeout = value.to_i * 60 - end - - def coverage_enabled? - coverage_regex.present? - end - - # Build a clone-able repo url - # using http and basic auth - def repo_url_with_auth - auth = "gitlab-ci-token:#{token}@" - http_url_to_repo.sub(/^https?:\/\//) do |prefix| - prefix + auth - end - end - - def available_services_names - %w(slack mail hip_chat) - end - - def build_missing_services - available_services_names.each do |service_name| - service = services.find { |service| service.to_param == service_name } - - # If service is available but missing in db - # we should create an instance. Ex `create_gitlab_ci_service` - self.send :"create_#{service_name}_service" if service.nil? - end - end - - def execute_services(data) - services.each do |service| - - # Call service hook only if it is active - begin - service.execute(data) if service.active && service.can_execute?(data) - rescue => e - logger.error(e) - end - end - end - - def setup_finished? - commits.any? - end - - def commits - gl_project.ci_commits.ordered - end - - def builds - gl_project.ci_builds - end - end -end diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb deleted file mode 100644 index 2d35aeac225..00000000000 --- a/app/models/ci/project_status.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Ci - module ProjectStatus - def status - last_commit.status if last_commit - end - - def broken? - last_commit.failed? if last_commit - end - - def success? - last_commit.success? if last_commit - end - - def broken_or_success? - broken? || success? - end - - def last_commit - @last_commit ||= commits.last if commits.any? - end - - def last_commit_date - last_commit.try(:created_at) - end - - def human_status - status - end - end -end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 89710485811..38b20cd7faa 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -25,7 +25,7 @@ module Ci has_many :builds, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' - has_many :projects, through: :runner_projects, class_name: 'Ci::Project' + has_many :projects, through: :runner_projects, class_name: '::Project', foreign_key: :gl_project_id has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' @@ -45,10 +45,6 @@ module Ci query: "%#{query.try(:downcase)}%") end - def gl_projects_ids - projects.select(:gitlab_id) - end - def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? end diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 3f4fc43873e..93d9be144e8 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -14,8 +14,8 @@ module Ci extend Ci::Model belongs_to :runner, class_name: 'Ci::Runner' - belongs_to :project, class_name: 'Ci::Project' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id - validates_uniqueness_of :runner_id, scope: :project_id + validates_uniqueness_of :runner_id, scope: :gl_project_id end end diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb deleted file mode 100644 index 8063c51e82b..00000000000 --- a/app/models/ci/service.rb +++ /dev/null @@ -1,105 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -# To add new service you should build a class inherited from Service -# and implement a set of methods -module Ci - class Service < ActiveRecord::Base - extend Ci::Model - - serialize :properties, JSON - - default_value_for :active, false - - after_initialize :initialize_properties - - belongs_to :project, class_name: 'Ci::Project' - - validates :project_id, presence: true - - def activated? - active - end - - def category - :common - end - - def initialize_properties - self.properties = {} if properties.nil? - end - - def title - # implement inside child - end - - def description - # implement inside child - end - - def help - # implement inside child - end - - def to_param - # implement inside child - end - - def fields - # implement inside child - [] - end - - def can_test? - project.builds.any? - end - - def can_execute?(build) - true - end - - def execute(build) - # implement inside child - end - - # Provide convenient accessor methods - # for each serialized property. - def self.prop_accessor(*args) - args.each do |arg| - class_eval %{ - def #{arg} - (properties || {})['#{arg}'] - end - - def #{arg}=(value) - self.properties ||= {} - self.properties['#{arg}'] = value - end - } - end - end - - def self.boolean_accessor(*args) - self.prop_accessor(*args) - - args.each do |arg| - class_eval %{ - def #{arg}? - ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) - end - } - end - end - end -end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index b73c35d5ae5..23516709a41 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -16,7 +16,7 @@ module Ci acts_as_paranoid - belongs_to :project, class_name: 'Ci::Project' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' validates_presence_of :token diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index b3d2b809e03..56759d3e50f 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -15,10 +15,10 @@ module Ci class Variable < ActiveRecord::Base extend Ci::Model - belongs_to :project, class_name: 'Ci::Project' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id validates_presence_of :key - validates_uniqueness_of :key, scope: :project_id + validates_uniqueness_of :key, scope: :gl_project_id attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base end diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb deleted file mode 100644 index 0dc15eb6683..00000000000 --- a/app/models/ci/web_hook.rb +++ /dev/null @@ -1,43 +0,0 @@ -# == Schema Information -# -# Table name: ci_web_hooks -# -# id :integer not null, primary key -# url :string(255) not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -module Ci - class WebHook < ActiveRecord::Base - extend Ci::Model - - include HTTParty - - belongs_to :project, class_name: 'Ci::Project' - - # HTTParty timeout - default_timeout 10 - - validates :url, presence: true, url: true - - def execute(data) - parsed_url = URI.parse(url) - if parsed_url.userinfo.blank? - Ci::WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false) - else - post_url = url.gsub("#{parsed_url.userinfo}@", "") - auth = { - username: URI.decode(parsed_url.user), - password: URI.decode(parsed_url.password), - } - Ci::WebHook.post(post_url, - body: data.to_json, - headers: { "Content-Type" => "application/json" }, - verify: false, - basic_auth: auth) - end - end - end -end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index ff619965a57..21c5c87bc3d 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -30,6 +30,7 @@ class CommitStatus < ActiveRecord::Base self.table_name = 'ci_builds' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :commit, class_name: 'Ci::Commit' belongs_to :user @@ -76,7 +77,7 @@ class CommitStatus < ActiveRecord::Base end after_transition [:pending, :running] => :success do |build, transition| - MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build) + MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.project, nil).trigger(build) end state :pending, value: 'pending' @@ -86,8 +87,7 @@ class CommitStatus < ActiveRecord::Base state :canceled, value: 'canceled' end - delegate :sha, :short_sha, :gl_project, - to: :commit, prefix: false + delegate :sha, :short_sha, to: :commit, prefix: false # TODO: this should be removed with all references def before_sha diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index 337b3097126..22638057773 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -25,4 +25,5 @@ class ProjectHook < WebHook scope :issue_hooks, -> { where(issues_events: true) } scope :note_hooks, -> { where(note_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) } + scope :build_hooks, -> { where(build_events: true) } end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 715ec5908b7..40eb0e20b4b 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -26,6 +26,7 @@ class WebHook < ActiveRecord::Base default_value_for :note_events, false default_value_for :merge_requests_events, false default_value_for :tag_push_events, false + default_value_for :build_events, false default_value_for :enable_ssl_verification, true # HTTParty timeout diff --git a/app/models/issue.rb b/app/models/issue.rb index 187b6482b6c..e04035b3af8 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -83,6 +83,14 @@ class Issue < ActiveRecord::Base reference end + def referenced_merge_requests + references = [self, *notes].flat_map do |note| + note.all_references(load_lazy_references: false).merge_requests + end.uniq + + Gitlab::Markdown::ReferenceFilter::LazyReference.load(references).uniq.sort_by(&:iid) + end + # Reset issue events cache # # Since we do cache @event we need to reset cache in special cases: diff --git a/app/models/note.rb b/app/models/note.rb index 8f0efa8d4b7..04053ccc61e 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -377,6 +377,7 @@ class Note < ActiveRecord::Base end def award_emoji_name - note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] + original_name = note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] + AwardEmoji.normilize_emoji_name(original_name) end end diff --git a/app/models/project.rb b/app/models/project.rb index e78868af1cc..e1f7bf971e3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -56,6 +56,7 @@ class Project < ActiveRecord::Base default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wall_enabled, false default_value_for :snippets_enabled, gitlab_config_features.snippets + default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } # set last_activity_at to the same as created_at after_create :set_last_activity_at @@ -77,10 +78,10 @@ class Project < ActiveRecord::Base # Project services has_many :services - has_one :gitlab_ci_service, dependent: :destroy has_one :campfire_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy + has_one :builds_email_service, dependent: :destroy has_one :irker_service, dependent: :destroy has_one :pivotaltracker_service, dependent: :destroy has_one :hipchat_service, dependent: :destroy @@ -121,14 +122,21 @@ class Project < ActiveRecord::Base has_many :deploy_keys, through: :deploy_keys_projects has_many :users_star_projects, dependent: :destroy has_many :starrers, through: :users_star_projects, source: :user - has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id - has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :releases, dependent: :destroy has_many :lfs_objects_projects, dependent: :destroy has_many :lfs_objects, through: :lfs_objects_projects has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" - has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id + + has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id + has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id + has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses + has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id + has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' + has_many :variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id + + accepts_nested_attributes_for :variables, allow_destroy: true delegate :name, to: :owner, allow_nil: true, prefix: true delegate :members, to: :team, prefix: true @@ -161,6 +169,11 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } + before_validation :set_runners_token_token + def set_runners_token_token + self.runners_token = SecureRandom.hex(15) if self.runners_token.blank? + end + mount_uploader :avatar, AvatarUploader # Scopes @@ -256,6 +269,10 @@ class Project < ActiveRecord::Base projects.iwhere('projects.path' => project_path).take end + def find_by_ci_id(id) + find_by(ci_id: id.to_i) + end + def visibility_levels Gitlab::VisibilityLevel.options end @@ -790,28 +807,6 @@ class Project < ActiveRecord::Base ci_commit(sha) || ci_commits.create(sha: sha) end - def ensure_gitlab_ci_project - gitlab_ci_project || create_gitlab_ci_project( - shared_runners_enabled: current_application_settings.shared_runners_enabled - ) - end - - # TODO: this should be migrated to Project table, - # the same as issues_enabled - def builds_enabled - gitlab_ci_service && gitlab_ci_service.active - end - - def builds_enabled? - builds_enabled - end - - def builds_enabled=(value) - service = gitlab_ci_service || create_gitlab_ci_service - service.active = value - service.save - end - def enable_ci self.builds_enabled = true end @@ -825,4 +820,34 @@ class Project < ActiveRecord::Base forked_project_link.destroy end end + + def any_runners?(&block) + if runners.active.any?(&block) + return true + end + + shared_runners_enabled? && Ci::Runner.shared.active.any?(&block) + end + + def valid_runners_token? token + self.runners_token && self.runners_token == token + end + + # TODO (ayufan): For now we use runners_token (backward compatibility) + # In 8.4 every build will have its own individual token valid for time of build + def valid_build_token? token + self.builds_enabled? && self.runners_token && self.runners_token == token + end + + def build_coverage_enabled? + build_coverage_regex.present? + end + + def build_timeout_in_minutes + build_timeout / 60 + end + + def build_timeout_in_minutes=(value) + self.build_timeout = value.to_i * 60 + end end diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb new file mode 100644 index 00000000000..8247c79fc33 --- /dev/null +++ b/app/models/project_services/builds_email_service.rb @@ -0,0 +1,90 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +class BuildsEmailService < Service + prop_accessor :recipients + boolean_accessor :add_pusher + boolean_accessor :notify_only_broken_builds + validates :recipients, presence: true, if: :activated? + + def initialize_properties + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + end + end + + def title + 'Builds emails' + end + + def description + 'Email the builds status to a list of recipients.' + end + + def to_param + 'builds_email' + end + + def supported_events + %w(build) + end + + def execute(push_data) + return unless supported_events.include?(push_data[:object_kind]) + + if should_build_be_notified?(push_data) + BuildEmailWorker.perform_async( + push_data[:build_id], + all_recipients(push_data), + push_data, + ) + end + end + + def fields + [ + { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by comma' }, + { type: 'checkbox', name: 'add_pusher', label: 'Add pusher to recipients list' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + ] + end + + def should_build_be_notified?(data) + case data[:build_status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end + + def all_recipients(data) + all_recipients = recipients.split(',') + + if add_pusher? && data[:user][:email] + all_recipients << "#{data[:user][:email]}" + end + + all_recipients + end +end diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb deleted file mode 100644 index d89466b689f..00000000000 --- a/app/models/project_services/ci/hip_chat_message.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Ci - class HipChatMessage - include Gitlab::Application.routes.url_helpers - - attr_reader :build - - def initialize(build) - @build = build - end - - def to_s - lines = Array.new - lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ") - lines.push("<a href=\"#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>") - lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>") - lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).") - lines.join('') - end - - def status_color(build_or_commit=nil) - build_or_commit ||= commit_status - case build_or_commit - when :success - 'green' - when :failed, :canceled - 'red' - else # :pending, :running or unknown - 'yellow' - end - end - - def notify? - [:failed, :canceled].include?(commit_status) - end - - - private - - def commit - build.commit - end - - def project - commit.project - end - - def build_status - build.status.to_sym - end - - def commit_status - commit.status.to_sym - end - - def humanized_status(build_or_commit=nil) - build_or_commit ||= commit_status - case build_or_commit - when :pending - "Pending" - when :running - "Running" - when :failed - "Failed" - when :success - "Successful" - when :canceled - "Canceled" - else - "Unknown" - end - end - end -end diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb deleted file mode 100644 index 0df03890efb..00000000000 --- a/app/models/project_services/ci/hip_chat_service.rb +++ /dev/null @@ -1,93 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -module Ci - class HipChatService < Ci::Service - prop_accessor :hipchat_token, :hipchat_room, :hipchat_server - boolean_accessor :notify_only_broken_builds - validates :hipchat_token, presence: true, if: :activated? - validates :hipchat_room, presence: true, if: :activated? - default_value_for :notify_only_broken_builds, true - - def title - "HipChat" - end - - def description - "Private group chat, video chat, instant messaging for teams" - end - - def help - end - - def to_param - 'hip_chat' - end - - def fields - [ - { type: 'text', name: 'hipchat_token', label: 'Token', placeholder: '' }, - { type: 'text', name: 'hipchat_room', label: 'Room', placeholder: '' }, - { type: 'text', name: 'hipchat_server', label: 'Server', placeholder: 'https://hipchat.example.com', help: 'Leave blank for default' }, - { type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' } - ] - end - - def can_execute?(build) - return if build.allow_failure? - - commit = build.commit - return unless commit - return unless commit.latest_builds.include? build - - case commit.status.to_sym - when :failed - true - when :success - true unless notify_only_broken_builds? - else - false - end - end - - def execute(build) - msg = Ci::HipChatMessage.new(build) - opts = default_options.merge( - token: hipchat_token, - room: hipchat_room, - server: server_url, - color: msg.status_color, - notify: msg.notify? - ) - Ci::HipChatNotifierWorker.perform_async(msg.to_s, opts) - end - - private - - def default_options - { - service_name: 'GitLab CI', - message_format: 'html' - } - end - - def server_url - if hipchat_server.blank? - 'https://api.hipchat.com' - else - hipchat_server - end - end - end -end diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb deleted file mode 100644 index bb961d06972..00000000000 --- a/app/models/project_services/ci/mail_service.rb +++ /dev/null @@ -1,84 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -module Ci - class MailService < Ci::Service - delegate :email_recipients, :email_recipients=, - :email_add_pusher, :email_add_pusher=, - :email_only_broken_builds, :email_only_broken_builds=, to: :project, prefix: false - - before_save :update_project - - default_value_for :active, true - - def title - 'Mail' - end - - def description - 'Email notification' - end - - def to_param - 'mail' - end - - def fields - [ - { type: 'text', name: 'email_recipients', label: 'Recipients', help: 'Whitespace-separated list of recipient addresses' }, - { type: 'checkbox', name: 'email_add_pusher', label: 'Add pusher to recipients list' }, - { type: 'checkbox', name: 'email_only_broken_builds', label: 'Notify only broken builds' } - ] - end - - def can_execute?(build) - return if build.allow_failure? - - # it doesn't make sense to send emails for retried builds - commit = build.commit - return unless commit - return unless commit.latest_builds.include?(build) - - case build.status.to_sym - when :failed - true - when :success - true unless email_only_broken_builds - else - false - end - end - - def execute(build) - build.project_recipients.each do |recipient| - case build.status.to_sym - when :success - mailer.build_success_email(build.id, recipient).deliver_later - when :failed - mailer.build_fail_email(build.id, recipient).deliver_later - end - end - end - - private - - def update_project - project.save! - end - - def mailer - Ci::Notify - end - end -end diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb deleted file mode 100644 index 1a6ff8e34c9..00000000000 --- a/app/models/project_services/ci/slack_message.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'slack-notifier' - -module Ci - class SlackMessage - include Gitlab::Application.routes.url_helpers - - def initialize(commit) - @commit = commit - end - - def pretext - '' - end - - def color - attachment_color - end - - def fallback - format(attachment_message) - end - - def attachments - fields = [] - - commit.latest_builds.each do |build| - next if build.allow_failure? - next unless build.failed? - fields << { - title: build.name, - value: "Build <#{namespace_project_build_url(build.gl_project.namespace, build.gl_project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)." - } - end - - [{ - text: attachment_message, - color: attachment_color, - fields: fields - }] - end - - private - - attr_reader :commit - - def attachment_message - out = "<#{ci_project_url(project)}|#{project_name}>: " - out << "Commit <#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> " - out << "(<#{commit_sha_link}|#{commit.short_sha}>) " - out << "of <#{commit_ref_link}|#{commit.ref}> " - out << "by #{commit.git_author_name} " if commit.git_author_name - out << "#{commit_status} in " - out << "#{commit.duration} second(s)" - end - - def format(string) - Slack::Notifier::LinkFormatter.format(string) - end - - def project - commit.project - end - - def project_name - project.name - end - - def commit_sha_link - "#{project.gitlab_url}/commit/#{commit.sha}" - end - - def commit_ref_link - "#{project.gitlab_url}/commits/#{commit.ref}" - end - - def attachment_color - if commit.success? - 'good' - else - 'danger' - end - end - - def commit_status - if commit.success? - 'succeeded' - else - 'failed' - end - end - end -end diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb deleted file mode 100644 index 7064bfe78db..00000000000 --- a/app/models/project_services/ci/slack_service.rb +++ /dev/null @@ -1,81 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -module Ci - class SlackService < Ci::Service - prop_accessor :webhook - boolean_accessor :notify_only_broken_builds - validates :webhook, presence: true, if: :activated? - - default_value_for :notify_only_broken_builds, true - - def title - 'Slack' - end - - def description - 'A team communication tool for the 21st century' - end - - def to_param - 'slack' - end - - def help - 'Visit https://www.slack.com/services/new/incoming-webhook. Then copy link and save project!' unless webhook.present? - end - - def fields - [ - { type: 'text', name: 'webhook', label: 'Webhook URL', placeholder: '' }, - { type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' } - ] - end - - def can_execute?(build) - return if build.allow_failure? - - commit = build.commit - return unless commit - return unless commit.latest_builds.include?(build) - - case commit.status.to_sym - when :failed - true - when :success - true unless notify_only_broken_builds? - else - false - end - end - - def execute(build) - message = Ci::SlackMessage.new(build.commit) - options = default_options.merge( - color: message.color, - fallback: message.fallback, - attachments: message.attachments - ) - Ci::SlackNotifierWorker.perform_async(webhook, message.pretext, options) - end - - private - - def default_options - { - username: 'GitLab CI' - } - end - end -end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 234e8e8b580..d73182d40ac 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -19,76 +19,5 @@ # class GitlabCiService < CiService - include Gitlab::Application.routes.url_helpers - - after_save :compose_service_hook, if: :activated? - after_save :ensure_gitlab_ci_project, if: :activated? - - def compose_service_hook - hook = service_hook || build_service_hook - hook.save - end - - def ensure_gitlab_ci_project - return unless project - project.ensure_gitlab_ci_project - end - - def supported_events - %w(push tag_push) - end - - def execute(data) - return unless supported_events.include?(data[:object_kind]) - - ci_project = project.gitlab_ci_project - if ci_project - current_user = User.find_by(id: data[:user_id]) - Ci::CreateCommitService.new.execute(ci_project, current_user, data) - end - end - - def token - if project.gitlab_ci_project.present? - project.gitlab_ci_project.token - end - end - - def get_ci_commit(sha, ref) - Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha) - end - - def commit_status(sha, ref) - get_ci_commit(sha, ref).status - rescue ActiveRecord::RecordNotFound - :error - end - - def commit_coverage(sha, ref) - get_ci_commit(sha, ref).coverage - rescue ActiveRecord::RecordNotFound - :error - end - - def build_page(sha, ref) - if project.gitlab_ci_project.present? - builds_namespace_project_commit_url(project.namespace, project, sha) - end - end - - def title - 'GitLab CI' - end - - def description - 'Continuous integration server from GitLab' - end - - def to_param - 'gitlab_ci' - end - - def fields - [] - end + # this is no longer used end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index af2840a57f0..1e1686a11c6 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -22,8 +22,16 @@ class HipchatService < Service MAX_COMMITS = 3 prop_accessor :token, :room, :server, :notify, :color, :api_version + boolean_accessor :notify_only_broken_builds validates :token, presence: true, if: :activated? + def initialize_properties + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + end + end + def title 'HipChat' end @@ -45,12 +53,13 @@ class HipchatService < Service { type: 'text', name: 'api_version', placeholder: 'Leave blank for default (v2)' }, { type: 'text', name: 'server', - placeholder: 'Leave blank for default. https://hipchat.example.com' } + placeholder: 'Leave blank for default. https://hipchat.example.com' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, ] end def supported_events - %w(push issue merge_request note tag_push) + %w(push issue merge_request note tag_push build) end def execute(data) @@ -94,6 +103,8 @@ class HipchatService < Service create_merge_request_message(data) unless is_update?(data) when "note" create_note_message(data) + when "build" + create_build_message(data) if should_build_be_notified?(data) end end @@ -235,6 +246,20 @@ class HipchatService < Service message end + def create_build_message(data) + ref_type = data[:tag] ? 'tag' : 'branch' + ref = data[:ref] + sha = data[:sha] + user_name = data[:commit][:author_name] + status = data[:commit][:status] + duration = data[:commit][:duration] + + branch_link = "<a href=\"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>" + commit_link = "<a href=\"#{project_url}/commit/#{URI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>" + + "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)" + end + def project_name project.name_with_namespace.gsub(/\s/, '') end @@ -250,4 +275,24 @@ class HipchatService < Service def is_update?(data) data[:object_attributes][:action] == 'update' end + + def humanized_status(status) + case status + when 'success' + 'passed' + else + status + end + end + + def should_build_be_notified?(data) + case data[:commit][:status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 7cd5e892507..375b4534d07 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -20,8 +20,16 @@ class SlackService < Service prop_accessor :webhook, :username, :channel + boolean_accessor :notify_only_broken_builds validates :webhook, presence: true, if: :activated? + def initialize_properties + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + end + end + def title 'Slack' end @@ -45,12 +53,13 @@ class SlackService < Service { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, { type: 'text', name: 'username', placeholder: 'username' }, - { type: 'text', name: 'channel', placeholder: '#channel' } + { type: 'text', name: 'channel', placeholder: '#channel' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, ] end def supported_events - %w(push issue merge_request note tag_push) + %w(push issue merge_request note tag_push build) end def execute(data) @@ -78,6 +87,8 @@ class SlackService < Service MergeMessage.new(data) unless is_update?(data) when "note" NoteMessage.new(data) + when "build" + BuildMessage.new(data) if should_build_be_notified?(data) end opt = {} @@ -86,7 +97,7 @@ class SlackService < Service if message notifier = Slack::Notifier.new(webhook, opt) - notifier.ping(message.pretext, attachments: message.attachments) + notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) end end @@ -103,9 +114,21 @@ class SlackService < Service 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 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" diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/slack_service/base_message.rb index aa00d6061a1..f1182824687 100644 --- a/app/models/project_services/slack_service/base_message.rb +++ b/app/models/project_services/slack_service/base_message.rb @@ -10,6 +10,9 @@ class SlackService format(message) end + def fallback + end + def attachments raise NotImplementedError end diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/slack_service/build_message.rb new file mode 100644 index 00000000000..c124cad4afd --- /dev/null +++ b/app/models/project_services/slack_service/build_message.rb @@ -0,0 +1,82 @@ +class SlackService + class BuildMessage < BaseMessage + attr_reader :sha + attr_reader :ref_type + attr_reader :ref + attr_reader :status + attr_reader :project_name + attr_reader :project_url + attr_reader :user_name + attr_reader :duration + + def initialize(params, commit = true) + @sha = params[:sha] + @ref_type = params[:tag] ? 'tag' : 'branch' + @ref = params[:ref] + @project_name = params[:project_name] + @project_url = params[:project_url] + @status = params[:commit][:status] + @user_name = params[:commit][:author_name] + @duration = params[:commit][:duration] + end + + def pretext + '' + end + + def fallback + format(message) + end + + def attachments + [{ text: format(message), color: attachment_color }] + end + + private + + def message + "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} second(s)" + end + + def format(string) + Slack::Notifier::LinkFormatter.format(string) + end + + def humanized_status + case status + when 'success' + 'passed' + else + status + end + end + + def attachment_color + if status == 'success' + 'good' + else + 'danger' + end + end + + def branch_url + "#{project_url}/commits/#{ref}" + end + + def branch_link + "[#{ref}](#{branch_url})" + end + + def project_link + "[#{project_name}](#{project_url})" + end + + def commit_url + "#{project_url}/commit/#{sha}/builds" + end + + def commit_link + "[#{Commit.truncate_sha(sha)}](#{commit_url})" + end + end +end diff --git a/app/models/service.rb b/app/models/service.rb index d610abd1683..d3bf7f0ebd1 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -30,6 +30,7 @@ class Service < ActiveRecord::Base default_value_for :merge_requests_events, true default_value_for :tag_push_events, true default_value_for :note_events, true + default_value_for :build_events, true after_initialize :initialize_properties @@ -40,13 +41,14 @@ class Service < ActiveRecord::Base validates :project_id, presence: true, unless: Proc.new { |service| service.template? } - scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') } + scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } scope :push_hooks, -> { where(push_events: true, active: true) } scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) } scope :issue_hooks, -> { where(issues_events: true, active: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) } scope :note_hooks, -> { where(note_events: true, active: true) } + scope :build_hooks, -> { where(build_events: true, active: true) } def activated? active @@ -133,6 +135,21 @@ class Service < ActiveRecord::Base end end + # Provide convenient boolean accessor methods + # for each serialized property. + # Also keep track of updated properties in a similar way as ActiveModel::Dirty + def self.boolean_accessor(*args) + self.prop_accessor(*args) + + args.each do |arg| + class_eval %{ + def #{arg}? + ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) + end + } + end + end + # Returns a hash of the properties that have been assigned a new value since last save, # indicating their original values (attr => original value). # ActiveRecord does not provide a mechanism to track changes in serialized keys, @@ -163,6 +180,7 @@ class Service < ActiveRecord::Base assembla bamboo buildkite + builds_email campfire custom_issue_tracker drone_ci @@ -170,7 +188,6 @@ class Service < ActiveRecord::Base external_wiki flowdock gemnasium - gitlab_ci hipchat irker jira diff --git a/app/models/user.rb b/app/models/user.rb index 1a8d8f1e249..fdd14f4571d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -136,7 +136,7 @@ class User < ActiveRecord::Base has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest" has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy has_one :abuse_report, dependent: :destroy - has_many :ci_builds, dependent: :nullify, class_name: 'Ci::Build' + has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # @@ -771,10 +771,9 @@ class User < ActiveRecord::Base def ci_authorized_runners @ci_authorized_runners ||= begin - runner_ids = Ci::RunnerProject.joins(:project). - where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})"). + runner_ids = Ci::RunnerProject. + where("ci_runner_projects.gl_project_id IN (#{ci_projects_union.to_sql})"). select(:runner_id) - Ci::Runner.specific.where(id: runner_ids) end end diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb index 912eb6258a4..ad901f2da5d 100644 --- a/app/services/ci/create_builds_service.rb +++ b/app/services/ci/create_builds_service.rb @@ -29,9 +29,11 @@ module Ci build_attrs.merge!(ref: ref, tag: tag, trigger_request: trigger_request, - user: user) + user: user, + project: commit.project) - commit.builds.create!(build_attrs) + build = commit.builds.create!(build_attrs) + build.execute_hooks end end end diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb deleted file mode 100644 index 479a2d6defc..00000000000 --- a/app/services/ci/create_commit_service.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Ci - class CreateCommitService - def execute(project, user, params) - sha = params[:checkout_sha] || params[:after] - origin_ref = params[:ref] - - unless origin_ref && sha.present? - return false - end - - ref = origin_ref.gsub(/\Arefs\/(tags|heads)\//, '') - - # Skip branch removal - if sha == Ci::Git::BLANK_SHA - return false - end - - tag = origin_ref.start_with?('refs/tags/') - commit = project.gl_project.ensure_ci_commit(sha) - unless commit.skip_ci? - commit.update_committed! - commit.create_builds(ref, tag, user) - end - - commit - end - end -end diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb index 4b86cb0a1f5..b3dfc707221 100644 --- a/app/services/ci/create_trigger_request_service.rb +++ b/app/services/ci/create_trigger_request_service.rb @@ -1,13 +1,13 @@ module Ci class CreateTriggerRequestService def execute(project, trigger, ref, variables = nil) - commit = project.gl_project.commit(ref) + commit = project.commit(ref) return unless commit # check if ref is tag - tag = project.gl_project.repository.find_tag(ref).present? + tag = project.repository.find_tag(ref).present? - ci_commit = project.gl_project.ensure_ci_commit(commit.sha) + ci_commit = project.ensure_ci_commit(commit.sha) trigger_request = trigger.trigger_requests.create!( variables: variables, diff --git a/app/services/ci/event_service.rb b/app/services/ci/event_service.rb deleted file mode 100644 index 3f4e02dd26c..00000000000 --- a/app/services/ci/event_service.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Ci - class EventService - def remove_project(user, project) - create( - description: "Project \"#{project.name}\" has been removed by #{user.username}", - user_id: user.id, - is_admin: true - ) - end - - def create_project(user, project) - create( - description: "Project \"#{project.name}\" has been created by #{user.username}", - user_id: user.id, - is_admin: true - ) - end - - def change_project_settings(user, project) - create( - project_id: project.id, - user_id: user.id, - description: "User \"#{user.username}\" updated projects settings" - ) - end - - def create(*args) - Ci::Event.create!(*args) - end - end -end diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb index b8d24193035..f469b13e902 100644 --- a/app/services/ci/image_for_build_service.rb +++ b/app/services/ci/image_for_build_service.rb @@ -4,10 +4,10 @@ module Ci sha = params[:sha] sha ||= if params[:ref] - project.gl_project.commit(params[:ref]).try(:sha) + project.commit(params[:ref]).try(:sha) end - commit = project.commits.ordered.find_by(sha: sha) + commit = project.ci_commits.ordered.find_by(sha: sha) image_name = image_for_commit(commit) image_path = Rails.root.join('public/ci', image_name) diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 7beb098659c..4ff268a6f06 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -8,10 +8,10 @@ module Ci builds = if current_runner.shared? # don't run projects which have not enables shared runners - builds.joins(commit: { gl_project: :gitlab_ci_project }).where(ci_projects: { shared_runners_enabled: true }) + builds.joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true }) else # do run projects which are only assigned to this runner - builds.joins(:commit).where(ci_commits: { gl_project_id: current_runner.gl_projects_ids }) + builds.where(project: current_runner.projects.where(builds_enabled: true)) end builds = builds.order('created_at ASC') @@ -20,10 +20,9 @@ module Ci build.can_be_served?(current_runner) end - if build # In case when 2 runners try to assign the same build, second runner will be declined - # with StateMachine::InvalidTransition in run! method. + # with StateMachines::InvalidTransition in run! method. build.with_lock do build.runner_id = current_runner.id build.save! @@ -33,7 +32,7 @@ module Ci build - rescue StateMachine::InvalidTransition + rescue StateMachines::InvalidTransition nil end end diff --git a/app/services/ci/test_hook_service.rb b/app/services/ci/test_hook_service.rb deleted file mode 100644 index 3a17596aaeb..00000000000 --- a/app/services/ci/test_hook_service.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Ci - class TestHookService - def execute(hook, current_user) - Ci::WebHookService.new.build_end(hook.project.commits.last.last_build) - end - end -end diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb new file mode 100644 index 00000000000..759c334ebe9 --- /dev/null +++ b/app/services/create_commit_builds_service.rb @@ -0,0 +1,28 @@ +class CreateCommitBuildsService + def execute(project, user, params) + return false unless project.builds_enabled? + + sha = params[:checkout_sha] || params[:after] + origin_ref = params[:ref] + + unless origin_ref && sha.present? + return false + end + + ref = Gitlab::Git.ref_name(origin_ref) + + # Skip branch removal + if sha == Gitlab::Git::BLANK_SHA + return false + end + + tag = Gitlab::Git.tag_ref?(origin_ref) + commit = project.ensure_ci_commit(sha) + unless commit.skip_ci? + commit.update_committed! + commit.create_builds(ref, tag, user) + end + + commit + end +end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index f11690aa3f4..d7ea30bc315 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -61,6 +61,7 @@ class GitPushService EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks) + CreateCommitBuildsService.new.execute(project, @user, @push_data) ProjectCacheWorker.perform_async(project.id) end diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 1cc42b0b0ad..4144c7111d0 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -10,6 +10,7 @@ class GitTagPushService EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) + CreateCommitBuildsService.new.execute(project, @user, @push_data) ProjectCacheWorker.perform_async(project.id) true diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 5da1c7afd92..0577ae778d5 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -7,6 +7,8 @@ module Projects description: @project.description, name: @project.name, path: @project.path, + shared_runners_enabled: @project.shared_runners_enabled, + builds_enabled: @project.builds_enabled, namespace_id: @params[:namespace].try(:id) || current_user.namespace.id } @@ -15,19 +17,6 @@ module Projects end new_project = CreateService.new(current_user, new_params).execute - - if new_project.persisted? - if @project.builds_enabled? - new_project.enable_ci - - settings = @project.gitlab_ci_project.attributes.select do |attr_name, value| - ["public", "shared_runners_enabled", "allow_git_fetch"].include? attr_name - end - - new_project.gitlab_ci_project.update(settings) - end - end - new_project end end diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml new file mode 100644 index 00000000000..6936e614346 --- /dev/null +++ b/app/views/admin/builds/_build.html.haml @@ -0,0 +1,73 @@ +- project = build.project +%tr.build + %td.status + = ci_status_with_icon(build.status) + + %td.build-link + - if build.target_url + = link_to build.target_url do + %strong Build ##{build.id} + - else + %strong Build ##{build.id} + + - if build.show_warning? + %i.fa.fa-warning.text-warning + + %td + - if project + = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project), class: "monospace" + + %td + = link_to build.short_sha, namespace_project_commit_path(project.namespace, project, build.sha), class: "monospace" + + %td + - if build.ref + = link_to build.ref, namespace_project_commits_path(project.namespace, project, build.ref) + - else + .light none + + %td + - if build.try(:runner) + = runner_link(build.runner) + - else + .light none + + %td + #{build.stage} / #{build.name} + + .pull-right + - if build.tags.any? + - build.tags.each do |tag| + %span.label.label-primary + = tag + - if build.try(:trigger_request) + %span.label.label-info triggered + - if build.try(:allow_failure) + %span.label.label-danger allowed to fail + + %td.duration + - if build.duration + #{duration_in_words(build.finished_at, build.started_at)} + + %td.timestamp + - if build.finished_at + %span #{time_ago_with_tooltip(build.finished_at)} + + - if defined?(coverage) && coverage + %td.coverage + - if build.try(:coverage) + #{build.coverage}% + + %td + .pull-right + - if current_user && can?(current_user, :download_build_artifacts, project) && build.download_url + = link_to build.download_url, title: 'Download artifacts' do + %i.fa.fa-download + - if current_user && can?(current_user, :manage_builds, build.project) + - if build.active? + - if build.cancel_url + = link_to build.cancel_url, method: :post, title: 'Cancel' do + %i.fa.fa-remove.cred + - elsif defined?(allow_retry) && allow_retry && build.retry_url + = link_to build.retry_url, method: :post, title: 'Retry' do + %i.fa.fa-repeat diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml new file mode 100644 index 00000000000..55da06a7fe9 --- /dev/null +++ b/app/views/admin/builds/index.html.haml @@ -0,0 +1,50 @@ +.project-issuable-filter + .controls + .pull-left.hidden-xs + - if @all_builds.running_or_pending.any? + = link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post + + %ul.center-top-menu + %li{class: ('active' if @scope.nil?)} + = link_to admin_builds_path do + Running + %span.badge.js-running-count= @all_builds.running_or_pending.count(:id) + + %li{class: ('active' if @scope == 'finished')} + = link_to admin_builds_path(scope: :finished) do + Finished + %span.badge.js-running-count= @all_builds.finished.count(:id) + + %li{class: ('active' if @scope == 'all')} + = link_to admin_builds_path(scope: :all) do + All + %span.badge.js-totalbuilds-count= @all_builds.count(:id) + +.gray-content-block + #{(@scope || 'running').capitalize} builds + +%ul.content-list + - if @builds.blank? + %li + .nothing-here-block No builds to show + - else + .table-holder + %table.table.builds + %thead + %tr + %th Status + %th Build ID + %th Project + %th Commit + %th Ref + %th Runner + %th Name + %th Duration + %th Finished at + %th + + - @builds.each do |build| + = render "admin/builds/build", build: build + + = paginate @builds, theme: 'gitlab' + diff --git a/app/views/ci/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index 701782d26bb..6745e58deca 100644 --- a/app/views/ci/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -8,14 +8,14 @@ %span.label.label-danger paused %td - = link_to ci_admin_runner_path(runner) do + = link_to admin_runner_path(runner) do = runner.short_sha %td .runner-description = runner.description %span (#{link_to 'edit', '#', class: 'edit-runner-link'}) .runner-description-form.hide - = form_for [:ci, :admin, runner], remote: true, html: { class: 'form-inline' } do |f| + = form_for [:admin, runner], remote: true, html: { class: 'form-inline' } do |f| .form-group = f.text_field :description, class: 'form-control' = f.submit 'Save', class: 'btn' @@ -38,11 +38,11 @@ Never %td .pull-right - = link_to 'Edit', ci_admin_runner_path(runner), class: 'btn btn-sm' + = link_to 'Edit', admin_runner_path(runner), class: 'btn btn-sm' - if runner.active? - = link_to 'Pause', [:pause, :ci, :admin, runner], data: { confirm: "Are you sure?" }, method: :get, class: 'btn btn-danger btn-sm' + = link_to 'Pause', [:pause, :admin, runner], data: { confirm: "Are you sure?" }, method: :get, class: 'btn btn-danger btn-sm' - else - = link_to 'Resume', [:resume, :ci, :admin, runner], method: :get, class: 'btn btn-success btn-sm' - = link_to 'Remove', [:ci, :admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + = link_to 'Resume', [:resume, :admin, runner], method: :get, class: 'btn btn-success btn-sm' + = link_to 'Remove', [:admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 0574ed38015..c5fb3c95506 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -39,7 +39,7 @@ .append-bottom-20.clearfix .pull-left - = form_tag ci_admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do + = form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do .form-group = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token', spellcheck: false = submit_tag 'Search', class: 'btn' @@ -63,5 +63,5 @@ %th - @runners.each do |runner| - = render "ci/admin/runners/runner", runner: runner + = render "admin/runners/runner", runner: runner = paginate @runners diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index fd3d33d657b..32051852dc0 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -22,7 +22,7 @@ %h4 This runner will process builds only from ASSIGNED projects %p You can't make this a shared runner. %hr -= form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| += form_for @runner, url: admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| .form-group = label_tag :token, class: 'control-label' do Token @@ -53,29 +53,24 @@ %th - @runner.runner_projects.each do |runner_project| - project = runner_project.project - - if project.gl_project + - if project %tr.alert-info %td %strong - = project.name + = project.name_with_namespace %td .pull-right - = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' + = link_to 'Disable', [:admin, project.namespace, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' %table.table %thead %tr %th Project %th - .pull-right - = link_to 'Assign to all', assign_all_ci_admin_runner_path(@runner), - class: 'btn btn-sm assign-all-runner', - title: 'Assign runner to all projects', - method: :put %tr %td - = form_tag ci_admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do + = form_tag admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do .form-group = search_field_tag :search, params[:search], class: 'form-control', spellcheck: false = submit_tag 'Search', class: 'btn' @@ -84,44 +79,44 @@ - @projects.each do |project| %tr %td - = project.name + = project.name_with_namespace %td .pull-right - = form_for [:ci, :admin, project, project.runner_projects.new] do |f| + = form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: @runner.id = f.submit 'Enable', class: 'btn btn-xs' = paginate @projects .col-md-6 %h4 Recent builds served by this runner - %table.builds.runner-builds + %table.table.builds.runner-builds %thead %tr - %th Build ID + %th Build %th Status %th Project %th Commit %th Finished at - @builds.each do |build| - - gl_project = build.gl_project + - project = build.project %tr.build %td.id - - if gl_project - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do - = build.id + - if project + = link_to namespace_project_build_path(project.namespace, project, build) do + %strong ##{build.id} - else - = build.id + %strong ##{build.id} %td.status = ci_status_with_icon(build.status) %td.status - - if gl_project - = gl_project.name_with_namespace + - if project + = project.name_with_namespace %td.build-link - - if gl_project + - if project = link_to ci_status_path(build.commit) do %strong #{build.commit.short_sha} diff --git a/app/views/ci/admin/runners/update.js.haml b/app/views/admin/runners/update.js.haml index 2b7d3067e20..2b7d3067e20 100644 --- a/app/views/ci/admin/runners/update.js.haml +++ b/app/views/admin/runners/update.js.haml diff --git a/app/views/ci/admin/application_settings/_form.html.haml b/app/views/ci/admin/application_settings/_form.html.haml deleted file mode 100644 index 634c9daa477..00000000000 --- a/app/views/ci/admin/application_settings/_form.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -= form_for @application_setting, url: ci_admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f| - - if @application_setting.errors.any? - #error_explanation - .alert.alert-danger - - @application_setting.errors.full_messages.each do |msg| - %p= msg - - %fieldset - %legend Default Project Settings - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :all_broken_builds do - = f.check_box :all_broken_builds - Send emails only on broken builds - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :add_pusher do - = f.check_box :add_pusher - Add pusher to recipients list - - .form-actions - = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/ci/admin/application_settings/show.html.haml b/app/views/ci/admin/application_settings/show.html.haml deleted file mode 100644 index 7ef0aa89ed6..00000000000 --- a/app/views/ci/admin/application_settings/show.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -%h3.page-title Settings -%hr -= render 'form' diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml deleted file mode 100644 index 2df58713214..00000000000 --- a/app/views/ci/admin/builds/_build.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -- gl_project = build.project.gl_project -- if build.commit && build.project - %tr.build - %td.build-link - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do - %strong #{build.id} - - %td.status - = ci_status_with_icon(build.status) - - %td.commit-link - = link_to ci_status_path(build.commit) do - %strong #{build.commit.short_sha} - - %td.runner - - if build.runner - = link_to build.runner.id, ci_admin_runner_path(build.runner) - - %td.build-project - = truncate build.project.name, length: 30 - - %td.build-message - %span= truncate(build.commit.git_commit_message, length: 30) - - %td.build-branch - %span= truncate(build.ref, length: 25) - - %td.duration - - if build.duration - #{duration_in_words(build.finished_at, build.started_at)} - - %td.timestamp - - if build.finished_at - %span #{time_ago_in_words build.finished_at} ago diff --git a/app/views/ci/admin/builds/index.html.haml b/app/views/ci/admin/builds/index.html.haml deleted file mode 100644 index d23119162cc..00000000000 --- a/app/views/ci/admin/builds/index.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%ul.nav.nav-tabs.append-bottom-20 - %li{class: ("active" if @scope.nil?)} - = link_to 'All builds', ci_admin_builds_path - - %li{class: ("active" if @scope == "pending")} - = link_to "Pending", ci_admin_builds_path(scope: :pending) - - %li{class: ("active" if @scope == "running")} - = link_to "Running", ci_admin_builds_path(scope: :running) - - -%table.builds - %thead - %tr - %th Build - %th Status - %th Commit - %th Runner - %th Project - %th Message - %th Branch - %th Duration - %th Finished at - - - @builds.each do |build| - = render "ci/admin/builds/build", build: build - -= paginate @builds diff --git a/app/views/ci/admin/events/index.html.haml b/app/views/ci/admin/events/index.html.haml deleted file mode 100644 index 5a5b4dc7c35..00000000000 --- a/app/views/ci/admin/events/index.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -.table-holder - %table.table - %thead - %tr - %th User ID - %th Description - %th When - - @events.each do |event| - %tr - %td - = event.user_id - %td - = event.description - %td.light - = time_ago_in_words event.updated_at - ago - -= paginate @events diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml deleted file mode 100644 index a342d6e1cf0..00000000000 --- a/app/views/ci/admin/projects/_project.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -- last_commit = project.commits.last -%tr - %td - = project.id - %td - = link_to [:ci, project] do - %strong= project.name - %td - - if last_commit - = ci_status_with_icon(last_commit.status) - - if project.last_commit_date - · - = time_ago_in_words project.last_commit_date - ago - - else - No builds yet - %td - - if project.public - %i.fa.fa-globe - Public - - else - %i.fa.fa-lock - Private - %td - = project.commits.count - %td - = link_to [:ci, :admin, project], method: :delete, class: 'btn btn-danger btn-sm' do - %i.fa.fa-remove - Remove diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml deleted file mode 100644 index 0da8547924b..00000000000 --- a/app/views/ci/admin/projects/index.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -.table-holder - %table.table - %thead - %tr - %th ID - %th Name - %th Last build - %th Access - %th Builds - %th - - - @projects.each do |project| - = render "ci/admin/projects/project", project: project - -= paginate @projects - diff --git a/app/views/ci/admin/runner_projects/index.html.haml b/app/views/ci/admin/runner_projects/index.html.haml deleted file mode 100644 index 6b4e3b2cb38..00000000000 --- a/app/views/ci/admin/runner_projects/index.html.haml +++ /dev/null @@ -1,57 +0,0 @@ -%p.lead - To register a new runner visit #{link_to 'this page ', ci_runners_path} - -.row - .col-md-8 - %h5 Activated: - %table.table - %tr - %th Runner ID - %th Runner Description - %th Last build - %th Builds Stats - %th Registered - %th - - - @runner_projects.each do |runner_project| - - runner = runner_project.runner - - builds = runner.builds.where(project_id: @project.id) - %tr - %td - %span.badge.badge-info= runner.id - %td - = runner.display_name - %td - - last_build = builds.last - - if last_build - = link_to last_build.short_sha, [last_build.project, last_build] - - else - unknown - %td - %span.badge.badge-success - #{builds.success.count} - %span / - %span.badge.badge-important - #{builds.failed.count} - %td - #{time_ago_in_words(runner_project.created_at)} ago - %td - = link_to 'Disable', [:ci, @project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm right' - .col-md-4 - %h5 Available - %table.table - %tr - %th ID - %th Token - %th - - - (Ci::Runner.all - @project.runners).each do |runner| - %tr - %td - = runner.id - %td - = runner.token - %td - = form_for [:ci, @project, @runner_project] do |f| - = f.hidden_field :runner_id, value: runner.id - = f.submit 'Add', class: 'btn btn-sm' diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml index b24a3b826cf..11163813f3e 100644 --- a/app/views/ci/commits/_commit.html.haml +++ b/app/views/ci/commits/_commit.html.haml @@ -27,7 +27,6 @@ - if commit.finished_at %span #{time_ago_in_words commit.finished_at} ago - - if commit.project.coverage_enabled? + - if commit.coverage %td.coverage - - if commit.coverage - #{commit.coverage}% + #{commit.coverage}% diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml index db2d7f2f4b6..09e7e653521 100644 --- a/app/views/ci/shared/_guide.html.haml +++ b/app/views/ci/shared/_guide.html.haml @@ -4,12 +4,10 @@ %ol %li Add at least one runner to the project. - Go to #{link_to 'Runners page', runners_path(@project.gl_project), target: :blank} for instructions. + Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions. %li - Put the .gitlab-ci.yml in the root of your repository. Examples can be found in #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. + Put the .gitlab-ci.yml in the root of your repository. Examples can be found in + #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} %li - Visit #{link_to 'GitLab project settings', @project.gitlab_url + "/services/gitlab_ci/edit", target: :blank} - and press the "Test settings" button. - %li Return to this page and refresh it, it should show a new build. diff --git a/app/views/ci/user_sessions/new.html.haml b/app/views/ci/user_sessions/new.html.haml deleted file mode 100644 index b8d9a1d7089..00000000000 --- a/app/views/ci/user_sessions/new.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.login-block - %h2 Login using GitLab account - %p.light - Make sure you have an account on the GitLab server - = link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink - %hr - = link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' ) diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml deleted file mode 100644 index dcda04a4638..00000000000 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -%ul.nav.nav-sidebar - = nav_link do - = link_to admin_root_path, title: 'Back to admin', data: {placement: 'right'}, class: 'back-link' do - = icon('caret-square-o-left fw') - %span - Back to admin - - %li.separate-item - = nav_link path: 'projects#index' do - = link_to ci_admin_projects_path do - = icon('list-alt fw') - %span - Projects - = nav_link path: 'events#index' do - = link_to ci_admin_events_path do - = icon('book fw') - %span - Events - = nav_link path: ['runners#index', 'runners#show'] do - = link_to ci_admin_runners_path do - = icon('cog fw') - %span - Runners - %span.count= Ci::Runner.count(:all) - = nav_link path: 'builds#index' do - = link_to ci_admin_builds_path do - = icon('link fw') - %span - Builds - %span.count= Ci::Build.count(:all) - = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do - = link_to ci_admin_application_settings_path do - = icon('cogs fw') - %span - Settings diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml deleted file mode 100644 index f094edbfa87..00000000000 --- a/app/views/layouts/ci/_nav_project.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%ul.nav.nav-sidebar - = nav_link do - = link_to project_path(@project.gl_project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do - = icon('caret-square-o-left fw') - %span - Back to project - %li.separate-item - = nav_link path: 'events#index' do - = link_to ci_project_events_path(@project) do - = icon('book fw') - %span - Events diff --git a/app/views/layouts/ci/admin.html.haml b/app/views/layouts/ci/admin.html.haml deleted file mode 100644 index c8cb185d28c..00000000000 --- a/app/views/layouts/ci/admin.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/head' - %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title = "Admin area" - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - - = render 'layouts/ci/page', sidebar: 'nav_admin' diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml deleted file mode 100644 index 38023468d0b..00000000000 --- a/app/views/layouts/ci/application.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/head' - %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title = "Continuous Integration" - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - - = render 'layouts/ci/page' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index d04a3d1f227..c60ac5eefac 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -24,11 +24,18 @@ = icon('key fw') %span Deploy Keys - = nav_link do - = link_to ci_admin_projects_path, title: 'Continuous Integration' do - = icon('building fw') + = nav_link path: ['runners#index', 'runners#show'] do + = link_to admin_runners_path do + = icon('cog fw') + %span + Runners + %span.count= Ci::Runner.count(:all) + = nav_link path: 'builds#index' do + = link_to admin_builds_path do + = icon('link fw') %span - Continuous Integration + Builds + %span.count= Ci::Build.count(:all) = nav_link(controller: :logs) do = link_to admin_logs_path, title: 'Logs' do = icon('file-text fw') diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 2fcba7bd672..c0d62028639 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -44,7 +44,7 @@ = icon('cubes fw') %span Builds - %span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all) + %span.count.builds_counter= @project.builds.running_or_pending.count(:all) - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index f0b3f27b626..970da78a5c9 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -50,18 +50,3 @@ = icon('retweet fw') %span Triggers - = nav_link path: 'ci_web_hooks#index' do - = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project), title: 'CI Web Hooks' do - = icon('link fw') - %span - CI Web Hooks - = nav_link path: 'ci_settings#edit' do - = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do - = icon('building fw') - %span - CI Settings - = nav_link controller: 'ci_services' do - = link_to namespace_project_ci_services_path(@project.namespace, @project), title: 'CI Services' do - = icon('share fw') - %span - CI Services diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml index 00cb4aa24cc..27112c6745a 100644 --- a/app/views/notify/_note_message.html.haml +++ b/app/views/notify/_note_message.html.haml @@ -1,2 +1,4 @@ %div + "#{link_to @note.author_name, user_url(@note.author)} wrote:" +%div = markdown(@note.note, pipeline: :email) diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml index de6291aa914..f4e9749e5c7 100644 --- a/app/views/ci/notify/build_fail_email.html.haml +++ b/app/views/notify/build_fail_email.html.haml @@ -1,13 +1,13 @@ - content_for :header do %h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab CI (build failed) + GitLab (build failed) %h3 Project: = link_to ci_project_url(@project) do = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p @@ -20,4 +20,4 @@ Message: #{@build.commit.git_commit_message} %p - Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/notify/build_fail_email.text.erb index 17a3b9b1d33..675acea60a1 100644 --- a/app/views/ci/notify/build_fail_email.text.erb +++ b/app/views/notify/build_fail_email.text.erb @@ -8,4 +8,4 @@ Stage: <%= @build.stage %> Job: <%= @build.name %> Message: <%= @build.commit.git_commit_message %> -Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> +Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %> diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml index 6ef1fd67d89..8b004d34cca 100644 --- a/app/views/ci/notify/build_success_email.html.haml +++ b/app/views/notify/build_success_email.html.haml @@ -1,6 +1,6 @@ - content_for :header do %h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab CI (build successful) + GitLab (build successful) %h3 Project: @@ -8,7 +8,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p @@ -21,4 +21,4 @@ Message: #{@build.commit.git_commit_message} %p - Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/notify/build_success_email.text.erb index bc8b978c3d7..747da44acae 100644 --- a/app/views/ci/notify/build_success_email.text.erb +++ b/app/views/notify/build_success_email.text.erb @@ -8,4 +8,4 @@ Stage: <%= @build.stage %> Job: <%= @build.name %> Message: <%= @build.commit.git_commit_message %> -Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> +Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %> diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index 12f83aae04b..4361f67a74d 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -1,30 +1,32 @@ -%h3 #{@author.name} #{@action_name} #{@ref_type} #{@ref_name} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)} +%h3 + #{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} + at #{link_to(@message.project_name_with_namespace, namespace_project_url(@message.project_namespace, @message.project))} -- if @compare - - if @reverse_compare +- if @message.compare + - if @message.reverse_compare? %p %strong WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below. %h4 - = @reverse_compare ? "Deleted commits:" : "Commits:" + = @message.reverse_compare? ? "Deleted commits:" : "Commits:" %ul - - @commits.each do |commit| + - @message.commits.each do |commit| %li - %strong #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)} + %strong #{link_to(commit.short_id, namespace_project_commit_url(@message.project_namespace, @message.project, commit))} %div %span by #{commit.author_name} %i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} %pre.commit-message = commit.safe_message - %h4 #{pluralize @diffs.count, "changed file"}: + %h4 #{pluralize @message.diffs_count, "changed file"}: %ul - - @diffs.each_with_index do |diff, i| + - @message.diffs.each_with_index do |diff, i| %li.file-stats - %a{href: "#{@target_url if @disable_diffs}#diff-#{i}" } + %a{href: "#{@message.target_url if @message.disable_diffs?}#diff-#{i}" } - if diff.deleted_file %span.deleted-file − @@ -40,11 +42,11 @@ - else = diff.new_path - - unless @disable_diffs + - unless @message.disable_diffs? %h4 Changes: - - @diffs.each_with_index do |diff, i| + - @message.diffs.each_with_index do |diff, i| %li{id: "diff-#{i}"} - %a{href: @target_url + "#diff-#{i}"} + %a{href: @message.target_url + "#diff-#{i}"} - if diff.deleted_file %strong = diff.old_path @@ -62,5 +64,5 @@ = color_email_diff(diff.diff) %br - - if @compare.timeout + - if @message.compare_timeout %h5 Huge diff. To prevent performance issues changes are hidden diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml index 97a176ed2a3..aa0e263b6df 100644 --- a/app/views/notify/repository_push_email.text.haml +++ b/app/views/notify/repository_push_email.text.haml @@ -1,21 +1,21 @@ -#{@author.name} #{@action_name} #{@ref_type} #{@ref_name} at #{@project.name_with_namespace} -- if @compare +#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} at #{@message.project_name_with_namespace} +- if @message.compare \ \ - - if @reverse_compare + - if @message.reverse_compare? WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below. \ \ - = @reverse_compare ? "Deleted commits:" : "Commits:" - - @commits.each do |commit| + = @message.reverse_compare? ? "Deleted commits:" : "Commits:" + - @message.commits.each do |commit| #{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} #{commit.safe_message} \- - - - - \ \ - #{pluralize @diffs.count, "changed file"}: + #{pluralize @message.diffs_count, "changed file"}: \ - - @diffs.each do |diff| + - @message.diffs.each do |diff| - if diff.deleted_file \- − #{diff.old_path} - elsif diff.renamed_file @@ -24,11 +24,11 @@ \- + #{diff.new_path} - else \- #{diff.new_path} - - unless @disable_diffs + - unless @message.disable_diffs? \ \ Changes: - - @diffs.each do |diff| + - @message.diffs.each do |diff| \ \===================================== - if diff.deleted_file @@ -39,11 +39,11 @@ = diff.new_path \===================================== != diff.diff - - if @compare.timeout + - if @message.compare_timeout \ \ Huge diff. To prevent performance issues it was hidden - - if @target_url + - if @message.target_url \ \ - View it on GitLab: #{@target_url} + View it on GitLab: #{@message.target_url} diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index fbf2c293db8..1a26908ab11 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -3,7 +3,7 @@ .project-issuable-filter .controls - - if @ci_project && can?(current_user, :manage_builds, @project) + - if can?(current_user, :manage_builds, @project) .pull-left.hidden-xs - if @all_builds.running_or_pending.any? = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post @@ -40,7 +40,7 @@ %thead %tr %th Status - %th Build ID + %th Runner %th Commit %th Ref %th Stage diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index d5e81f84b56..20a5b6a66e7 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -56,7 +56,7 @@ %br Go to - = link_to namespace_project_runners_path(@build.gl_project.namespace, @build.gl_project) do + = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do Runners page .row.prepend-top-default @@ -113,7 +113,7 @@ %p %span.attr-name Runner: - if @build.runner && current_user && current_user.admin - = link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id) + = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id) - elsif @build.runner \##{@build.runner.id} diff --git a/app/views/projects/ci_services/_form.html.haml b/app/views/projects/ci_services/_form.html.haml deleted file mode 100644 index 397832e56db..00000000000 --- a/app/views/projects/ci_services/_form.html.haml +++ /dev/null @@ -1,54 +0,0 @@ -%h3.page-title - = @service.title - = boolean_to_icon @service.activated? - -%p= @service.description - - -%hr - -= form_for(@service, as: :service, url: namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f| - - if @service.errors.any? - .alert.alert-danger - %ul - - @service.errors.full_messages.each do |msg| - %li= msg - - - if @service.help.present? - .bs-callout - = @service.help - - .form-group - = f.label :active, "Active", class: "control-label" - .col-sm-10 - = f.check_box :active - - - @service.fields.each do |field| - - name = field[:name] - - label = field[:label] || name - - value = @service.send(name) - - type = field[:type] - - placeholder = field[:placeholder] - - choices = field[:choices] - - default_choice = field[:default_choice] - - help = field[:help] - - .form-group - = f.label label, class: "control-label" - .col-sm-10 - - if type == 'text' - = f.text_field name, class: "form-control", placeholder: placeholder - - elsif type == 'textarea' - = f.text_area name, rows: 5, class: "form-control", placeholder: placeholder - - elsif type == 'checkbox' - = f.check_box name - - elsif type == 'select' - = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } - - if help - .light #{help} - - .form-actions - = f.submit 'Save', class: 'btn btn-save' - - - if @service.valid? && @service.activated? && @service.can_test? - = link_to 'Test settings', test_namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), class: 'btn' diff --git a/app/views/projects/ci_services/edit.html.haml b/app/views/projects/ci_services/edit.html.haml deleted file mode 100644 index dacb6b4f6f4..00000000000 --- a/app/views/projects/ci_services/edit.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -- page_title @service.title, "CI Services" -= render 'form' diff --git a/app/views/projects/ci_services/index.html.haml b/app/views/projects/ci_services/index.html.haml deleted file mode 100644 index 3f26c7851d8..00000000000 --- a/app/views/projects/ci_services/index.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- page_title "CI Services" -%h3.page-title Project services -%p.light Project services allow you to integrate GitLab CI with other applications - -%table.table - %thead - %tr - %th - %th Service - %th Description - %th Last edit - - @services.sort_by(&:title).each do |service| - %tr - %td - = boolean_to_icon service.activated? - %td - = link_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) do - %strong= service.title - %td - = service.description - %td.light - = time_ago_in_words service.updated_at - ago diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml deleted file mode 100644 index ee6b8885e2d..00000000000 --- a/app/views/projects/ci_settings/_form.html.haml +++ /dev/null @@ -1,120 +0,0 @@ -%h3.page-title - CI settings -%hr -.bs-callout.help-callout - %p - If you want to test your .gitlab-ci.yml, you can use special tool - #{link_to "Lint", ci_lint_path} - %p - Edit your - #{link_to ".gitlab-ci.yml using web-editor", yaml_web_editor_link(@ci_project)} - -- unless @project.empty_repo? - %p - Paste build status image for #{@repository.root_ref} with next link - = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do - Status Badge - .badge-codes-block.bs-callout.bs-callout-info.hide - %p - Status badge for - %span.label.label-info #{@ref} - branch - %div - %label Markdown: - = text_field_tag 'badge_md', markdown_badge_code(@ci_project, @repository.root_ref), readonly: true, class: 'form-control' - %label Html: - = text_field_tag 'badge_html', html_badge_code(@ci_project, @repository.root_ref), readonly: true, class: 'form-control' - -= nested_form_for @ci_project, url: namespace_project_ci_settings_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| - - if @ci_project.errors.any? - #error_explanation - %p.lead= "#{pluralize(@ci_project.errors.count, "error")} prohibited this project from being saved:" - .alert.alert-error - %ul - - @ci_project.errors.full_messages.each do |msg| - %li= msg - - %fieldset - %legend Build settings - .form-group - = label_tag nil, class: 'control-label' do - Get code - .col-sm-10 - %p Get recent application code using the following command: - .radio - = label_tag do - = f.radio_button :allow_git_fetch, 'false' - %strong git clone - .light Slower but makes sure you have a clean dir before every build - .radio - = label_tag do - = f.radio_button :allow_git_fetch, 'true' - %strong git fetch - .light Faster - .form-group - = f.label :timeout_in_minutes, 'Timeout', class: 'control-label' - .col-sm-10 - = f.number_field :timeout_in_minutes, class: 'form-control', min: '0' - .light per build in minutes - - - %fieldset - %legend Build Schedule - .form-group - = f.label :always_build, 'Schedule build', class: 'control-label' - .col-sm-10 - .checkbox - = f.label :always_build do - = f.check_box :always_build - %span.light Repeat last build after X hours if no builds - .form-group - = f.label :polling_interval, "Build interval", class: 'control-label' - .col-sm-10 - = f.number_field :polling_interval, placeholder: '5', min: '0', class: 'form-control' - .light In hours - - %fieldset - %legend Project settings - .form-group - = f.label :default_ref, "Make tabs for the following branches", class: 'control-label' - .col-sm-10 - = f.text_field :default_ref, class: 'form-control', placeholder: 'master, stable' - .light You will be able to filter builds by the following branches - .form-group - = f.label :public, 'Public mode', class: 'control-label' - .col-sm-10 - .checkbox - = f.label :public do - = f.check_box :public - %span.light Anyone can see project and builds - .form-group - = f.label :coverage_regex, "Test coverage parsing", class: 'control-label' - .col-sm-10 - .input-group - %span.input-group-addon / - = f.text_field :coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' - %span.input-group-addon / - .light We will use this regular expression to find test coverage output in build trace. Leave blank if you want to disable this feature - .bs-callout.bs-callout-info - %p Below are examples of regex for existing tools: - %ul - %li - Simplecov (Ruby) - - %code \(\d+.\d+\%\) covered - %li - pytest-cov (Python) - - %code \d+\%\s*$ - %li - phpunit --coverage-text --colors=never (PHP) - - %code ^\s*Lines:\s*\d+.\d+\% - - %fieldset - %legend Advanced settings - .form-group - = f.label :token, "CI token", class: 'control-label' - .col-sm-10 - = f.text_field :token, class: 'form-control', placeholder: 'xEeFCaDAB89' - - .form-actions - = f.submit 'Save changes', class: 'btn btn-save' - - unless @ci_project.new_record? - = link_to 'Remove Project', ci_project_path(@ci_project), method: :delete, data: { confirm: 'Project will be removed. Are you sure?' }, class: 'btn btn-danger pull-right' diff --git a/app/views/projects/ci_settings/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml deleted file mode 100644 index 1374e6680f9..00000000000 --- a/app/views/projects/ci_settings/_no_runners.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -.alert.alert-danger - %p - There are NO runners to build this project. - %br - You can add Specific runner for this project on Runners page - - - if current_user.admin - or add Shared runner for whole application in admin area. diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml deleted file mode 100644 index acc912d4596..00000000000 --- a/app/views/projects/ci_settings/edit.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -- page_title "CI Settings" - -- if no_runners_for_project?(@ci_project) - = render 'no_runners' - -= render 'form' diff --git a/app/views/projects/ci_web_hooks/index.html.haml b/app/views/projects/ci_web_hooks/index.html.haml deleted file mode 100644 index 2998fb08ff1..00000000000 --- a/app/views/projects/ci_web_hooks/index.html.haml +++ /dev/null @@ -1,94 +0,0 @@ -- page_title "CI Web Hooks" -%h3.page-title - CI Web hooks - -%p.light - Web Hooks can be used for binding events when build completed. - -%hr.clearfix - -= form_for @web_hook, url: namespace_project_ci_web_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| - -if @web_hook.errors.any? - .alert.alert-danger - - @web_hook.errors.full_messages.each do |msg| - %p= msg - .form-group - = f.label :url, "URL", class: 'control-label' - .col-sm-10 - = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' - .form-actions - = f.submit "Add Web Hook", class: "btn btn-create" - --if @web_hooks.any? - %h4 Activated web hooks (#{@web_hooks.count}) - .table-holder - %table.table - - @web_hooks.each do |hook| - %tr - %td - .clearfix - %span.monospace= hook.url - %td - .pull-right - - if @ci_project.commits.any? - = link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped" - = link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" - -%h4 Web Hook data example - -:erb - <pre> - <code> - { - "build_id": 2, - "build_name":"rspec_linux" - "build_status": "failed", - "build_started_at": "2014-05-05T18:01:02.563Z", - "build_finished_at": "2014-05-05T18:01:07.611Z", - "project_id": 1, - "project_name": "Brightbox \/ Brightbox Cli", - "gitlab_url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli", - "ref": "master", - "sha": "a26cf5de9ed9827746d4970872376b10d9325f40", - "before_sha": "34f57f6ba3ed0c21c5e361bbb041c3591411176c", - "push_data": { - "before": "34f57f6ba3ed0c21c5e361bbb041c3591411176c", - "after": "a26cf5de9ed9827746d4970872376b10d9325f40", - "ref": "refs\/heads\/master", - "user_id": 1, - "user_name": "Administrator", - "project_id": 5, - "repository": { - "name": "Brightbox Cli", - "url": "dzaporozhets@localhost:brightbox\/brightbox-cli.git", - "description": "Voluptatibus quae error consectetur voluptas dolores vel excepturi possimus.", - "homepage": "http:\/\/localhost:3000\/brightbox\/brightbox-cli" - }, - "commits": [ - { - "id": "a26cf5de9ed9827746d4970872376b10d9325f40", - "message": "Release v1.2.2", - "timestamp": "2014-04-22T16:46:42+03:00", - "url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/a26cf5de9ed9827746d4970872376b10d9325f40", - "author": { - "name": "Paul Thornthwaite", - "email": "tokengeek@gmail.com" - } - }, - { - "id": "34f57f6ba3ed0c21c5e361bbb041c3591411176c", - "message": "Fix server user data update\n\nIncorrect condition was being used so Base64 encoding option was having\nopposite effect from desired.", - "timestamp": "2014-04-11T18:17:26+03:00", - "url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/34f57f6ba3ed0c21c5e361bbb041c3591411176c", - "author": { - "name": "Paul Thornthwaite", - "email": "tokengeek@gmail.com" - } - } - ], - "total_commits_count": 2, - "ci_yaml_file":"rspec_linux:\r\n script: ls\r\n" - } - } - </code> - </pre> diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml index e4d81182c1a..329aaa0bb8b 100644 --- a/app/views/projects/commit/_builds.html.haml +++ b/app/views/projects/commit/_builds.html.haml @@ -1,17 +1,17 @@ .gray-content-block.middle-block .pull-right - - if @ci_project && can?(current_user, :manage_builds, @ci_commit.gl_project) + - if can?(current_user, :manage_builds, @ci_commit.project) - if @ci_commit.builds.latest.failed.any?(&:retryable?) - = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post + = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post - if @ci_commit.builds.running_or_pending.any? - = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post + = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post .oneline = pluralize @statuses.count(:id), "build" - if defined?(link_to_commit) && link_to_commit for commit - = link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: "monospace" + = link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: "monospace" - if @ci_commit.duration > 0 in = time_interval_in_words @ci_commit.duration @@ -22,8 +22,9 @@ %ul - @ci_commit.yaml_errors.split(",").each do |error| %li= error + You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} -- if @ci_commit.gl_project.builds_enabled? && !@ci_commit.ci_yaml_file +- if @ci_commit.project.builds_enabled? && !@ci_commit.ci_yaml_file .bs-callout.bs-callout-warning \.gitlab-ci.yml not found in this commit @@ -38,12 +39,12 @@ %th Name %th Duration %th Finished at - - if @ci_project && @ci_project.coverage_enabled? + - if @ci_commit.project.build_coverage_enabled? %th Coverage %th - @ci_commit.refs.each do |ref| = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, - locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true } + locals: { coverage: @ci_commit.project.build_coverage_enabled?, stage: true, allow_retry: true } - if @ci_commit.retried.any? .gray-content-block.second-block @@ -60,8 +61,8 @@ %th Name %th Duration %th Finished at - - if @ci_project && @ci_project.coverage_enabled? + - if @ci_commit.project.build_coverage_enabled? %th Coverage %th = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, - locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true } + locals: { coverage: @ci_commit.project.build_coverage_enabled?, stage: true } diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index a527bb2f84a..45a00e4d259 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -69,7 +69,7 @@ - if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url = link_to commit_status.download_url, title: 'Download artifacts' do %i.fa.fa-download - - if current_user && can?(current_user, :manage_builds, commit_status.gl_project) + - if current_user && can?(current_user, :manage_builds, commit_status.project) - if commit_status.active? - if commit_status.cancel_url = link_to commit_status.cancel_url, method: :post, title: 'Cancel' do diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index d865d299135..650629ef1b9 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -112,6 +112,62 @@ %hr = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + %fieldset.features + %legend + Continuous Integration + .form-group + .col-sm-offset-2.col-sm-10 + %p Get recent application code using the following command: + .radio + = f.label :build_allow_git_fetch do + = f.radio_button :build_allow_git_fetch, 'false' + %strong git clone + %br + %span.descr Slower but makes sure you have a clean dir before every build + .radio + = f.label :build_allow_git_fetch do + = f.radio_button :build_allow_git_fetch, 'true' + %strong git fetch + %br + %span.descr Faster + .form-group + = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label' + .col-sm-10 + = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' + %p.help-block per build in minutes + .form-group + = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label' + .col-sm-10 + .input-group + %span.input-group-addon / + = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' + %span.input-group-addon / + %p.help-block + We will use this regular expression to find test coverage output in build trace. + Leave blank if you want to disable this feature + .bs-callout.bs-callout-info + %p Below are examples of regex for existing tools: + %ul + %li + Simplecov (Ruby) - + %code \(\d+.\d+\%\) covered + %li + pytest-cov (Python) - + %code \d+\%\s*$ + %li + phpunit --coverage-text --colors=never (PHP) - + %code ^\s*Lines:\s*\d+.\d+\% + + + %fieldset.features + %legend + Advanced settings + .form-group + = f.label :runners_token, "CI token", class: 'control-label' + .col-sm-10 + = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' + %p.help-block The secure token used to checkout project. + .form-actions = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml index cf4285a2671..4b12e5f2da1 100644 --- a/app/views/projects/graphs/ci/_overall.haml +++ b/app/views/projects/graphs/ci/_overall.haml @@ -1,20 +1,19 @@ -- ci_project = @project.gitlab_ci_project %h4 Overall stats %ul %li Total: - %strong= pluralize ci_project.builds.count(:all), 'build' + %strong= pluralize @project.builds.count(:all), 'build' %li Successful: - %strong= pluralize ci_project.builds.success.count(:all), 'build' + %strong= pluralize @project.builds.success.count(:all), 'build' %li Failed: - %strong= pluralize ci_project.builds.failed.count(:all), 'build' + %strong= pluralize @project.builds.failed.count(:all), 'build' %li Success ratio: %strong - #{success_ratio(ci_project.builds.success, ci_project.builds.failed)}% + #{success_ratio(@project.builds.success, @project.builds.failed)}% %li Commits covered: %strong - = ci_project.commits.count(:all) + = @project.ci_commits.count(:all) diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 3702aeaecba..b18d9197d0b 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -55,6 +55,13 @@ %strong Merge Request events %p.light This url will be triggered when a merge request is created + %div + = f.check_box :build_events, class: 'pull-left' + .prepend-left-20 + = f.label :build_events, class: 'list-label' do + %strong Build events + %p.light + This url will be triggered when the build status changes .form-group = f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox' .col-sm-10 @@ -78,7 +85,7 @@ .clearfix %span.monospace= hook.url %p - - %w(push_events tag_push_events issues_events note_events merge_requests_events).each do |trigger| + - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index f2011542ca7..405bae1bbb9 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -5,24 +5,8 @@ - else = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-grouped btn-close js-note-target-close', title: 'Close Issue' -= render 'shared/show_aside' - .gray-content-block.second-block.oneline-block - .row - .col-md-9 - .votes-holder.pull-right - #votes= render 'votes/votes_block', votable: @issue - = render "shared/issuable/participants" - .col-md-3 - .input-group.cross-project-reference - %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} - = cross_project_reference(@project, @issue) - = clipboard_button(clipboard_target: 'span#cross-project-reference') + = render 'votes/votes_block', votable: @issue -.row - %section.col-md-9 - .voting_notes#notes= render 'projects/notes/notes_with_form' - %aside.col-md-3 - .issuable-affix - .context - = render 'shared/issuable/context', issuable: @issue +#notes + = render 'projects/notes/notes_with_form' diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml new file mode 100644 index 00000000000..fe856ac991e --- /dev/null +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -0,0 +1,25 @@ +-if @merge_requests.any? + %h2.merge-requests-title + = pluralize(@merge_requests.count, 'Related Merge Request') + %ul.bordered-list + - has_any_ci = @merge_requests.any?(&:ci_commit) + - @merge_requests.each do |merge_request| + %li + %span.merge-request-ci-status + - if merge_request.ci_commit + = render_ci_status(merge_request.ci_commit) + - elsif has_any_ci + = icon('blank fw') + %span.merge-request-id + \##{merge_request.iid} + %span.merge-request-info + %strong + = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" + in + - project = merge_request.target_project + = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) + %span.merge-request-status.prepend-left-10 + - if merge_request.merged? + MERGED + - elsif merge_request.closed? + CLOSED diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index a78d20cc07e..cc2cf8c8716 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -3,13 +3,13 @@ .issue .issue-details.issuable-details - .page-title + .issuable-title .issue-box{ class: issue_box_class(@issue) } - if @issue.closed? Closed - else Open - %span.issue-id Issue ##{@issue.iid} + %span.issuable-id Issue ##{@issue.iid} %span.creator · opened by #{link_to_member(@project, @issue.author, size: 24)} @@ -36,18 +36,29 @@ = icon('pencil-square-o') Edit - .gray-content-block.middle-block - %h2.issue-title - = markdown escape_once(@issue.title), pipeline: :single_line - %div - - if @issue.description.present? - .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} - .wiki - = preserve do - = markdown(@issue.description, cache_key: [@issue, "description"]) - %textarea.hidden.js-task-list-field - = @issue.description - - if @closed_by_merge_requests.present? - = render 'projects/issues/closed_by_box' - .issue-discussion - = render 'projects/issues/discussion' + .row + %section.col-md-9 + .gray-content-block + %h2.issue-title + = markdown escape_once(@issue.title), pipeline: :single_line + %div + - if @issue.description.present? + .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} + .wiki + = preserve do + = markdown(@issue.description, cache_key: [@issue, "description"]) + %textarea.hidden.js-task-list-field + = @issue.description + + .merge-requests + = render 'merge_requests' + + - if @closed_by_merge_requests.present? + = render 'projects/issues/closed_by_box' + .issue-discussion + = render 'projects/issues/discussion' + + %aside.col-md-3 + = render 'shared/issuable/sidebar', issuable: @issue + + = render 'shared/show_aside' diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml index b7735aaf3c1..2f0f3fcfb06 100644 --- a/app/views/projects/issues/update.js.haml +++ b/app/views/projects/issues/update.js.haml @@ -1,3 +1,3 @@ -$('.context').html("#{escape_javascript(render 'shared/issuable/context', issuable: @issue)}"); -$('.context').effect('highlight') +$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}"); +$('.issuable-sidebar').parent().effect('highlight') new Issue(); diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index d64b19ae91a..7a7428d35cc 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -5,24 +5,7 @@ - if @merge_request.closed? = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" -= render 'shared/show_aside' +.gray-content-block.second-block.oneline-block + = render 'votes/votes_block', votable: @merge_request -.gray-content-block.middle-block.oneline-block - .row - .col-md-9 - .votes-holder.pull-right - #votes= render 'votes/votes_block', votable: @merge_request - = render "shared/issuable/participants" - .col-md-3 - .input-group.cross-project-reference - %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} - = cross_project_reference(@project, @merge_request) - = clipboard_button(clipboard_target: 'span#cross-project-reference') - -.row - %section.col-md-9 - .voting_notes#notes= render "projects/notes/notes_with_form" - %aside.col-md-3 - .issuable-affix - .context - = render 'shared/issuable/context', issuable: @merge_request +#notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 960d1561e73..04f8fd74422 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -7,71 +7,79 @@ .merge-request{'data-url' => merge_request_path(@merge_request)} .merge-request-details.issuable-details = render "projects/merge_requests/show/mr_title" - = render "projects/merge_requests/show/mr_box" - .append-bottom-default.mr-source-target.prepend-top-default - - if @merge_request.open? - .pull-right - - if @merge_request.source_branch_exists? - = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do - = icon('cloud-download fw') - Check out branch + .row + %section.col-md-9 + = render "projects/merge_requests/show/mr_box" + .append-bottom-default.mr-source-target.prepend-top-default + - if @merge_request.open? + .pull-right + - if @merge_request.source_branch_exists? + = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do + = icon('cloud-download fw') + Check out branch - %span.dropdown - %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} } - = icon('download') - Download as - %span.caret - %ul.dropdown-menu - %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) - %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) - .normal - %span Request to merge - %span.label-branch= source_branch_with_namespace(@merge_request) - %span into - = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do - = @merge_request.target_branch + %span.dropdown + %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} } + = icon('download') + Download as + %span.caret + %ul.dropdown-menu + %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) + %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) + .normal + %span Request to merge + %span.label-branch= source_branch_with_namespace(@merge_request) + %span into + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch - = render "projects/merge_requests/show/how_to_merge" - = render "projects/merge_requests/widget/show.html.haml" + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/widget/show.html.haml" - - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) - .light.prepend-top-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" + - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) + .light.prepend-top-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" - - if @commits.present? - %ul.merge-request-tabs.center-top-menu.no-top.no-bottom - %li.notes-tab - = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do - Discussion - %span.badge= @merge_request.mr_and_commit_notes.user.count - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do - Commits - %span.badge= @commits.size - %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do - Changes - %span.badge= @merge_request.diffs.size - - if @ci_commit - %li.builds-tab - = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do - Builds - %span.badge= @statuses.size + - if @commits.present? + %ul.merge-request-tabs.center-top-menu.no-top.no-bottom + %li.notes-tab + = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do + Discussion + %span.badge= @merge_request.mr_and_commit_notes.user.count + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do + Commits + %span.badge= @commits.size + %li.diffs-tab + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do + Changes + %span.badge= @merge_request.diffs.size + - if @ci_commit + %li.builds-tab + = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do + Builds + %span.badge= @statuses.size - .tab-content - #notes.notes.tab-pane.voting_notes - = render "projects/merge_requests/discussion" - #commits.commits.tab-pane - - # This tab is always loaded via AJAX - #diffs.diffs.tab-pane - - # This tab is always loaded via AJAX - #builds.builds.tab-pane - - # This tab is always loaded via AJAX + .tab-content + #notes.notes.tab-pane.voting_notes + = render "projects/merge_requests/discussion" + #commits.commits.tab-pane + - # This tab is always loaded via AJAX + #diffs.diffs.tab-pane + - # This tab is always loaded via AJAX + #builds.builds.tab-pane + - # This tab is always loaded via AJAX + + .mr-loading-status + = spinner + + %aside.col-md-3 + = render 'shared/issuable/sidebar', issuable: @merge_request + + = render 'shared/show_aside' - .mr-loading-status - = spinner :javascript var merge_request; diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 4dfe46e2b86..d65c3b16618 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,7 +1,7 @@ -.page-title +.issuable-title .issue-box{ class: issue_box_class(@merge_request) } = @merge_request.state_human_name - %span.issue-id Merge Request ##{@merge_request.iid} + %span.issuable-id Merge Request ##{@merge_request.iid} %span.creator · opened by #{link_to_member(@project, @merge_request.author, size: 24)} diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml index 25583b2cc6f..93db65ddf79 100644 --- a/app/views/projects/merge_requests/update.js.haml +++ b/app/views/projects/merge_requests/update.js.haml @@ -1,3 +1,3 @@ -$('.context').html("#{escape_javascript(render 'shared/issuable/context', issuable: @merge_request)}"); -$('.context').effect('highlight') +$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}"); +$('.issuable-sidebar').parent().effect('highlight') merge_request = new MergeRequest(); diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index 8c2b5366a06..6f52c963a53 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -33,7 +33,7 @@ .remove_source_branch_in_progress.hide %p = icon('spinner spin') - Removing source branch '#{@merge_request.source_branch}'. Please wait. This page will be automatically reload. + Removing source branch '#{@merge_request.source_branch}'. Please wait, this page will be automatically reloaded. :javascript $('.remove_source_branch').on('click', function() { diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index e6b8a2e6fe7..4d95afc28bb 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -15,10 +15,10 @@ - if runner.belongs_to_one_project? = link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - else - - runner_project = @ci_project.runner_projects.find_by(runner_id: runner) - = link_to 'Disable for this project', [:ci, @ci_project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + - runner_project = @project.runner_projects.find_by(runner_id: runner) + = link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - elsif runner.specific? - = form_for [:ci, @ci_project, @ci_project.runner_projects.new] do |f| + = form_for [@project.namespace, @project, @project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: runner.id = f.submit 'Enable for this project', class: 'btn btn-sm' .pull-right diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index 316ea747b14..6a37f444bb7 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -3,17 +3,17 @@ .bs-callout.bs-callout-warning GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. %hr - - if @ci_project.shared_runners_enabled - = link_to toggle_shared_runners_ci_project_path(@ci_project), class: 'btn btn-warning', method: :post do + - if @project.shared_runners_enabled? + = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do Disable shared runners - else - = link_to toggle_shared_runners_ci_project_path(@ci_project), class: 'btn btn-success', method: :post do + = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-success', method: :post do Enable shared runners for this project - if @shared_runners_count.zero? - This application has no shared runners yet. - Please use specific runners or ask administrator to create one + This GitLab server does not provide any shared runners yet. + Please use specific runners or ask the administrator to create one. - else %h4.underlined-title Available shared runners - #{@shared_runners_count} %ul.bordered-list.available-shared-runners diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index c13625c7e49..30cd1263a12 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -12,7 +12,7 @@ %code #{ci_root_url(only_path: false)} %li Use the following registration token during setup: - %code #{@ci_project.token} + %code #{@project.runners_token} %li Start runner! diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index b3ad79a200e..bd346c4b8e6 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -36,7 +36,8 @@ :plain curl -X POST \ -F token=TOKEN \ - #{ci_build_trigger_url(@ci_project.id, 'REF_NAME')} + -F ref=REF_NAME \ + #{builds_trigger_url(@project.id)} %h3 Use .gitlab-ci.yml @@ -51,7 +52,7 @@ trigger: type: deploy script: - - "curl -X POST -F token=TOKEN #{ci_build_trigger_url(@ci_project.id, 'REF_NAME')}" + - "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}" %h3 Pass build variables @@ -65,5 +66,6 @@ :plain curl -X POST \ -F token=TOKEN \ + -F "ref=REF_NAME" \ -F "variables[RUN_NIGHTLY_BUILD]=true" \ - #{ci_build_trigger_url(@ci_project.id, 'REF_NAME')} + #{builds_trigger_url(@project.id)} diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml index e052da1ac43..e80dffc1ced 100644 --- a/app/views/projects/variables/show.html.haml +++ b/app/views/projects/variables/show.html.haml @@ -10,13 +10,13 @@ %hr -= nested_form_for @ci_project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| += nested_form_for @project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| - if @project.errors.any? #error_explanation - %p.lead= "#{pluralize(@ci_project.errors.count, "error")} prohibited this project from being saved:" + %p.lead= "#{pluralize(@project.errors.count, "error")} prohibited this project from being saved:" .alert.alert-error %ul - - @ci_project.errors.full_messages.each do |msg| + - @project.errors.full_messages.each do |msg| %li= msg = f.fields_for :variables do |variable_form| diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index 16a98a7233c..28d6f421fea 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -59,6 +59,15 @@ %strong Merge Request events %p.light This url will be triggered when a merge request is created + - if @service.supported_events.include?("build") + %div + = form.check_box :build_events, class: 'pull-left' + .prepend-left-20 + = form.label :build_events, class: 'list-label' do + %strong Build events + %p.light + This url will be triggered when a build status changes + - @service.fields.each do |field| - type = field[:type] diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml deleted file mode 100644 index 2aa46662613..00000000000 --- a/app/views/shared/issuable/_context.html.haml +++ /dev/null @@ -1,57 +0,0 @@ -= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| - %div.prepend-top-default - .issuable-context-title - %label - Assignee: - - if issuable.assignee - %strong= link_to_member(@project, issuable.assignee, size: 24) - - else - none - .issuable-context-selectbox - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) - - %div.prepend-top-default.clearfix - .issuable-context-title - %label - Milestone: - - if issuable.milestone - %span.back-to-milestone - = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do - %strong - = icon('clock-o') - = issuable.milestone.title - - else - none - .issuable-context-selectbox - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) - = hidden_field_tag :issuable_context - = f.submit class: 'btn hide' - - - if issuable.labels.any? - %div.prepend-top-default.clearfix - .issuable-context-title - %label Labels - .issuable-show-labels - - issuable.labels.each do |label| - = link_to_label(label) - - - if current_user - - subscribed = issuable.subscribed?(current_user) - %div.prepend-top-default.clearfix - .issuable-context-title - %label Subscription - - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - .subscription-status{data: {status: subscribtion_status}} - .description-block.unsubscribed{class: ( 'hidden' if subscribed )} - You're not receiving notifications from this thread. - .description-block.subscribed{class: ( 'hidden' unless subscribed )} - You're receiving notifications because you're subscribed to this thread. - %button.btn.btn-block.subscribe-button{:type => 'button'} - = icon('eye') - %span= subscribed ? 'Unsubscribe' : 'Subscribe' - -:javascript - new Subscription("#{toggle_subscription_path(issuable)}"); - new IssuableContext(); diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index b4e0def48b6..da6bacbb74a 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -1,5 +1,5 @@ -.participants - %span - = pluralize @participants.count, "participant" - - @participants.each do |participant| +.block.participants + .title + = pluralize participants.count, "participant" + - participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml new file mode 100644 index 00000000000..0019f739b89 --- /dev/null +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -0,0 +1,83 @@ +.issuable-sidebar.issuable-affix + = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| + .block + .title + Cross-project reference + .cross-project-reference + %span#cross-project-reference + = cross_project_reference(@project, issuable) + = clipboard_button(clipboard_target: 'span#cross-project-reference') + + .block.assignee + .title + %label + Assignee + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .pull-right + = link_to 'Edit', '#', class: 'edit-link' + .value + - if issuable.assignee + %strong= link_to_member(@project, issuable.assignee, size: 24) + - else + .light None + + .selectbox + = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) + + .block.milestone + .title + %label + Milestone + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .pull-right + = link_to 'Edit', '#', class: 'edit-link' + .value + - if issuable.milestone + %span.back-to-milestone + = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do + %strong + = icon('clock-o') + = issuable.milestone.title + - else + .light None + .selectbox + = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) + = hidden_field_tag :issuable_context + = f.submit class: 'btn hide' + + - if issuable.project.labels.any? + .block + .title + %label Labels + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .pull-right + = link_to 'Edit', '#', class: 'edit-link' + .value.issuable-show-labels + - if issuable.labels.any? + - issuable.labels.each do |label| + = link_to_label(label) + - else + .light None + .selectbox + = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, + { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } + + = render "shared/issuable/participants", participants: issuable.participants(current_user) + + - if current_user + - subscribed = issuable.subscribed?(current_user) + .block.light + .title + %label.light Notifications + - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' + %button.btn.btn-block.btn-gray.subscribe-button{:type => 'button'} + %span= subscribed ? 'Unsubscribe' : 'Subscribe' + .subscription-status{data: {status: subscribtion_status}} + .unsubscribed{class: ( 'hidden' if subscribed )} + You're not receiving notifications from this thread. + .subscribed{class: ( 'hidden' unless subscribed )} + You're receiving notifications because you're subscribed to this thread. + + :javascript + new Subscription("#{toggle_subscription_path(issuable)}"); + new IssuableContext(); diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 7eb27c12d33..6071f1484c6 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -19,7 +19,8 @@ post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" noteable_type = "#{votable.class.name.underscore}" noteable_id = "#{votable.id}" - window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + aliases = #{AwardEmoji::ALIASES.to_json} + window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id, aliases) $(".awards-menu li").click (e)-> emoji = $(this).data("emoji") diff --git a/app/workers/build_email_worker.rb b/app/workers/build_email_worker.rb new file mode 100644 index 00000000000..1c7a04a66a8 --- /dev/null +++ b/app/workers/build_email_worker.rb @@ -0,0 +1,19 @@ +class BuildEmailWorker + include Sidekiq::Worker + + def perform(build_id, recipients, push_data) + recipients.each do |recipient| + begin + case push_data['build_status'] + when 'success' + Notify.build_success_email(build_id, recipient).deliver_now + when 'failed' + Notify.build_fail_email(build_id, recipient).deliver_now + end + # These are input errors and won't be corrected even if Sidekiq retries + rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e + logger.info("Failed to send e-mail for project '#{push_data['project_name']}' to #{recipient}: #{e}") + end + end + end +end diff --git a/app/workers/ci/hip_chat_notifier_worker.rb b/app/workers/ci/hip_chat_notifier_worker.rb deleted file mode 100644 index ebb43570e2a..00000000000 --- a/app/workers/ci/hip_chat_notifier_worker.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Ci - class HipChatNotifierWorker - include Sidekiq::Worker - - def perform(message, options={}) - room = options.delete('room') - token = options.delete('token') - server = options.delete('server') - name = options.delete('service_name') - client_opts = { - api_version: 'v2', - server_url: server - } - - client = HipChat::Client.new(token, client_opts) - client[room].send(name, message, options.symbolize_keys) - end - end -end diff --git a/app/workers/ci/slack_notifier_worker.rb b/app/workers/ci/slack_notifier_worker.rb deleted file mode 100644 index 3bbb9b4bec7..00000000000 --- a/app/workers/ci/slack_notifier_worker.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Ci - class SlackNotifierWorker - include Sidekiq::Worker - - def perform(webhook_url, message, options={}) - notifier = Slack::Notifier.new(webhook_url) - notifier.ping(message, options) - end - end -end diff --git a/app/workers/ci/web_hook_worker.rb b/app/workers/ci/web_hook_worker.rb deleted file mode 100644 index 0bb83845572..00000000000 --- a/app/workers/ci/web_hook_worker.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Ci - class WebHookWorker - include Sidekiq::Worker - - def perform(hook_id, data) - Ci::WebHook.find(hook_id).execute data - end - end -end diff --git a/config/routes.rb b/config/routes.rb index 54d6afe2dca..e2d4fcb65a8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,43 +24,10 @@ Rails.application.routes.draw do resource :lint, only: [:show, :create] resources :projects do - collection do - post :add - get :disabled - end - member do get :status, to: 'projects#badge' get :integration - post :toggle_shared_runners end - - resources :runner_projects, only: [:create, :destroy] - end - - resource :user_sessions do - get :auth - get :callback - end - - namespace :admin do - resources :runners, only: [:index, :show, :update, :destroy] do - member do - put :assign_all - get :resume - get :pause - end - end - - resources :events, only: [:index] - - resources :projects do - resources :runner_projects - end - - resources :builds, only: :index - - resource :application_settings, only: [:show, :update] end root to: 'projects#index' @@ -271,6 +238,8 @@ Rails.application.routes.draw do member do put :transfer end + + resources :runner_projects end end @@ -281,6 +250,19 @@ Rails.application.routes.draw do resources :labels + resources :runners, only: [:index, :show, :update, :destroy] do + member do + get :resume + get :pause + end + end + + resources :builds, only: :index do + collection do + post :cancel_all + end + end + root to: 'dashboard#index' end @@ -596,18 +578,6 @@ Rails.application.routes.draw do resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :destroy] - resource :ci_settings, only: [:edit, :update, :destroy] - resources :ci_web_hooks, only: [:index, :create, :destroy] do - member do - get :test - end - end - - resources :ci_services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do - member do - get :test - end - end resources :builds, only: [:index, :show] do collection do @@ -686,7 +656,13 @@ Rails.application.routes.draw do get :resume get :pause end + + collection do + post :toggle_shared_runners + end end + + resources :runner_projects, only: [:create, :destroy] end end end diff --git a/db/migrate/20151203162134_add_build_events_to_services.rb b/db/migrate/20151203162134_add_build_events_to_services.rb new file mode 100644 index 00000000000..a84be7db3f1 --- /dev/null +++ b/db/migrate/20151203162134_add_build_events_to_services.rb @@ -0,0 +1,6 @@ +class AddBuildEventsToServices < ActiveRecord::Migration + def up + add_column :services, :build_events, :boolean, default: false, null: false + add_column :web_hooks, :build_events, :boolean, default: false, null: false + end +end diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb new file mode 100644 index 00000000000..825ba1973ff --- /dev/null +++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb @@ -0,0 +1,13 @@ +class MigrateCiWebHooks < ActiveRecord::Migration + include Gitlab::Database + + def up + execute( + 'INSERT INTO web_hooks (url, project_id, type, created_at, updated_at, push_events, issues_events, merge_requests_events, tag_push_events, note_events, build_events) ' \ + "SELECT ci_web_hooks.url, projects.id, 'ProjectHook', ci_web_hooks.created_at, ci_web_hooks.updated_at, " \ + "#{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value} FROM ci_web_hooks " \ + 'JOIN ci_projects ON ci_web_hooks.project_id = ci_projects.id ' \ + 'JOIN projects ON ci_projects.gitlab_id = projects.id' + ) + end +end diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb new file mode 100644 index 00000000000..202fac8e3fc --- /dev/null +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -0,0 +1,41 @@ +class MigrateCiEmails < ActiveRecord::Migration + include Gitlab::Database + + def up + # This inserts a new service: BuildsEmailService + # It "manually" constructs the properties (JSON-encoded) + # Migrating all ci_projects e-mail related columns + execute( + 'INSERT INTO services (project_id, type, created_at, updated_at, active, push_events, issues_events, merge_requests_events, tag_push_events, note_events, build_events, properties) ' \ + "SELECT projects.id, 'BuildsEmailService', ci_services.created_at, ci_services.updated_at, " \ + "#{true_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value}, " \ + "CONCAT('{\"notify_only_broken_builds\":\"', #{convert_bool('ci_projects.email_only_broken_builds')}, " \ + "'\",\"add_pusher\":\"', #{convert_bool('ci_projects.email_add_pusher')}, " \ + "'\",\"recipients\":\"', #{escape_text('ci_projects.email_recipients')}, " \ + "'\"}') " \ + 'FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id = ci_projects.id ' \ + 'JOIN projects ON ci_projects.gitlab_id = projects.id ' \ + "WHERE ci_services.type = 'Ci::MailService' AND ci_services.active" + ) + end + + def down + end + + # This function escapes double-quotes and slash + def escape_text(name) + "REPLACE(REPLACE(#{name}, '\\', '\\\\'), '\"', '\\\"')" + end + + # This function returns 0 or 1 for column + def convert_bool(name) + if Gitlab::Database.postgresql? + # PostgreSQL uses BOOLEAN type + "CASE WHEN #{name} IS TRUE THEN '1' ELSE '0' END" + else + # MySQL uses TINYINT + "#{name}" + end + end +end diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb new file mode 100644 index 00000000000..f14efa3e95d --- /dev/null +++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb @@ -0,0 +1,33 @@ +class MigrateCiSlackService < ActiveRecord::Migration + include Gitlab::Database + + def up + properties_query = 'SELECT properties FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::SlackService' AND ci_services.active " \ + 'LIMIT 1' + + active_query = 'SELECT 1 FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::SlackService' AND ci_services.active " \ + 'LIMIT 1' + + # We update the service since services are always generated for project, even if they are inactive + # Activate service and migrate properties if currently the service is not active + execute( + "UPDATE services SET properties=(#{properties_query}), active=#{true_value}, " \ + "push_events=#{false_value}, issues_events=#{false_value}, merge_requests_events=#{false_value}, " \ + "tag_push_events=#{false_value}, note_events=#{false_value}, build_events=#{true_value} " \ + "WHERE NOT services.active AND services.type='SlackService' AND (#{active_query}) IS NOT NULL" + ) + + # Tick only build_events if the service is already active + execute( + "UPDATE services SET build_events=#{true_value} " \ + "WHERE services.active AND services.type='SlackService' AND (#{active_query}) IS NOT NULL" + ) + end + + def down + end +end diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb new file mode 100644 index 00000000000..b9e04323576 --- /dev/null +++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb @@ -0,0 +1,34 @@ +class MigrateCiHipChatService < ActiveRecord::Migration + include Gitlab::Database + + def up + # From properties strip `hipchat_` key + properties_query = "SELECT REPLACE(properties, '\"hipchat_', '\"') FROM ci_services " \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::HipChatService' AND ci_services.active " \ + 'LIMIT 1' + + active_query = 'SELECT 1 FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::HipChatService' AND ci_services.active " \ + 'LIMIT 1' + + # We update the service since services are always generated for project, even if they are inactive + # Activate service and migrate properties if currently the service is not active + execute( + "UPDATE services SET properties=(#{properties_query}), active=#{true_value}, " \ + "push_events=#{false_value}, issues_events=#{false_value}, merge_requests_events=#{false_value}, " \ + "tag_push_events=#{false_value}, note_events=#{false_value}, build_events=#{true_value} " \ + "WHERE NOT services.active AND services.type='HipchatService' AND (#{active_query}) IS NOT NULL" + ) + + # Tick only build_events if the service is already active + execute( + "UPDATE services SET build_events=#{true_value} " \ + "WHERE services.active AND services.type='HipchatService' AND (#{active_query}) IS NOT NULL" + ) + end + + def down + end +end diff --git a/db/migrate/20151210125928_add_ci_to_project.rb b/db/migrate/20151210125928_add_ci_to_project.rb new file mode 100644 index 00000000000..8a65abab636 --- /dev/null +++ b/db/migrate/20151210125928_add_ci_to_project.rb @@ -0,0 +1,11 @@ +class AddCiToProject < ActiveRecord::Migration + def up + add_column :projects, :ci_id, :integer + add_column :projects, :builds_enabled, :boolean, default: true, null: false + add_column :projects, :shared_runners_enabled, :boolean, default: true, null: false + add_column :projects, :runners_token, :string + add_column :projects, :build_coverage_regex, :string + add_column :projects, :build_allow_git_fetch, :boolean, default: true, null: false + add_column :projects, :build_timeout, :integer, default: 3600, null: false + end +end diff --git a/db/migrate/20151210125929_add_project_id_to_ci.rb b/db/migrate/20151210125929_add_project_id_to_ci.rb new file mode 100644 index 00000000000..5d1cf543576 --- /dev/null +++ b/db/migrate/20151210125929_add_project_id_to_ci.rb @@ -0,0 +1,8 @@ +class AddProjectIdToCi < ActiveRecord::Migration + def up + add_column :ci_builds, :gl_project_id, :integer + add_column :ci_runner_projects, :gl_project_id, :integer + add_column :ci_triggers, :gl_project_id, :integer + add_column :ci_variables, :gl_project_id, :integer + end +end diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb new file mode 100644 index 00000000000..d17b2a425f8 --- /dev/null +++ b/db/migrate/20151210125930_migrate_ci_to_project.rb @@ -0,0 +1,37 @@ +class MigrateCiToProject < ActiveRecord::Migration + def up + migrate_project_id_for_table('ci_runner_projects') + migrate_project_id_for_table('ci_triggers') + migrate_project_id_for_table('ci_variables') + migrate_project_id_for_builds + + migrate_project_column('id', 'ci_id') + migrate_project_column('shared_runners_enabled', 'shared_runners_enabled') + migrate_project_column('token', 'runners_token') + migrate_project_column('coverage_regex', 'build_coverage_regex') + migrate_project_column('allow_git_fetch', 'build_allow_git_fetch') + migrate_project_column('timeout', 'build_timeout') + migrate_ci_service + end + + def migrate_project_id_for_table(table) + subquery = "SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = #{table}.project_id" + execute("UPDATE #{table} SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") + end + + def migrate_project_id_for_builds + subquery = 'SELECT gl_project_id FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id' + execute("UPDATE ci_builds SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") + end + + def migrate_project_column(column, new_column = nil) + new_column ||= column + subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" + execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE #{new_column} IS NULL AND (#{subquery}) IS NOT NULL") + end + + def migrate_ci_service + subquery = "SELECT active FROM services WHERE projects.id = services.project_id AND type='GitlabCiService'" + execute("UPDATE projects SET builds_enabled=(#{subquery}) WHERE builds_enabled IS NULL AND (#{subquery}) IS NOT NULL") + end +end diff --git a/db/migrate/20151210125931_add_index_to_ci_tables.rb b/db/migrate/20151210125931_add_index_to_ci_tables.rb new file mode 100644 index 00000000000..9fedb5d612c --- /dev/null +++ b/db/migrate/20151210125931_add_index_to_ci_tables.rb @@ -0,0 +1,12 @@ +class AddIndexToCiTables < ActiveRecord::Migration + def up + add_index :ci_builds, :gl_project_id + add_index :ci_runner_projects, :gl_project_id + add_index :ci_triggers, :gl_project_id + add_index :ci_variables, :gl_project_id + add_index :projects, :runners_token + add_index :projects, :builds_enabled + add_index :projects, [:builds_enabled, :shared_runners_enabled] + add_index :projects, [:ci_id] + end +end diff --git a/db/migrate/20151210125932_drop_null_for_ci_tables.rb b/db/migrate/20151210125932_drop_null_for_ci_tables.rb new file mode 100644 index 00000000000..0b007430b0c --- /dev/null +++ b/db/migrate/20151210125932_drop_null_for_ci_tables.rb @@ -0,0 +1,9 @@ +class DropNullForCiTables < ActiveRecord::Migration + def up + remove_index :ci_variables, :project_id + remove_index :ci_runner_projects, :project_id + change_column_null :ci_triggers, :project_id, true + change_column_null :ci_variables, :project_id, true + change_column_null :ci_runner_projects, :project_id, true + end +end diff --git a/db/schema.rb b/db/schema.rb index a3b649f3fd0..0167e30ff8b 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: 20151210072243) do +ActiveRecord::Schema.define(version: 20151210125932) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 20151210072243) do t.string "target_url" t.string "description" t.text "artifacts_file" + t.integer "gl_project_id" end add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree @@ -118,6 +119,7 @@ ActiveRecord::Schema.define(version: 20151210072243) do add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree + add_index "ci_builds", ["gl_project_id"], name: "index_ci_builds_on_gl_project_id", using: :btree add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree @@ -202,13 +204,14 @@ ActiveRecord::Schema.define(version: 20151210072243) do add_index "ci_projects", ["shared_runners_enabled"], name: "index_ci_projects_on_shared_runners_enabled", using: :btree create_table "ci_runner_projects", force: :cascade do |t| - t.integer "runner_id", null: false - t.integer "project_id", null: false + t.integer "runner_id", null: false + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end - add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree + add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| @@ -278,24 +281,27 @@ ActiveRecord::Schema.define(version: 20151210072243) do create_table "ci_triggers", force: :cascade do |t| t.string "token" - t.integer "project_id", null: false + t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree + add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree create_table "ci_variables", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id" t.string "key" t.text "value" t.text "encrypted_value" t.string "encrypted_value_salt" t.string "encrypted_value_iv" + t.integer "gl_project_id" end - add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree + add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "ci_web_hooks", force: :cascade do |t| t.string "url", null: false @@ -650,13 +656,24 @@ ActiveRecord::Schema.define(version: 20151210072243) do t.string "import_source" t.integer "commit_count", default: 0 t.text "import_error" - end - + t.integer "ci_id" + t.boolean "builds_enabled", default: true, null: false + t.boolean "shared_runners_enabled", default: true, null: false + t.string "runners_token" + t.string "build_coverage_regex" + t.boolean "build_allow_git_fetch", default: true, null: false + t.integer "build_timeout", default: 3600, null: false + end + + add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree + add_index "projects", ["builds_enabled"], name: "index_projects_on_builds_enabled", using: :btree + add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree + add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree @@ -707,6 +724,7 @@ ActiveRecord::Schema.define(version: 20151210072243) do t.boolean "merge_requests_events", default: true t.boolean "tag_push_events", default: true t.boolean "note_events", default: true, null: false + t.boolean "build_events", default: false, null: false end add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree @@ -855,6 +873,7 @@ ActiveRecord::Schema.define(version: 20151210072243) do t.boolean "tag_push_events", default: false t.boolean "note_events", default: false, null: false t.boolean "enable_ssl_verification", default: true + t.boolean "build_events", default: false, null: false end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree diff --git a/doc/api/projects.md b/doc/api/projects.md index 43a50a9a810..1a524400627 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -32,7 +32,6 @@ Parameters: - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - `search` (optional) - Return list of authorized projects according to a search criteria -- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first ```json [ @@ -137,7 +136,6 @@ Parameters: - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - `search` (optional) - Return list of authorized projects according to a search criteria -- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first ### List ALL projects @@ -153,7 +151,6 @@ Parameters: - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - `search` (optional) - Return list of authorized projects according to a search criteria -- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first ### Get single project diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3dbf1afc7a9..7e2edb945da 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -113,6 +113,9 @@ The YAML-defined variables are also set to all created service containers, thus ### cache `cache` is used to specify list of files and directories which should be cached between builds. +Caches are stored according to the branch/ref and the job name. Caches are not +currently shared between different job names or between branches/refs. This means +caching will benefit you if you push subsequent commits to an existing feature branch. **The global setting allows to specify default cached files for all jobs.** diff --git a/doc/install/installation.md b/doc/install/installation.md index 618391e16d2..0a19a060a9a 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -62,7 +62,7 @@ up-to-date and install it. Install the required packages (needed to compile Ruby and native extensions to Ruby gems): - sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake nodejs + sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake nodejs If you want to use Kerberos for user authentication, then install libkrb5-dev: @@ -174,7 +174,23 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ## 6. Redis - sudo apt-get install redis-server +As of this writing, most Debian/Ubuntu distributions ship with Redis 2.2 or +2.4. GitLab requires at least Redis 2.8. If your platform doesn't provide +this, the following instructions cover building and installing Redis from +scratch. + +Ubuntu users [can also use a PPA](https://launchpad.net/~chris-lea/+archive/ubuntu/redis-server) +to install a recent version of Redis. + + # Build Redis + wget http://download.redis.io/releases/redis-2.8.23.tar.gz + tar xzf redis-2.8.23.tar.gz + cd redis-2.8.23 + make + + # Install Redis + cd utils + sudo ./install_server.sh # Configure redis to use sockets sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig @@ -197,7 +213,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da fi # Activate the changes to redis.conf - sudo service redis-server restart + sudo service redis_6379 start # Add git to the redis group sudo usermod -aG redis git diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 1b8c28dd0f4..1632e42f701 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -38,7 +38,8 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - } + }, + "label" => "Company Login" # optional label for SAML login button, defaults to "Saml" } ] ``` @@ -79,4 +80,4 @@ On the sign in page there should now be a SAML button below the regular sign in If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page, this likely indicates that GitLab could not get the email address for the SAML user. -Make sure the IdP provides a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username. +Make sure the IdP provides a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username.
\ No newline at end of file diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index b08a79ca0aa..46dfa2232b4 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -142,7 +142,7 @@ git diff origin/8-1-stable:lib/support/nginx/gitlab origin/8-2-stable:lib/suppor If you are using Apache instead of NGINX please see the updated [Apache templates]. Also note that because Apache does not support upstreams behind Unix sockets you -will need to let gitlab-git-http-server listen on a TCP port. You can do this +will need to let gitlab-workhorse listen on a TCP port. You can do this via [/etc/default/gitlab]. [Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 7490ad400b5..aa9078b878f 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -95,6 +95,16 @@ Feature: Project Merge Requests And I should see a diff comment saying "Typo, please fix" @javascript + Scenario: I delete a comment on a merge request diff + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I click on the Changes tab + And I leave a comment like "Line is wrong" on diff + And I delete the comment "Line is wrong" on diff + And I click on the Discussion tab + Then I should not see any discussion + + @javascript Scenario: I comment on a line of a commit in merge request Given project "Shop" have "Bug NS-05" open merge request with diffs inside And I visit merge request page "Bug NS-05" diff --git a/features/project/service.feature b/features/project/service.feature index 5014b52b9f6..ff3e7a0b38e 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -7,12 +7,6 @@ Feature: Project Services When I visit project "Shop" services page Then I should see list of available services - Scenario: Activate gitlab-ci service - When I visit project "Shop" services page - And I click gitlab-ci service link - And I fill gitlab-ci settings - Then I should see service settings saved - Scenario: Activate hipchat service When I visit project "Shop" services page And I click hipchat service link diff --git a/features/steps/admin/settings.rb b/features/steps/admin/settings.rb index 6acbf46eb20..037f7494a77 100644 --- a/features/steps/admin/settings.rb +++ b/features/steps/admin/settings.rb @@ -32,6 +32,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps page.check('Comments') page.check('Issues events') page.check('Merge Request events') + page.check('Build events') click_on 'Save' end @@ -39,6 +40,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps fill_in 'Webhook', with: 'http://localhost' fill_in 'Username', with: 'test_user' fill_in 'Channel', with: '#test_channel' + page.check('Notify only broken builds') end step 'I should see service template settings saved' do diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index 0d6a9a8fc66..101eb408ba1 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -104,7 +104,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps step 'commit has ci status' do @project.enable_ci - ci_commit = create :ci_commit, gl_project: @project, sha: sample_commit.id + ci_commit = create :ci_commit, project: @project, sha: sample_commit.id create :ci_build, commit: ci_commit end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 13d9d180dd1..0d340d97ff9 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -211,6 +211,25 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end + step 'I delete the comment "Line is wrong" on diff' do + page.within('.diff-file:nth-of-type(5) .note') do + find('.js-note-delete').click + end + end + + step 'I click on the Discussion tab' do + page.within '.merge-request-tabs' do + click_link 'Discussion' + end + + # Waits for load + expect(page).to have_css('.tab-content #notes.active') + end + + step 'I should not see any discussion' do + expect(page).not_to have_css('.notes .discussion') + end + step 'I should see a discussion has started on diff' do page.within(".notes .discussion") do page.should have_content "#{current_user.name} started a discussion" @@ -386,7 +405,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps step '"Bug NS-05" has CI status' do project = merge_request.source_project project.enable_ci - ci_commit = create :ci_commit, gl_project: project, sha: merge_request.last_commit.id + ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id create :ci_build, commit: ci_commit end diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 1c700df0c63..ed3957ca873 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -11,7 +11,6 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(page).to have_content 'Project services' expect(page).to have_content 'Campfire' expect(page).to have_content 'HipChat' - expect(page).to have_content 'GitLab CI' expect(page).to have_content 'Assembla' expect(page).to have_content 'Pushover' expect(page).to have_content 'Atlassian Bamboo' @@ -20,15 +19,6 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(page).to have_content 'Irker (IRC gateway)' end - step 'I click gitlab-ci service link' do - click_link 'GitLab CI' - end - - step 'I fill gitlab-ci settings' do - check 'Active' - click_button 'Save' - end - step 'I should see service settings saved' do expect(find_field('Active').value).to eq '1' end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 7021fac5fe4..da643bf3ba9 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -204,7 +204,7 @@ module SharedProject step 'project "Shop" has CI build' do project = Project.find_by(name: "Shop") - create :ci_commit, gl_project: project, sha: project.commit.sha + create :ci_commit, project: project, sha: project.commit.sha end step 'I should see last commit with CI status' do diff --git a/lib/api/api.rb b/lib/api/api.rb index fe1bf8a4816..7834262d612 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -53,5 +53,6 @@ module API mount Settings mount Keys mount Tags + mount Triggers end end diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 2c0596c9dfb..1162271f5fc 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -53,7 +53,7 @@ module API name = params[:name] || params[:context] status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref]) - status ||= GenericCommitStatus.new(commit: ci_commit, user: current_user) + status ||= GenericCommitStatus.new(project: @project, commit: ci_commit, user: current_user) status.update(attrs) case params[:state].to_s diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 81bf7a8222b..b1cd80bdf65 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -45,7 +45,8 @@ module API class ProjectHook < Hook expose :project_id, :push_events - expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification + expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events + expose :enable_ssl_verification end class ForkedFromProject < Grape::Entity @@ -63,6 +64,7 @@ module API expose :name, :name_with_namespace expose :path, :path_with_namespace expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at + expose :shared_runners_enabled expose :creator_id expose :namespace expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } @@ -252,7 +254,7 @@ module API class ProjectService < Grape::Entity expose :id, :title, :created_at, :updated_at, :active - expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events + expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events # Expose serialized properties expose :properties do |service, options| field_names = service.fields. @@ -359,5 +361,9 @@ module API end end end + + class TriggerRequest < Grape::Entity + expose :id, :variables + end end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 92540ccf2b1..a4df810e755 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -266,12 +266,7 @@ module API projects = projects.search(params[:search]) end - if params[:ci_enabled_first].present? - projects.includes(:gitlab_ci_service). - reorder("services.active DESC, projects.#{project_order_by} #{project_sort}") - else - projects.reorder(project_order_by => project_sort) - end + projects.reorder(project_order_by => project_sort) end def project_order_by diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index 882d1a083ad..cf9938d25a7 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -45,6 +45,7 @@ module API :merge_requests_events, :tag_push_events, :note_events, + :build_events, :enable_ssl_verification ] @hook = user_project.hooks.new(attrs) @@ -77,6 +78,7 @@ module API :merge_requests_events, :tag_push_events, :note_events, + :build_events, :enable_ssl_verification ] diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6928fe0eb9d..bdf4b77596e 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -82,6 +82,7 @@ module API # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) + # shared_runners_enabled (optional) # namespace_id (optional) - defaults to user namespace # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) - 0 by default @@ -98,6 +99,7 @@ module API :builds_enabled, :wiki_enabled, :snippets_enabled, + :shared_runners_enabled, :namespace_id, :public, :visibility_level, @@ -126,6 +128,7 @@ module API # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) + # shared_runners_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) # import_url (optional) @@ -142,6 +145,7 @@ module API :builds_enabled, :wiki_enabled, :snippets_enabled, + :shared_runners_enabled, :public, :visibility_level, :import_url] @@ -183,6 +187,7 @@ module API # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) + # shared_runners_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) - visibility level of a project # Example Request @@ -197,6 +202,7 @@ module API :builds_enabled, :wiki_enabled, :snippets_enabled, + :shared_runners_enabled, :public, :visibility_level] attrs = map_public_to_visibility_level(attrs) diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb new file mode 100644 index 00000000000..2781f1cf191 --- /dev/null +++ b/lib/api/triggers.rb @@ -0,0 +1,48 @@ +module API + # Triggers API + class Triggers < Grape::API + resource :projects do + # Trigger a GitLab project build + # + # Parameters: + # id (required) - The ID of a CI project + # ref (required) - The name of project's branch or tag + # token (required) - The uniq token of trigger + # variables (optional) - The list of variables to be injected into build + # Example Request: + # POST /projects/:id/trigger/builds + post ":id/trigger/builds" do + required_attributes! [:ref, :token] + + project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id]) + trigger = Ci::Trigger.find_by_token(params[:token].to_s) + not_found! unless project && trigger + unauthorized! unless trigger.project == project + + # validate variables + variables = params[:variables] + if variables + unless variables.is_a?(Hash) + render_api_error!('variables needs to be a hash', 400) + end + + unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } + render_api_error!('variables needs to be a map of key-valued strings', 400) + end + + # convert variables from Mash to Hash + variables = variables.to_h + end + + # create request and trigger builds + trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) + if trigger_request + present trigger_request, with: Entities::TriggerRequest + else + errors = 'No builds created' + render_api_error!(errors, 400) + end + end + end + end +end diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index d58a196c4ef..4d99164bc33 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -6,7 +6,42 @@ class AwardEmoji "ambulance", "anguished", "two_hearts", "wink" ] + ALIASES = { + pout: "rage", + satisfied: "laughing", + hankey: "shit", + poop: "shit", + collision: "boom", + thumbsup: "+1", + thumbsdown: "-1", + punch: "facepunch", + raised_hand: "hand", + running: "runner", + ng_woman: "no_good", + shoe: "mans_shoe", + tshirt: "shirt", + honeybee: "bee", + flipper: "dolphin", + paw_prints: "feet", + waxing_gibbous_moon: "moon", + telephone: "phone", + knife: "hocho", + envelope: "email", + pencil: "memo", + open_book: "book", + sailboat: "boat", + red_car: "car", + lantern: "izakaya_lantern", + uk: "gb", + heavy_exclamation_mark: "exclamation", + squirrel: "shipit" + }.with_indifferent_access + def self.path_to_emoji_image(name) "emoji/#{Emoji.emoji_filename(name)}.png" end + + def self.normilize_emoji_name(name) + ALIASES[name] || name + end end diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 07e68216d7f..5c347e432b4 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -30,9 +30,7 @@ module Ci helpers Gitlab::CurrentSettings mount Builds - mount Commits mount Runners - mount Projects mount Triggers end end diff --git a/lib/ci/api/commits.rb b/lib/ci/api/commits.rb deleted file mode 100644 index a60769d8305..00000000000 --- a/lib/ci/api/commits.rb +++ /dev/null @@ -1,66 +0,0 @@ -module Ci - module API - class Commits < Grape::API - resource :commits do - # Get list of commits per project - # - # Parameters: - # project_id (required) - The ID of a project - # project_token (requires) - Project token - # page (optional) - # per_page (optional) - items per request (default is 20) - # - get do - required_attributes! [:project_id, :project_token] - project = Ci::Project.find(params[:project_id]) - authenticate_project_token!(project) - - commits = project.commits.page(params[:page]).per(params[:per_page] || 20) - present commits, with: Entities::CommitWithBuilds - end - - # Create a commit - # - # Parameters: - # project_id (required) - The ID of a project - # project_token (requires) - Project token - # data (required) - GitLab push data - # - # Sample GitLab push data: - # { - # "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", - # "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - # "ref": "refs/heads/master", - # "commits": [ - # { - # "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - # "message": "Update Catalan translation to e38cb41.", - # "timestamp": "2011-12-12T14:27:31+02:00", - # "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - # "author": { - # "name": "Jordi Mallach", - # "email": "jordi@softcatala.org", - # } - # }, .... more commits - # ] - # } - # - # Example Request: - # POST /commits - post do - required_attributes! [:project_id, :data, :project_token] - project = Ci::Project.find(params[:project_id]) - authenticate_project_token!(project) - commit = Ci::CreateCommitService.new.execute(project, current_user, params[:data]) - - if commit.persisted? - present commit, with: Entities::CommitWithBuilds - else - errors = commit.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - end - end - end -end diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 750f421872d..e4ac0545ea2 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -37,15 +37,6 @@ module Ci expose :id, :token end - class Project < Grape::Entity - expose :id, :name, :token, :default_ref, :gitlab_url, :path, - :always_build, :polling_interval, :public, :ssh_url_to_repo, :gitlab_id - - expose :timeout do |model| - model.timeout - end - end - class RunnerProject < Grape::Entity expose :id, :project_id, :runner_id end diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index abd1869efc0..443563c2e4a 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -13,10 +13,6 @@ module Ci forbidden! unless current_runner end - def authenticate_project_token!(project) - forbidden! unless project.valid_token?(params[:project_token]) - end - def authenticate_build_token!(build) token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s forbidden! unless token && build.valid_token?(token) diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb deleted file mode 100644 index d719ad9e8d5..00000000000 --- a/lib/ci/api/projects.rb +++ /dev/null @@ -1,195 +0,0 @@ -module Ci - module API - # Projects API - class Projects < Grape::API - before { authenticate! } - - resource :projects do - # Register new webhook for project - # - # Parameters - # project_id (required) - The ID of a project - # web_hook (required) - WebHook URL - # Example Request - # POST /projects/:project_id/webhooks - post ":project_id/webhooks" do - required_attributes! [:web_hook] - - project = Ci::Project.find(params[:project_id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - web_hook = project.web_hooks.new({ url: params[:web_hook] }) - - if web_hook.save - present web_hook, with: Entities::WebHook - else - errors = web_hook.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Retrieve all Gitlab CI projects that the user has access to - # - # Example Request: - # GET /projects - get do - gitlab_projects = current_user.authorized_projects - gitlab_projects = filter_projects(gitlab_projects) - gitlab_projects = paginate gitlab_projects - - ids = gitlab_projects.map { |project| project.id } - - projects = Ci::Project.where("gitlab_id IN (?)", ids).load - present projects, with: Entities::Project - end - - # Retrieve all Gitlab CI projects that the user owns - # - # Example Request: - # GET /projects/owned - get "owned" do - gitlab_projects = current_user.owned_projects - gitlab_projects = filter_projects(gitlab_projects) - gitlab_projects = paginate gitlab_projects - - ids = gitlab_projects.map { |project| project.id } - - projects = Ci::Project.where("gitlab_id IN (?)", ids).load - present projects, with: Entities::Project - end - - # Retrieve info for a Gitlab CI project - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id - get ":id" do - project = Ci::Project.find(params[:id]) - unauthorized! unless can?(current_user, :read_project, project.gl_project) - - present project, with: Entities::Project - end - - # Create Gitlab CI project using Gitlab project info - # - # Parameters: - # gitlab_id (required) - The gitlab id of the project - # default_ref - The branch to run against (defaults to `master`) - # Example Request: - # POST /projects - post do - required_attributes! [:gitlab_id] - - filtered_params = { - gitlab_id: params[:gitlab_id], - # we accept gitlab_url for backward compatibility for a while (added to 7.11) - default_ref: params[:default_ref] || 'master' - } - - project = Ci::Project.new(filtered_params) - project.build_missing_services - - if project.save - present project, with: Entities::Project - else - errors = project.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Update a Gitlab CI project - # - # Parameters: - # id (required) - The ID of a project - # default_ref - The branch to run against (defaults to `master`) - # Example Request: - # PUT /projects/:id - put ":id" do - project = Ci::Project.find(params[:id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - attrs = attributes_for_keys [:default_ref] - - if project.update_attributes(attrs) - present project, with: Entities::Project - else - errors = project.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Remove a Gitlab CI project - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # DELETE /projects/:id - delete ":id" do - project = Ci::Project.find(params[:id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - project.destroy - end - - # Link a Gitlab CI project to a runner - # - # Parameters: - # id (required) - The ID of a CI project - # runner_id (required) - The ID of a runner - # Example Request: - # POST /projects/:id/runners/:runner_id - post ":id/runners/:runner_id" do - project = Ci::Project.find(params[:id]) - runner = Ci::Runner.find(params[:runner_id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - options = { - project_id: project.id, - runner_id: runner.id - } - - runner_project = Ci::RunnerProject.new(options) - - if runner_project.save - present runner_project, with: Entities::RunnerProject - else - errors = project.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Remove a Gitlab CI project from a runner - # - # Parameters: - # id (required) - The ID of a CI project - # runner_id (required) - The ID of a runner - # Example Request: - # DELETE /projects/:id/runners/:runner_id - delete ":id/runners/:runner_id" do - project = Ci::Project.find(params[:id]) - runner = Ci::Runner.find(params[:runner_id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - options = { - project_id: project.id, - runner_id: runner.id - } - - runner_project = Ci::RunnerProject.find_by(options) - - if runner_project.present? - runner_project.destroy - else - not_found! - end - end - end - end - end -end diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index 3edf067f46d..bfc14fe7a6b 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -3,17 +3,6 @@ module Ci # Runners API class Runners < Grape::API resource :runners do - # Get list of all available runners - # - # Example Request: - # GET /runners - get do - authenticate! - runners = Ci::Runner.all - - present runners, with: Entities::Runner - end - # Delete runner # Parameters: # token (required) - The unique token of runner @@ -47,7 +36,7 @@ module Ci tag_list: params[:tag_list], is_shared: true ) - elsif project = Ci::Project.find_by(token: params[:token]) + elsif project = Project.find_by(runners_token: params[:token]) # Create a specific runner for project. project.runners.create( description: params[:description], diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb index 40907d6db54..63b42113513 100644 --- a/lib/ci/api/triggers.rb +++ b/lib/ci/api/triggers.rb @@ -14,7 +14,7 @@ module Ci post ":id/refs/:ref/trigger" do required_attributes! [:token] - project = Ci::Project.find(params[:id]) + project = Project.find_by(ci_id: params[:id].to_i) trigger = Ci::Trigger.find_by_token(params[:token].to_s) not_found! unless project && trigger unauthorized! unless trigger.project == project diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb index 5ff7407c6fe..d53bdcbd0f2 100644 --- a/lib/ci/charts.rb +++ b/lib/ci/charts.rb @@ -60,7 +60,7 @@ module Ci class BuildTime < Chart def collect - commits = project.commits.last(30) + commits = project.ci_commits.last(30) commits.each do |commit| @labels << commit.short_sha diff --git a/lib/ci/current_settings.rb b/lib/ci/current_settings.rb deleted file mode 100644 index fd78b024970..00000000000 --- a/lib/ci/current_settings.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Ci - module CurrentSettings - def current_application_settings - key = :ci_current_application_settings - - RequestStore.store[key] ||= begin - if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('ci_application_settings') - Ci::ApplicationSetting.current || Ci::ApplicationSetting.create_from_defaults - else - fake_application_settings - end - end - end - - def fake_application_settings - OpenStruct.new( - all_broken_builds: Ci::Settings.gitlab_ci['all_broken_builds'], - add_pusher: Ci::Settings.gitlab_ci['add_pusher'], - ) - end - end -end diff --git a/lib/ci/git.rb b/lib/ci/git.rb deleted file mode 100644 index 7acc3f38edb..00000000000 --- a/lib/ci/git.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Ci - module Git - BLANK_SHA = '0' * 40 - end -end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index e7fb1b79b7c..bcdfd38d292 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -132,26 +132,36 @@ module Ci end def validate_job!(name, job) + validate_job_name!(name) + validate_job_keys!(name, job) + validate_job_types!(name, job) + + validate_job_stage!(name, job) if job[:stage] + validate_job_cache!(name, job) if job[:cache] + validate_job_artifacts!(name, job) if job[:artifacts] + end + + private + + def validate_job_name!(name) if name.blank? || !validate_string(name) raise ValidationError, "job name should be non-empty string" end + end + def validate_job_keys!(name, job) job.keys.each do |key| unless ALLOWED_JOB_KEYS.include? key raise ValidationError, "#{name} job: unknown parameter #{key}" end end + end + def validate_job_types!(name, job) if !validate_string(job[:script]) && !validate_array_of_strings(job[:script]) raise ValidationError, "#{name} job: script should be a string or an array of a strings" end - if job[:stage] - unless job[:stage].is_a?(String) && job[:stage].in?(stages) - raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" - end - end - if job[:image] && !validate_string(job[:image]) raise ValidationError, "#{name} job: image should be a string" end @@ -172,36 +182,40 @@ module Ci raise ValidationError, "#{name} job: except parameter should be an array of strings" end - if job[:cache] - if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked]) - raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean" - end - - if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths]) - raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings" - end + if job[:allow_failure] && !validate_boolean(job[:allow_failure]) + raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" end - if job[:artifacts] - if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked]) - raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean" - end + if job[:when] && !job[:when].in?(%w(on_success on_failure always)) + raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always" + end + end - if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths]) - raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings" - end + def validate_job_stage!(name, job) + unless job[:stage].is_a?(String) && job[:stage].in?(stages) + raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" end + end - if job[:allow_failure] && !validate_boolean(job[:allow_failure]) - raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" + def validate_job_cache!(name, job) + if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked]) + raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean" end - if job[:when] && !job[:when].in?(%w(on_success on_failure always)) - raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always" + if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths]) + raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings" end end - private + def validate_job_artifacts!(name, job) + if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked]) + raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean" + end + + if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths]) + raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings" + end + end def validate_array_of_strings(values) values.is_a?(Array) && values.all? { |value| validate_string(value) } diff --git a/lib/ci/scheduler.rb b/lib/ci/scheduler.rb deleted file mode 100644 index ee0958f4be1..00000000000 --- a/lib/ci/scheduler.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Ci - class Scheduler - def perform - projects = Ci::Project.where(always_build: true).all - projects.each do |project| - last_commit = project.commits.last - next unless last_commit && last_commit.last_build - - interval = project.polling_interval - if (last_commit.last_build.created_at + interval.hours) < Time.now - last_commit.retry - end - end - end - end -end diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 0d156047ff0..cdcaae8094c 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -77,7 +77,9 @@ module Grack if project && matched_login.present? && git_cmd == 'git-upload-pack' underscored_service = matched_login['s'].underscore - if Service.available_services_names.include?(underscored_service) + if underscored_service == 'gitlab_ci' + return project && project.valid_build_token?(password) + elsif Service.available_services_names.include?(underscored_service) service_method = "#{underscored_service}_service" service = project.send(service_method) diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb new file mode 100644 index 00000000000..86bfa0a4378 --- /dev/null +++ b/lib/gitlab/build_data_builder.rb @@ -0,0 +1,64 @@ +module Gitlab + class BuildDataBuilder + class << self + def build(build) + project = build.project + commit = build.commit + user = build.user + + data = { + object_kind: 'build', + + ref: build.ref, + tag: build.tag, + before_sha: build.before_sha, + sha: build.sha, + + # TODO: should this be not prefixed with build_? + # Leaving this way to have backward compatibility + build_id: build.id, + build_name: build.name, + build_stage: build.stage, + build_status: build.status, + build_started_at: build.started_at, + build_finished_at: build.finished_at, + build_duration: build.duration, + + # TODO: do we still need it? + project_id: project.id, + project_name: project.name_with_namespace, + + user: { + id: user.try(:id), + name: user.try(:name), + email: user.try(:email), + }, + + commit: { + id: commit.id, + sha: commit.sha, + message: commit.git_commit_message, + author_name: commit.git_author_name, + author_email: commit.git_author_email, + status: commit.status, + duration: commit.duration, + started_at: commit.started_at, + finished_at: commit.finished_at, + }, + + repository: { + name: project.name, + url: project.url_to_repo, + description: project.description, + homepage: project.web_url, + git_http_url: project.http_url_to_repo, + git_ssh_url: project.ssh_url_to_repo, + visibility_level: project.visibility_level + }, + } + + data + end + end + end +end diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 71f37f1fef8..de77a6fbff1 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -7,5 +7,23 @@ module Gitlab def self.postgresql? ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql' end + + def true_value + case ActiveRecord::Base.connection.adapter_name.downcase + when 'postgresql' + "'t'" + else + 1 + end + end + + def false_value + case ActiveRecord::Base.connection.adapter_name.downcase + when 'postgresql' + "'f'" + else + 0 + end + end end end diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb new file mode 100644 index 00000000000..a2eb7a70bd2 --- /dev/null +++ b/lib/gitlab/email/message/repository_push.rb @@ -0,0 +1,137 @@ +module Gitlab + module Email + module Message + class RepositoryPush + attr_accessor :recipient + attr_reader :author_id, :ref, :action + + include Gitlab::Application.routes.url_helpers + + delegate :namespace, :name_with_namespace, to: :project, prefix: :project + delegate :name, to: :author, prefix: :author + + def initialize(notify, project_id, recipient, opts = {}) + raise ArgumentError, 'Missing options: author_id, ref, action' unless + opts[:author_id] && opts[:ref] && opts[:action] + + @notify = notify + @project_id = project_id + @recipient = recipient + @opts = opts.dup + + @author_id = @opts.delete(:author_id) + @ref = @opts.delete(:ref) + @action = @opts.delete(:action) + end + + def project + @project ||= Project.find(@project_id) + end + + def author + @author ||= User.find(@author_id) + end + + def commits + @commits ||= (Commit.decorate(compare.commits, project) if compare) + end + + def diffs + @diffs ||= (compare.diffs if compare) + end + + def diffs_count + diffs.count if diffs + end + + def compare + @opts[:compare] + end + + def compare_timeout + compare.timeout if compare + end + + def reverse_compare? + @opts[:reverse_compare] || false + end + + def disable_diffs? + @opts[:disable_diffs] || false + end + + def send_from_committer_email? + @opts[:send_from_committer_email] || false + end + + def action_name + @action_name ||= + case @action + when :create + 'pushed new' + when :delete + 'deleted' + else + 'pushed to' + end + end + + def ref_name + @ref_name ||= Gitlab::Git.ref_name(@ref) + end + + def ref_type + @ref_type ||= Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch' + end + + def target_url + if @action == :push && commits + if commits.length > 1 + namespace_project_compare_url(project_namespace, + project, + from: Commit.new(compare.base, project), + to: Commit.new(compare.head, project)) + else + namespace_project_commit_url(project_namespace, + project, commits.first) + end + else + unless @action == :delete + namespace_project_tree_url(project_namespace, + project, ref_name) + end + end + end + + def reply_to + if send_from_committer_email? && @notify.can_send_from_user_email?(author) + author.email + else + Gitlab.config.gitlab.email_reply_to + end + end + + def subject + subject_text = '[Git]' + subject_text << "[#{project.path_with_namespace}]" + subject_text << "[#{ref_name}]" if @action == :push + subject_text << ' ' + + if @action == :push && commits + if commits.length > 1 + subject_text << "Deleted " if reverse_compare? + subject_text << "#{commits.length} commits: #{commits.first.title}" + else + subject_text << "Deleted 1 commit: " if reverse_compare? + subject_text << commits.first.title + end + else + subject_action = action_name.dup + subject_action[0] = subject_action[0].capitalize + subject_text << "#{subject_action} #{ref_type} #{ref_name}" + end + end + end + end + end +end diff --git a/lib/tasks/ci/schedule_builds.rake b/lib/tasks/ci/schedule_builds.rake deleted file mode 100644 index 49435504c67..00000000000 --- a/lib/tasks/ci/schedule_builds.rake +++ /dev/null @@ -1,6 +0,0 @@ -namespace :ci do - desc "GitLab CI | Clean running builds" - task schedule_builds: :environment do - Ci::Scheduler.new.perform - end -end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index a25fac62cfc..b5af3d88b4c 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -331,7 +331,7 @@ namespace :gitlab do end def check_redis_version - min_redis_version = "2.4.0" + min_redis_version = "2.8.0" print "Redis version >= #{min_redis_version}? ... " redis_version = run(%W(redis-cli --version)) diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 2fcd70182b9..f76e826f138 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -42,6 +42,10 @@ FactoryGirl.define do commit factory: :ci_commit + after(:build) do |build, evaluator| + build.project = build.commit.project + end + factory :ci_not_started_build do started_at nil finished_at nil diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb index 70e3fa319c6..b42cafa518a 100644 --- a/spec/factories/ci/commits.rb +++ b/spec/factories/ci/commits.rb @@ -21,7 +21,7 @@ FactoryGirl.define do factory :ci_empty_commit, class: Ci::Commit do sha '97de212e80737a608d939f648d959671fb0a0142' - gl_project factory: :empty_project + project factory: :empty_project factory :ci_commit_without_jobs do after(:build) do |commit| diff --git a/spec/factories/ci/events.rb b/spec/factories/ci/events.rb deleted file mode 100644 index 9638618a400..00000000000 --- a/spec/factories/ci/events.rb +++ /dev/null @@ -1,24 +0,0 @@ -# == Schema Information -# -# Table name: events -# -# id :integer not null, primary key -# project_id :integer -# user_id :integer -# is_admin :integer -# description :text -# created_at :datetime -# updated_at :datetime -# - -FactoryGirl.define do - factory :ci_event, class: Ci::Event do - sequence :description do |n| - "updated project settings#{n}" - end - - factory :ci_admin_event do - is_admin true - end - end -end diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb deleted file mode 100644 index 11cb8c9eeaa..00000000000 --- a/spec/factories/ci/projects.rb +++ /dev/null @@ -1,50 +0,0 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) not null -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :ci_project_without_token, class: Ci::Project do - default_ref 'master' - - shared_runners_enabled false - - factory :ci_project do - token 'iPWx6WM4lhHNedGfBpPJNP' - end - - initialize_with do - # TODO: - # this is required, because builds_enabled is initialized when Project is created - # and this create gitlab_ci_project if builds is set to true - # here we take created gitlab_ci_project and update it's attributes - ci_project = create(:empty_project).ensure_gitlab_ci_project - ci_project.update_attributes(attributes) - ci_project - end - end -end diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb index 3aa14ca434d..008d1c5d961 100644 --- a/spec/factories/ci/runner_projects.rb +++ b/spec/factories/ci/runner_projects.rb @@ -14,6 +14,6 @@ FactoryGirl.define do factory :ci_runner_project, class: Ci::RunnerProject do runner_id 1 - project_id 1 + gl_project_id 1 end end diff --git a/spec/factories/ci/web_hook.rb b/spec/factories/ci/web_hook.rb deleted file mode 100644 index 40d878ecb3c..00000000000 --- a/spec/factories/ci/web_hook.rb +++ /dev/null @@ -1,6 +0,0 @@ -FactoryGirl.define do - factory :ci_web_hook, class: Ci::WebHook do - sequence(:url) { FFaker::Internet.uri('http') } - project factory: :ci_project - end -end diff --git a/spec/features/ci/admin/builds_spec.rb b/spec/features/atom/builds_spec.rb index 623d466c67b..72764b1629d 100644 --- a/spec/features/ci/admin/builds_spec.rb +++ b/spec/features/atom/builds_spec.rb @@ -5,17 +5,16 @@ describe "Admin Builds" do let(:build) { FactoryGirl.create :ci_build, commit: commit } before do - skip_ci_admin_auth - login_as :user + login_as :admin end describe "GET /admin/builds" do before do build - visit ci_admin_builds_path + visit admin_builds_path end - it { expect(page).to have_content "All builds" } + it { expect(page).to have_content "Running" } it { expect(page).to have_content build.short_sha } end @@ -26,43 +25,43 @@ describe "Admin Builds" do FactoryGirl.create :ci_build, commit: commit, status: "success" FactoryGirl.create :ci_build, commit: commit, status: "failed" - visit ci_admin_builds_path + visit admin_builds_path + + within ".center-top-menu" do + click_on "All" + end expect(page.all(".build-link").size).to eq(4) end - it "shows pending builds" do + it "shows finished builds" do build = FactoryGirl.create :ci_build, commit: commit, status: "pending" build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" - build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - visit ci_admin_builds_path + visit admin_builds_path - within ".nav.nav-tabs" do - click_on "Pending" + within ".center-top-menu" do + click_on "Finished" end - expect(page.find(".build-link")).to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build.id) expect(page.find(".build-link")).not_to have_content(build1.id) - expect(page.find(".build-link")).not_to have_content(build2.id) - expect(page.find(".build-link")).not_to have_content(build3.id) + expect(page.find(".build-link")).to have_content(build2.id) end it "shows running builds" do build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - visit ci_admin_builds_path + visit admin_builds_path - within ".nav.nav-tabs" do + within ".center-top-menu" do click_on "Running" end - expect(page.find(".build-link")).to have_content(build1.id) - expect(page.find(".build-link")).not_to have_content(build.id) + expect(page.find(".build-link")).to have_content(build.id) expect(page.find(".build-link")).not_to have_content(build2.id) expect(page.find(".build-link")).not_to have_content(build3.id) end diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/atom/runners_spec.rb index fd1967d0698..d5dd11a01d3 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/atom/runners_spec.rb @@ -10,7 +10,7 @@ describe "Admin Runners" do runner = FactoryGirl.create(:ci_runner) commit = FactoryGirl.create(:ci_commit) FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) - visit ci_admin_runners_path + visit admin_runners_path end it { page.has_text? "Manage Runners" } @@ -36,9 +36,9 @@ describe "Admin Runners" do let(:runner) { FactoryGirl.create :ci_runner } before do - @project1 = FactoryGirl.create(:ci_project) - @project2 = FactoryGirl.create(:ci_project) - visit ci_admin_runner_path(runner) + @project1 = FactoryGirl.create(:empty_project) + @project2 = FactoryGirl.create(:empty_project) + visit admin_runner_path(runner) end describe 'runner info' do @@ -53,7 +53,7 @@ describe "Admin Runners" do describe 'search' do before do search_form = find('#runner-projects-search') - search_form.fill_in 'search', with: @project1.gl_project.name + search_form.fill_in 'search', with: @project1.name search_form.click_button 'Search' end diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index 52134556339..dc41be8246f 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -79,6 +79,6 @@ describe "User Feed", feature: true do end def safe_name - CGI.escapeHTML(user.name) + html_escape(user.name) end end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 1f99a808f87..f0031a0a247 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -7,15 +7,15 @@ describe "Builds" do login_as(:user) @commit = FactoryGirl.create :ci_commit @build = FactoryGirl.create :ci_build, commit: @commit - @gl_project = @commit.project.gl_project - @gl_project.team << [@user, :master] + @project = @commit.project + @project.team << [@user, :master] end describe "GET /:project/builds" do context "Running scope" do before do @build.run! - visit namespace_project_builds_path(@gl_project.namespace, @gl_project) + visit namespace_project_builds_path(@project.namespace, @project) end it { expect(page).to have_content 'Running' } @@ -28,7 +28,7 @@ describe "Builds" do context "Finished scope" do before do @build.run! - visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :finished) + visit namespace_project_builds_path(@project.namespace, @project, scope: :finished) end it { expect(page).to have_content 'No builds to show' } @@ -37,8 +37,8 @@ describe "Builds" do context "All builds" do before do - @gl_project.ci_builds.running_or_pending.each(&:success) - visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :all) + @project.builds.running_or_pending.each(&:success) + visit namespace_project_builds_path(@project.namespace, @project, scope: :all) end it { expect(page).to have_content 'All' } @@ -52,7 +52,7 @@ describe "Builds" do describe "POST /:project/builds/:id/cancel_all" do before do @build.run! - visit namespace_project_builds_path(@gl_project.namespace, @gl_project) + visit namespace_project_builds_path(@project.namespace, @project) click_link "Cancel running" end @@ -62,7 +62,7 @@ describe "Builds" do describe "GET /:project/builds/:id" do before do - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) end it { expect(page).to have_content @commit.sha[0..7] } @@ -72,7 +72,7 @@ describe "Builds" do context "Download artifacts" do before do @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) end it { expect(page).to have_content 'Download artifacts' } @@ -82,7 +82,7 @@ describe "Builds" do describe "POST /:project/builds/:id/cancel" do before do @build.run! - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) click_link "Cancel" end @@ -93,7 +93,7 @@ describe "Builds" do describe "POST /:project/builds/:id/retry" do before do @build.run! - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) click_link "Cancel" click_link 'Retry' end @@ -105,7 +105,7 @@ describe "Builds" do describe "GET /:project/builds/:id/download" do before do @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) click_link 'Download artifacts' end diff --git a/spec/features/ci/admin/events_spec.rb b/spec/features/ci/admin/events_spec.rb deleted file mode 100644 index a7e75cc4f6b..00000000000 --- a/spec/features/ci/admin/events_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe "Admin Events" do - let(:event) { FactoryGirl.create :ci_admin_event } - - before do - skip_ci_admin_auth - login_as :user - end - - describe "GET /admin/events" do - before do - event - visit ci_admin_events_path - end - - it { expect(page).to have_content "Events" } - it { expect(page).to have_content event.description } - end -end diff --git a/spec/features/ci/admin/projects_spec.rb b/spec/features/ci/admin/projects_spec.rb deleted file mode 100644 index b88f55a6807..00000000000 --- a/spec/features/ci/admin/projects_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe "Admin Projects" do - let(:project) { FactoryGirl.create :ci_project } - - before do - skip_ci_admin_auth - login_as :user - end - - describe "GET /admin/projects" do - before do - project - visit ci_admin_projects_path - end - - it { expect(page).to have_content "Projects" } - end -end diff --git a/spec/features/ci_settings_spec.rb b/spec/features/ci_settings_spec.rb deleted file mode 100644 index 7e25e883018..00000000000 --- a/spec/features/ci_settings_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec_helper' - -describe "CI settings" do - let(:user) { create(:user) } - before { login_as(user) } - - before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] - visit edit_namespace_project_ci_settings_path(@gl_project.namespace, @gl_project) - end - - it { expect(page).to have_content 'Build Schedule' } - - it "updates configuration" do - fill_in 'Timeout', with: '70' - click_button 'Save changes' - expect(page).to have_content 'was successfully updated' - expect(find_field('Timeout').value).to eq '70' - end -end diff --git a/spec/features/ci_web_hooks_spec.rb b/spec/features/ci_web_hooks_spec.rb deleted file mode 100644 index efae0a42c1e..00000000000 --- a/spec/features/ci_web_hooks_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe 'CI web hooks' do - let(:user) { create(:user) } - before { login_as(user) } - - before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] - visit namespace_project_ci_web_hooks_path(@gl_project.namespace, @gl_project) - end - - context 'create a trigger' do - before do - fill_in 'web_hook_url', with: 'http://example.com' - click_on 'Add Web Hook' - end - - it { expect(@project.web_hooks.count).to eq(1) } - - it 'revokes the trigger' do - click_on 'Remove' - expect(@project.web_hooks.count).to eq(0) - end - end -end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 90739cd6a28..cc0d4c150fe 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -9,8 +9,7 @@ describe "Commits" do before do login_as :user project.team << [@user, :master] - @ci_project = project.ensure_gitlab_ci_project - @commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha + @commit = FactoryGirl.create :ci_commit, project: project, sha: project.commit.sha @build = FactoryGirl.create :ci_build, commit: @commit @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 3d6d87e764a..a2fb3e4c75d 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -69,7 +69,10 @@ describe 'Issues', feature: true do click_button 'Save changes' - expect(page).to have_content 'Assignee: none' + page.within('.assignee') do + expect(page).to have_content 'None' + end + expect(issue.reload.assignee).to be_nil end end @@ -202,11 +205,11 @@ describe 'Issues', feature: true do it 'with dropdown menu' do visit namespace_project_issue_path(project.namespace, project, issue) - find('.context #issue_assignee_id'). + find('.issuable-sidebar #issue_assignee_id'). set project.team.members.first.id click_button 'Update Issue' - expect(page).to have_content 'Assignee:' + expect(page).to have_content 'Assignee' has_select?('issue_assignee_id', selected: project.team.members.first.name) end @@ -241,12 +244,16 @@ describe 'Issues', feature: true do it 'with dropdown menu' do visit namespace_project_issue_path(project.namespace, project, issue) - find('.context'). + find('.issuable-sidebar'). select(milestone.title, from: 'issue_milestone_id') click_button 'Update Issue' expect(page).to have_content "Milestone changed to #{milestone.title}" - expect(page).to have_content "Milestone: #{milestone.title}" + + page.within('.milestone') do + expect(page).to have_content milestone.title + end + has_select?('issue_assignee_id', selected: milestone.title) end end @@ -279,13 +286,19 @@ describe 'Issues', feature: true do it 'allows user to remove assignee', js: true do visit namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_content "Assignee: #{user2.name}" - first('#s2id_issue_assignee_id').click + page.within('.assignee') do + expect(page).to have_content user2.name + end + + find('.assignee .edit-link').click sleep 2 # wait for ajax stuff to complete first('.user-result').click - expect(page).to have_content 'Assignee: none' + page.within('.assignee') do + expect(page).to have_content 'None' + end + sleep 2 # wait for ajax stuff to complete expect(issue.reload.assignee).to be_nil end diff --git a/spec/features/ci/lint_spec.rb b/spec/features/lint_spec.rb index 5d8f56e2cfb..5d8f56e2cfb 100644 --- a/spec/features/ci/lint_spec.rb +++ b/spec/features/lint_spec.rb diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index a674124aab7..28a46a0725d 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -12,7 +12,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do end context "Active build for Merge Request" do - let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } + let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } let!(:ci_build) { create(:ci_build, commit: ci_commit) } before do @@ -47,7 +47,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do merge_user: user, title: "MepMep", merge_when_build_succeeds: true) end - let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } + let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } let!(:ci_build) { create(:ci_build, commit: ci_commit) } before do diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index b0259026630..d97831aae14 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -8,14 +8,14 @@ describe "Runners" do describe "specific runners" do before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] + @project = FactoryGirl.create :empty_project, shared_runners_enabled: false + @project.team << [user, :master] - @project2 = FactoryGirl.create :ci_project - @project2.gl_project.team << [user, :master] + @project2 = FactoryGirl.create :empty_project + @project2.team << [user, :master] - @project3 = FactoryGirl.create :ci_project - @project3.gl_project.team << [user, :developer] + @project3 = FactoryGirl.create :empty_project + @project3.team << [user, :developer] @shared_runner = FactoryGirl.create :ci_shared_runner @specific_runner = FactoryGirl.create :ci_specific_runner @@ -25,7 +25,7 @@ describe "Runners" do @project2.runners << @specific_runner2 @project3.runners << @specific_runner3 - visit runners_path(@project.gl_project) + visit runners_path(@project) end before do @@ -49,7 +49,7 @@ describe "Runners" do it "disables specific runner for project" do @project2.runners << @specific_runner - visit runners_path(@project.gl_project) + visit runners_path(@project) within ".activated-specific-runners" do click_on "Disable for this project" @@ -69,9 +69,9 @@ describe "Runners" do describe "shared runners" do before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - visit runners_path(@project.gl_project) + @project = FactoryGirl.create :empty_project, shared_runners_enabled: false + @project.team << [user, :master] + visit runners_path(@project) end it "enables shared runners" do @@ -82,14 +82,14 @@ describe "Runners" do describe "show page" do before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] + @project = FactoryGirl.create :empty_project + @project.team << [user, :master] @specific_runner = FactoryGirl.create :ci_specific_runner @project.runners << @specific_runner end it "shows runner information" do - visit runners_path(@project.gl_project) + visit runners_path(@project) click_on @specific_runner.short_sha expect(page).to have_content(@specific_runner.platform) end diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 69492d58878..3cbc8253ad6 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -5,10 +5,9 @@ describe 'Triggers' do before { login_as(user) } before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] - visit namespace_project_triggers_path(@gl_project.namespace, @gl_project) + @project = FactoryGirl.create :empty_project + @project.team << [user, :master] + visit namespace_project_triggers_path(@project.namespace, @project) end context 'create a trigger' do diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index adb602f3edd..afea1840cd7 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -6,13 +6,12 @@ describe "Variables" do describe "specific runners" do before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] + @project = FactoryGirl.create :empty_project + @project.team << [user, :master] end it "creates variable", js: true do - visit namespace_project_variables_path(@gl_project.namespace, @gl_project) + visit namespace_project_variables_path(@project.namespace, @project) click_on "Add a variable" fill_in "Key", with: "SECRET_KEY" fill_in "Value", with: "SECRET_VALUE" diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index ce4a5244bd0..c90133fbf03 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -532,21 +532,21 @@ module Ci end it "returns errors if job stage is not a string" do - config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) + config = YAML.dump({ rspec: { script: "test", type: 1 } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a pre-defined stage" do - config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + config = YAML.dump({ rspec: { script: "test", type: "acceptance" } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a defined stage" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index 73458735ad9..cd26dca0998 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -191,15 +191,10 @@ describe Grack::Auth, lib: true do context "when a gitlab ci token is provided" do let(:token) { "123" } - let(:gitlab_ci_project) { FactoryGirl.create :ci_project, token: token } + let(:project) { FactoryGirl.create :empty_project } before do - project.gitlab_ci_project = gitlab_ci_project - project.save - - gitlab_ci_service = project.build_gitlab_ci_service - gitlab_ci_service.active = true - gitlab_ci_service.save + project.update_attributes(runners_token: token, builds_enabled: true) env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) end diff --git a/spec/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb new file mode 100644 index 00000000000..839b30f1ff4 --- /dev/null +++ b/spec/lib/gitlab/build_data_builder_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe 'Gitlab::BuildDataBuilder' do + let(:build) { create(:ci_build) } + + describe :build do + let(:data) do + Gitlab::BuildDataBuilder.build(build) + end + + it { expect(data).to be_a(Hash) } + it { expect(data[:ref]).to eq(build.ref) } + it { expect(data[:sha]).to eq(build.sha) } + it { expect(data[:tag]).to eq(build.tag) } + it { expect(data[:build_id]).to eq(build.id) } + it { expect(data[:build_status]).to eq(build.status) } + it { expect(data[:project_id]).to eq(build.project.id) } + it { expect(data[:project_name]).to eq(build.project.name_with_namespace) } + end +end diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb new file mode 100644 index 00000000000..56ae2a8d121 --- /dev/null +++ b/spec/lib/gitlab/email/message/repository_push_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +describe Gitlab::Email::Message::RepositoryPush do + include RepoHelpers + + let!(:group) { create(:group, name: 'my_group') } + let!(:project) { create(:project, name: 'my_project', namespace: group) } + let!(:author) { create(:author, name: 'Author') } + + let(:message) do + described_class.new(Notify, project.id, 'recipient@example.com', opts) + end + + context 'new commits have been pushed to repository' do + let(:opts) do + { author_id: author.id, ref: 'master', action: :push, compare: compare, + send_from_committer_email: true } + end + let(:compare) do + Gitlab::Git::Compare.new(project.repository.raw_repository, + sample_image_commit.id, sample_commit.id) + end + + describe '#project' do + subject { message.project } + it { is_expected.to eq project } + it { is_expected.to be_an_instance_of Project } + end + + describe '#project_namespace' do + subject { message.project_namespace } + it { is_expected.to eq group } + it { is_expected.to be_kind_of Namespace } + end + + describe '#project_name_with_namespace' do + subject { message.project_name_with_namespace } + it { is_expected.to eq 'my_group / my_project' } + end + + describe '#author' do + subject { message.author } + it { is_expected.to eq author } + it { is_expected.to be_an_instance_of User } + end + + describe '#author_name' do + subject { message.author_name } + it { is_expected.to eq 'Author' } + end + + describe '#commits' do + subject { message.commits } + it { is_expected.to be_kind_of Array } + it { is_expected.to all(be_instance_of Commit) } + end + + describe '#diffs' do + subject { message.diffs } + it { is_expected.to all(be_an_instance_of Gitlab::Git::Diff) } + end + + describe '#diffs_count' do + subject { message.diffs_count } + it { is_expected.to eq compare.diffs.count } + end + + describe '#compare' do + subject { message.compare } + it { is_expected.to be_an_instance_of Gitlab::Git::Compare } + end + + describe '#compare_timeout' do + subject { message.compare_timeout } + it { is_expected.to eq compare.timeout } + end + + describe '#reverse_compare?' do + subject { message.reverse_compare? } + it { is_expected.to eq false } + end + + describe '#disable_diffs?' do + subject { message.disable_diffs? } + it { is_expected.to eq false } + end + + describe '#send_from_committer_email?' do + subject { message.send_from_committer_email? } + it { is_expected.to eq true } + end + + describe '#action_name' do + subject { message.action_name } + it { is_expected.to eq 'pushed to' } + end + + describe '#ref_name' do + subject { message.ref_name } + it { is_expected.to eq 'master' } + end + + describe '#ref_type' do + subject { message.ref_type } + it { is_expected.to eq 'branch' } + end + + describe '#target_url' do + subject { message.target_url } + it { is_expected.to include 'compare' } + it { is_expected.to include compare.commits.first.parents.first.id } + it { is_expected.to include compare.commits.last.id } + end + + describe '#subject' do + subject { message.subject } + it { is_expected.to include "[Git][#{project.path_with_namespace}]" } + it { is_expected.to include "#{compare.commits.length} commits" } + it { is_expected.to include compare.commits.first.message.split("\n").first } + end + end +end diff --git a/spec/mailers/ci/notify_spec.rb b/spec/mailers/ci/notify_spec.rb deleted file mode 100644 index b83fb41603b..00000000000 --- a/spec/mailers/ci/notify_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'spec_helper' - -describe Ci::Notify do - include EmailSpec::Helpers - include EmailSpec::Matchers - - before do - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - end - - describe 'build success' do - subject { Ci::Notify.build_success_email(@build.id, 'wow@example.com') } - - it 'has the correct subject' do - should have_subject /Build success for/ - end - - it 'contains name of project' do - should have_body_text /build successful/ - end - end - - describe 'build fail' do - subject { Ci::Notify.build_fail_email(@build.id, 'wow@example.com') } - - it 'has the correct subject' do - should have_subject /Build failed for/ - end - - it 'contains name of project' do - should have_body_text /build failed/ - end - end -end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 27e509933b2..154901a2fbc 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -13,6 +13,7 @@ describe Notify do let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to } let(:recipient) { create(:user, email: 'recipient@example.com') } let(:project) { create(:project) } + let(:build) { create(:ci_build) } before(:each) do ActionMailer::Base.deliveries.clear @@ -865,4 +866,32 @@ describe Notify do is_expected.to have_body_text /#{diff_path}/ end end + + describe 'build success' do + before { build.success } + + subject { Notify.build_success_email(build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build success for/ + end + + it 'contains name of project' do + should have_body_text build.project_name + end + end + + describe 'build fail' do + before { build.drop } + + subject { Notify.build_fail_email(build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build failed for/ + end + + it 'contains name of project' do + should have_body_text build.project_name + end + end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b67b84959d9..5f64453a35f 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -73,26 +73,4 @@ describe ApplicationSetting, models: true do expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com']) end end - - context 'shared runners' do - let(:gl_project) { create(:empty_project) } - - before do - allow_any_instance_of(Project).to receive(:current_application_settings).and_return(setting) - end - - subject { gl_project.ensure_gitlab_ci_project.shared_runners_enabled } - - context 'enabled' do - before { setting.update_attributes(shared_runners_enabled: true) } - - it { is_expected.to be_truthy } - end - - context 'disabled' do - before { setting.update_attributes(shared_runners_enabled: false) } - - it { is_expected.to be_falsey } - end - end end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 70c831b7cbe..96b6f1dbca6 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -26,9 +26,8 @@ require 'spec_helper' describe Ci::Build, models: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:project) { FactoryGirl.create :empty_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } let(:build) { FactoryGirl.create :ci_build, commit: commit } it { is_expected.to validate_presence_of :ref } @@ -112,7 +111,7 @@ describe Ci::Build, models: true do let(:token) { 'my_secret_token' } before do - build.project.update_attributes(token: token) + build.project.update_attributes(runners_token: token) build.update_attributes(trace: token) end @@ -120,11 +119,12 @@ describe Ci::Build, models: true do end end - describe :timeout do - subject { build.timeout } - - it { is_expected.to eq(commit.project.timeout) } - end + # TODO: build timeout + # describe :timeout do + # subject { build.timeout } + # + # it { is_expected.to eq(commit.project.timeout) } + # end describe :options do let(:options) do @@ -140,11 +140,12 @@ describe Ci::Build, models: true do it { is_expected.to eq(options) } end - describe :allow_git_fetch do - subject { build.allow_git_fetch } - - it { is_expected.to eq(project.allow_git_fetch) } - end + # TODO: allow_git_fetch + # describe :allow_git_fetch do + # subject { build.allow_git_fetch } + # + # it { is_expected.to eq(project.allow_git_fetch) } + # end describe :project do subject { build.project } @@ -164,12 +165,6 @@ describe Ci::Build, models: true do it { is_expected.to eq(project.name) } end - describe :repo_url do - subject { build.repo_url } - - it { is_expected.to eq(project.repo_url_with_auth) } - end - describe :extract_coverage do context 'valid content & regex' do subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } @@ -266,40 +261,6 @@ describe Ci::Build, models: true do end end - describe :project_recipients do - let(:pusher_email) { 'pusher@gitlab.test' } - let(:user) { User.new(notification_email: pusher_email) } - subject { build.project_recipients } - - before do - build.update_attributes(user: user) - end - - it 'should return pusher_email as only recipient when no additional recipients are given' do - project.update_attributes(email_add_pusher: true, - email_recipients: '') - is_expected.to eq([pusher_email]) - end - - it 'should return pusher_email and additional recipients' do - project.update_attributes(email_add_pusher: true, - email_recipients: 'rec1 rec2') - is_expected.to eq(['rec1', 'rec2', pusher_email]) - end - - it 'should return recipients' do - project.update_attributes(email_add_pusher: false, - email_recipients: 'rec1 rec2') - is_expected.to eq(['rec1', 'rec2']) - end - - it 'should return unique recipients only' do - project.update_attributes(email_add_pusher: true, - email_recipients: "rec1 rec1 #{pusher_email}") - is_expected.to eq(['rec1', pusher_email]) - end - end - describe :can_be_served? do let(:runner) { FactoryGirl.create :ci_specific_runner } @@ -415,4 +376,18 @@ describe Ci::Build, models: true do is_expected.to_not be_nil end end + + describe :repo_url do + let(:build) { FactoryGirl.create :ci_build } + let(:project) { build.project } + + subject { build.repo_url } + + it { is_expected.to be_a(String) } + it { is_expected.to end_with(".git") } + it { is_expected.to start_with(project.web_url[0..6]) } + it { is_expected.to include(build.token) } + it { is_expected.to include('gitlab-ci-token') } + it { is_expected.to include(project.web_url[7..-1]) } + end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 89813cdf7fc..ac61c8fb525 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -13,17 +13,16 @@ # tag :boolean default(FALSE) # yaml_errors :text # committed_at :datetime -# gl_project_id :integer +# project_id :integer # require 'spec_helper' describe Ci::Commit, models: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:project) { FactoryGirl.create :empty_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } - it { is_expected.to belong_to(:gl_project) } + it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:statuses) } it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:builds) } @@ -37,16 +36,16 @@ describe Ci::Commit, models: true do let(:project) { FactoryGirl.create :empty_project } it 'returns ordered list of commits' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project expect(project.ci_commits.ordered).to eq([commit2, commit1]) end it 'returns commits ordered by committed_at and id, with nulls last' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project - commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project - commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project + commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1]) end end @@ -162,7 +161,7 @@ describe Ci::Commit, models: true do end describe :create_builds do - let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let!(:commit) { FactoryGirl.create :ci_commit, project: project } def create_builds(trigger_request = nil) commit.create_builds('master', false, nil, trigger_request) @@ -390,9 +389,8 @@ describe Ci::Commit, models: true do end describe "coverage" do - let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" } + let(:commit) { FactoryGirl.create :ci_commit, project: project } it "calculates average when there are two builds with coverage" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb deleted file mode 100644 index 7d54b6cf84c..00000000000 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe Ci::HipChatMessage, models: true do - subject { Ci::HipChatMessage.new(build) } - - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - - let(:build) do - commit.builds.first - end - - context 'when all matrix builds succeed' do - it 'returns a successful message' do - commit.create_builds('master', false, nil) - commit.builds.update_all(status: "success") - commit.reload - - expect(subject.status_color).to eq 'green' - expect(subject.notify?).to be_falsey - expect(subject.to_s).to match(/Commit #\d+/) - expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./) - end - end - - context 'when at least one matrix build fails' do - it 'returns a failure message' do - commit.create_builds('master', false, nil) - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect(subject.status_color).to eq 'red' - expect(subject.notify?).to be_truthy - expect(subject.to_s).to match(/Commit #\d+/) - expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./) - end - end -end diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb deleted file mode 100644 index 714f1e17e0b..00000000000 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - - -require 'spec_helper' - -describe Ci::HipChatService, models: true do - - describe "Validations" do - - context "active" do - before do - subject.active = true - end - - it { is_expected.to validate_presence_of :hipchat_room } - it { is_expected.to validate_presence_of :hipchat_token } - - end - end - - describe "Execute" do - - let(:service) { Ci::HipChatService.new } - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } - let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } - - before do - allow(service).to receive_messages( - project: commit.project, - project_id: commit.project_id, - notify_only_broken_builds: false, - hipchat_room: 123, - hipchat_token: 'a1b2c3d4e5f6' - ) - - WebMock.stub_request(:post, api_url) - end - - - it "should call the HipChat API" do - service.execute(build) - Ci::HipChatNotifierWorker.drain - - expect( WebMock ).to have_requested(:post, api_url).once - end - - it "calls the worker with expected arguments" do - expect( Ci::HipChatNotifierWorker ).to receive(:perform_async) \ - .with(an_instance_of(String), hash_including( - token: 'a1b2c3d4e5f6', - room: 123, - server: 'https://api.hipchat.com', - color: 'red', - notify: true - )) - - service.execute(build) - end - end -end diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb deleted file mode 100644 index 638d9a4a626..00000000000 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ /dev/null @@ -1,177 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Ci::MailService, models: true do - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Validations" do - context "active" do - before do - subject.active = true - end - end - end - - describe 'Sends email for' do - let(:mail) { Ci::MailService.new } - let(:user) { User.new(notification_email: 'git@example.com')} - - describe 'failed build' do - let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) - expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) - end - end - end - - describe 'successfull build' do - let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) } - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) - expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) - end - end - end - - describe 'successfull build and project has email_recipients' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: false, - email_recipients: "jeroen@example.com") - end - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2) - expect( - ActionMailer::Base.deliveries.map(&:to).flatten - ).to include("git@example.com", "jeroen@example.com") - end - end - end - - describe 'successful build and notify only broken builds' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: true, - email_recipients: "jeroen@example.com") - end - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect do - mail.execute(build) if mail.can_execute?(build) - end.to_not change{ ActionMailer::Base.deliveries.size } - end - end - end - - describe 'successful build and can test service' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: false, - email_recipients: "jeroen@example.com") - end - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } - - before do - allow(mail).to receive_messages( - project: project - ) - build - end - - it do - expect(mail.can_test?).to eq(true) - end - end - - describe 'retried build should not receive email' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: true, - email_recipients: "jeroen@example.com") - end - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - Ci::Build.retry(build) - perform_enqueued_jobs do - expect do - mail.execute(build) if mail.can_execute?(build) - end.to_not change{ ActionMailer::Base.deliveries.size } - end - end - end - end -end diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb deleted file mode 100644 index 226032b4cda..00000000000 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'spec_helper' - -describe Ci::SlackMessage, models: true do - subject { Ci::SlackMessage.new(commit) } - - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - - context 'when all matrix builds succeeded' do - let(:color) { 'good' } - - it 'returns a message with success' do - commit.create_builds('master', false, nil) - commit.builds.update_all(status: "success") - commit.reload - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Commit') - expect(subject.fallback).to include("\##{commit.id}") - expect(subject.fallback).to include('succeeded') - expect(subject.attachments.first[:fields]).to be_empty - end - end - - context 'when one of matrix builds failed' do - let(:color) { 'danger' } - - it 'returns a message with information about failed build' do - commit.create_builds('master', false, nil) - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Commit') - expect(subject.fallback).to include("\##{commit.id}") - expect(subject.fallback).to include('failed') - expect(subject.attachments.first[:fields].size).to eq(1) - expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name) - expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}") - end - end -end diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb deleted file mode 100644 index e7d7d5d6f4c..00000000000 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Ci::SlackService, models: true do - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Validations" do - context "active" do - before do - subject.active = true - end - - it { is_expected.to validate_presence_of :webhook } - end - end - - describe "Execute" do - let(:slack) { Ci::SlackService.new } - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } - let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } - let(:notify_only_broken_builds) { false } - - before do - allow(slack).to receive_messages( - project: commit.project, - project_id: commit.project_id, - webhook: webhook_url, - notify_only_broken_builds: notify_only_broken_builds - ) - - WebMock.stub_request(:post, webhook_url) - end - - it "should call Slack API" do - slack.execute(build) - Ci::SlackNotifierWorker.drain - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end -end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb deleted file mode 100644 index 346471aa9b5..00000000000 --- a/spec/models/ci/project_spec.rb +++ /dev/null @@ -1,246 +0,0 @@ -# == Schema Information -# -# Table name: ci_projects -# -# id :integer not null, primary key -# name :string(255) -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -require 'spec_helper' - -describe Ci::Project, models: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { project.gl_project } - subject { project } - - it { is_expected.to have_many(:runner_projects) } - it { is_expected.to have_many(:runners) } - it { is_expected.to have_many(:web_hooks) } - it { is_expected.to have_many(:events) } - it { is_expected.to have_many(:variables) } - it { is_expected.to have_many(:triggers) } - it { is_expected.to have_many(:services) } - - it { is_expected.to validate_presence_of :timeout } - it { is_expected.to validate_presence_of :gitlab_id } - - describe 'before_validation' do - it 'should set an random token if none provided' do - project = FactoryGirl.create :ci_project_without_token - expect(project.token).not_to eq("") - end - - it 'should not set an random toke if one provided' do - project = FactoryGirl.create :ci_project - expect(project.token).to eq("iPWx6WM4lhHNedGfBpPJNP") - end - end - - describe :name_with_namespace do - subject { project.name_with_namespace } - - it { is_expected.to eq(project.name) } - it { is_expected.to eq(gl_project.name_with_namespace) } - end - - describe :path_with_namespace do - subject { project.path_with_namespace } - - it { is_expected.to eq(project.path) } - it { is_expected.to eq(gl_project.path_with_namespace) } - end - - describe :path_with_namespace do - subject { project.web_url } - - it { is_expected.to eq(gl_project.web_url) } - end - - describe :web_url do - subject { project.web_url } - - it { is_expected.to eq(project.gitlab_url) } - it { is_expected.to eq(gl_project.web_url) } - end - - describe :http_url_to_repo do - subject { project.http_url_to_repo } - - it { is_expected.to eq(gl_project.http_url_to_repo) } - end - - describe :ssh_url_to_repo do - subject { project.ssh_url_to_repo } - - it { is_expected.to eq(gl_project.ssh_url_to_repo) } - end - - describe :commits do - subject { project.commits } - - before do - FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project - end - - it { is_expected.to eq(gl_project.ci_commits) } - end - - describe :builds do - subject { project.builds } - - before do - commit = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project - FactoryGirl.create :ci_build, commit: commit - end - - it { is_expected.to eq(gl_project.ci_builds) } - end - - describe "ordered_by_last_commit_date" do - it "returns ordered projects" do - newest_project = FactoryGirl.create :empty_project - newest_ci_project = newest_project.ensure_gitlab_ci_project - oldest_project = FactoryGirl.create :empty_project - oldest_ci_project = oldest_project.ensure_gitlab_ci_project - project_without_commits = FactoryGirl.create :empty_project - ci_project_without_commits = project_without_commits.ensure_gitlab_ci_project - - FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: newest_project - FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: oldest_project - - expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_ci_project, oldest_ci_project, ci_project_without_commits]) - end - end - - context :valid_project do - let(:commit) { FactoryGirl.create(:ci_commit) } - - context :project_with_commit_and_builds do - let(:project) { commit.project } - - before do - FactoryGirl.create(:ci_build, commit: commit) - end - - it { expect(project.status).to eq('pending') } - it { expect(project.last_commit).to be_kind_of(Ci::Commit) } - it { expect(project.human_status).to eq('pending') } - end - end - - describe '#email_notification?' do - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - expect(project.email_notification?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: 'test tesft' - expect(project.email_notification?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: '' - expect(project.email_notification?).to eq(false) - end - end - - describe '#broken_or_success?' do - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(true) - allow(project).to receive(:success?).and_return(true) - expect(project.broken_or_success?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(true) - allow(project).to receive(:success?).and_return(false) - expect(project.broken_or_success?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(false) - allow(project).to receive(:success?).and_return(true) - expect(project.broken_or_success?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(false) - allow(project).to receive(:success?).and_return(false) - expect(project.broken_or_success?).to eq(false) - end - end - - describe :repo_url_with_auth do - let(:project) { FactoryGirl.create :ci_project } - subject { project.repo_url_with_auth } - - it { is_expected.to be_a(String) } - it { is_expected.to end_with(".git") } - it { is_expected.to start_with(project.gitlab_url[0..6]) } - it { is_expected.to include(project.token) } - it { is_expected.to include('gitlab-ci-token') } - it { is_expected.to include(project.gitlab_url[7..-1]) } - end - - describe :any_runners do - it "there are no runners available" do - project = FactoryGirl.create(:ci_project) - expect(project.any_runners?).to be_falsey - end - - it "there is a specific runner" do - project = FactoryGirl.create(:ci_project) - project.runners << FactoryGirl.create(:ci_specific_runner) - expect(project.any_runners?).to be_truthy - end - - it "there is a shared runner" do - project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) - FactoryGirl.create(:ci_shared_runner) - expect(project.any_runners?).to be_truthy - end - - it "there is a shared runner, but they are prohibited to use" do - project = FactoryGirl.create(:ci_project) - FactoryGirl.create(:ci_shared_runner) - expect(project.any_runners?).to be_falsey - end - - it "checks the presence of specific runner" do - project = FactoryGirl.create(:ci_project) - specific_runner = FactoryGirl.create(:ci_specific_runner) - project.runners << specific_runner - expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy - end - - it "checks the presence of shared runner" do - project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) - shared_runner = FactoryGirl.create(:ci_shared_runner) - expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy - end - end -end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 6ebb5e86863..232760dfeba 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -38,7 +38,7 @@ describe Ci::Runner, models: true do end describe :assign_to do - let!(:project) { FactoryGirl.create :ci_project } + let!(:project) { FactoryGirl.create :empty_project } let!(:shared_runner) { FactoryGirl.create(:ci_shared_runner) } before { shared_runner.assign_to(project) } @@ -116,8 +116,8 @@ describe Ci::Runner, models: true do describe "belongs_to_one_project?" do it "returns false if there are two projects runner assigned to" do runner = FactoryGirl.create(:ci_specific_runner) - project = FactoryGirl.create(:ci_project) - project1 = FactoryGirl.create(:ci_project) + project = FactoryGirl.create(:empty_project) + project1 = FactoryGirl.create(:empty_project) project.runners << runner project1.runners << runner @@ -126,7 +126,7 @@ describe Ci::Runner, models: true do it "returns true" do runner = FactoryGirl.create(:ci_specific_runner) - project = FactoryGirl.create(:ci_project) + project = FactoryGirl.create(:empty_project) project.runners << runner expect(runner.belongs_to_one_project?).to be_truthy diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb deleted file mode 100644 index 34e3af7f810..00000000000 --- a/spec/models/ci/service_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Ci::Service, models: true do - - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Mass assignment" do - end - - describe "Test Button" do - before do - @service = Ci::Service.new - end - - describe "Testable" do - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - - before do - allow(@service).to receive_messages( - project: commit.project - ) - build - @testable = @service.can_test? - end - - describe :can_test do - it { expect(@testable).to eq(true) } - end - end - end -end diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 61eb3c08296..cb2f51e2011 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -13,7 +13,7 @@ require 'spec_helper' describe Ci::Trigger, models: true do - let(:project) { FactoryGirl.create :ci_project } + let(:project) { FactoryGirl.create :empty_project } describe 'before_validation' do it 'should set an random token if none provided' do diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb deleted file mode 100644 index 1a4edec9d4f..00000000000 --- a/spec/models/ci/web_hook_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# == Schema Information -# -# Table name: ci_web_hooks -# -# id :integer not null, primary key -# url :string(255) not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -require 'spec_helper' - -describe Ci::WebHook, models: true do - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Validations" do - it { is_expected.to validate_presence_of(:url) } - - context "url format" do - it { is_expected.to allow_value("http://example.com").for(:url) } - it { is_expected.to allow_value("https://excample.com").for(:url) } - it { is_expected.to allow_value("http://test.com/api").for(:url) } - it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) } - it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) } - - it { is_expected.not_to allow_value("example.com").for(:url) } - it { is_expected.not_to allow_value("ftp://example.com").for(:url) } - it { is_expected.not_to allow_value("herp-and-derp").for(:url) } - end - end - - describe "execute" do - before(:each) do - @web_hook = FactoryGirl.create(:ci_web_hook) - @project = @web_hook.project - @data = { before: 'oldrev', after: 'newrev', ref: 'ref' } - - WebMock.stub_request(:post, @web_hook.url) - end - - it "POSTs to the web hook URL" do - @web_hook.execute(@data) - expect(WebMock).to have_requested(:post, @web_hook.url).once - end - - it "POSTs the data as JSON" do - json = @data.to_json - - @web_hook.execute(@data) - expect(WebMock).to have_requested(:post, @web_hook.url).with(body: json).once - end - - it "catches exceptions" do - expect(Ci::WebHook).to receive(:post).and_raise("Some HTTP Post error") - - expect{ @web_hook.execute(@data) }. - to raise_error(RuntimeError, 'Some HTTP Post error') - end - end -end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 5e311ead28b..b8f901b3433 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -39,12 +39,13 @@ describe CommitStatus, models: true do it { is_expected.to belong_to(:commit) } it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:project) } + it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) } it { is_expected.to delegate_method(:sha).to(:commit) } it { is_expected.to delegate_method(:short_sha).to(:commit) } - it { is_expected.to delegate_method(:gl_project).to(:commit) } it { is_expected.to respond_to :success? } it { is_expected.to respond_to :failed? } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 5b6f177ebb2..216c7dabae0 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -159,4 +159,13 @@ describe Note, models: true do expect(note.editable?).to be_falsy end end + + describe "set_award!" do + let(:issue) { create :issue } + + it "converts aliases to actual name" do + note = create :note, note: ":thumbsup:", noteable: issue + expect(note.reload.note).to eq("+1") + end + end end diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb deleted file mode 100644 index 835bf364050..00000000000 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# - -require 'spec_helper' - -describe GitlabCiService, models: true do - describe 'associations' do - it { is_expected.to belong_to(:project) } - it { is_expected.to have_one(:service_hook) } - end - - describe 'commits methods' do - before do - @ci_project = create(:ci_project) - @service = GitlabCiService.new - allow(@service).to receive_messages( - service_hook: true, - project_url: 'http://ci.gitlab.org/projects/2', - token: 'verySecret', - project: @ci_project.gl_project - ) - end - - describe :build_page do - it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/builds")} - end - - describe "execute" do - let(:user) { create(:user, username: 'username') } - let(:project) { create(:project, name: 'project') } - let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } - - it "calls CreateCommitService" do - expect_any_instance_of(Ci::CreateCommitService).to receive(:execute).with(@ci_project, user, push_sample_data) - - @service.execute(push_sample_data) - end - end - end -end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index c96ab548149..a5662b08bda 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -247,6 +247,55 @@ describe HipchatService, models: true do end end + context 'build events' do + let(:build) { create(:ci_build) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + + context 'for failed' do + before { build.drop } + + it "should call Hipchat API" do + hipchat.execute(data) + + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should create a build message" do + message = hipchat.send(:create_build_message, data) + + project_url = project.web_url + project_name = project.name_with_namespace.gsub(/\s/, '') + sha = data[:sha] + ref = data[:ref] + ref_type = data[:tag] ? 'tag' : 'branch' + duration = data[:commit][:duration] + + expect(message).to eq("<a href=\"#{project_url}\">#{project_name}</a>: " \ + "Commit <a href=\"#{project_url}/commit/#{sha}/builds\">#{Commit.truncate_sha(sha)}</a> " \ + "of <a href=\"#{project_url}/commits/#{ref}\">#{ref}</a> #{ref_type} " \ + "by #{data[:commit][:author_name]} failed in #{duration} second(s)") + end + end + + context 'for succeeded' do + before do + build.success + end + + it "should call Hipchat API" do + hipchat.notify_only_broken_builds = false + hipchat.execute(data) + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should notify only broken" do + hipchat.notify_only_broken_builds = true + hipchat.execute(data) + expect(WebMock).to_not have_requested(:post, api_url).once + end + end + end + context "#message_options" do it "should be set to the defaults" do expect(hipchat.send(:message_options)).to eq({ notify: false, color: 'yellow' }) diff --git a/spec/models/project_services/slack_service/build_message_spec.rb b/spec/models/project_services/slack_service/build_message_spec.rb new file mode 100644 index 00000000000..621c83c0cda --- /dev/null +++ b/spec/models/project_services/slack_service/build_message_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe SlackService::BuildMessage do + subject { SlackService::BuildMessage.new(args) } + + let(:args) do + { + sha: '97de212e80737a608d939f648d959671fb0a0142', + ref: 'develop', + tag: false, + + project_name: 'project_name', + project_url: 'somewhere.com', + + commit: { + status: status, + author_name: 'hacker', + duration: 10, + }, + } + end + + context 'succeeded' do + let(:status) { 'success' } + let(:color) { 'good' } + + it 'returns a message with information about succeeded build' do + message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker passed in 10 second(s)' + expect(subject.pretext).to be_empty + expect(subject.fallback).to eq(message) + expect(subject.attachments).to eq([text: message, color: color]) + end + end + + context 'failed' do + let(:status) { 'failed' } + let(:color) { 'danger' } + + it 'returns a message with information about failed build' do + message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 10 second(s)' + expect(subject.pretext).to be_empty + expect(subject.fallback).to eq(message) + expect(subject.attachments).to eq([text: message, color: color]) + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6ddb0e2b8f7..87582e07494 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -54,6 +54,13 @@ describe Project, models: true do it { is_expected.to have_one(:slack_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(:commit_statuses) } + it { is_expected.to have_many(:ci_commits) } + it { is_expected.to have_many(:builds) } + it { is_expected.to have_many(:runner_projects) } + it { is_expected.to have_many(:runners) } + it { is_expected.to have_many(:variables) } + it { is_expected.to have_many(:triggers) } end describe 'modules' do @@ -88,6 +95,18 @@ describe Project, models: true do expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/) end end + + describe 'project token' do + it 'should set an random token if none provided' do + project = FactoryGirl.create :empty_project, runners_token: '' + expect(project.runners_token).not_to eq('') + end + + it 'should not set an random toke if one provided' do + project = FactoryGirl.create :empty_project, runners_token: 'my-token' + expect(project.runners_token).to eq('my-token') + end + end describe 'Respond to' do it { is_expected.to respond_to(:url_to_repo) } @@ -395,12 +414,7 @@ describe Project, models: true do describe :ci_commit do let(:project) { create :project } - let(:commit) { create :ci_commit, gl_project: project } - - before do - project.ensure_gitlab_ci_project - project.create_gitlab_ci_service(active: true) - end + let(:commit) { create :ci_commit, project: project } it { expect(project.ci_commit(commit.sha)).to eq(commit) } end @@ -412,9 +426,7 @@ describe Project, models: true do subject { project.builds_enabled } - it { is_expected.to eq(project.gitlab_ci_service.active) } it { expect(project.builds_enabled?).to be_truthy } - it { expect(project.gitlab_ci_project).to be_a(Ci::Project) } end describe '.trending' do @@ -475,4 +487,65 @@ describe Project, models: true do it { is_expected.to eq([]) } end end + + context 'shared runners by default' do + let(:project) { create(:empty_project) } + + subject { project.shared_runners_enabled } + + context 'are enabled' do + before { stub_application_setting(shared_runners_enabled: true) } + + it { is_expected.to be_truthy } + end + + context 'are disabled' do + before { stub_application_setting(shared_runners_enabled: false) } + + it { is_expected.to be_falsey } + end + end + + describe :any_runners do + let(:project) { create(:empty_project, shared_runners_enabled: shared_runners_enabled) } + let(:specific_runner) { create(:ci_specific_runner) } + let(:shared_runner) { create(:ci_shared_runner) } + + context 'for shared runners disabled' do + let(:shared_runners_enabled) { false } + + it 'there are no runners available' do + expect(project.any_runners?).to be_falsey + end + + it 'there is a specific runner' do + project.runners << specific_runner + expect(project.any_runners?).to be_truthy + end + + it 'there is a shared runner, but they are prohibited to use' do + shared_runner + expect(project.any_runners?).to be_falsey + end + + it 'checks the presence of specific runner' do + project.runners << specific_runner + expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy + end + end + + context 'for shared runners enabled' do + let(:shared_runners_enabled) { true } + + it 'there is a shared runner' do + shared_runner + expect(project.any_runners?).to be_truthy + end + + it 'checks the presence of shared runner' do + shared_runner + expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy + end + end + end end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index 606b226ad77..142b637d291 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -1,11 +1,17 @@ require 'spec_helper' -describe API::API, 'ProjectHooks', api: true do +describe API::API, 'ProjectHooks', api: true do include ApiHelpers let(:user) { create(:user) } let(:user3) { create(:user) } let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } - let!(:hook) { create(:project_hook, project: project, url: "http://example.com", push_events: true, merge_requests_events: true, tag_push_events: true, issues_events: true, note_events: true, enable_ssl_verification: true) } + let!(:hook) do + create(:project_hook, + project: project, url: "http://example.com", + push_events: true, merge_requests_events: true, tag_push_events: true, + issues_events: true, note_events: true, build_events: true, + enable_ssl_verification: true) + end before do project.team << [user, :master] @@ -26,6 +32,7 @@ describe API::API, 'ProjectHooks', api: true do expect(json_response.first['merge_requests_events']).to eq(true) expect(json_response.first['tag_push_events']).to eq(true) expect(json_response.first['note_events']).to eq(true) + expect(json_response.first['build_events']).to eq(true) expect(json_response.first['enable_ssl_verification']).to eq(true) end end @@ -83,6 +90,7 @@ describe API::API, 'ProjectHooks', api: true do expect(json_response['merge_requests_events']).to eq(false) expect(json_response['tag_push_events']).to eq(false) expect(json_response['note_events']).to eq(false) + expect(json_response['build_events']).to eq(false) expect(json_response['enable_ssl_verification']).to eq(true) end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 24b765f4979..e784b7d1f2d 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -86,18 +86,6 @@ describe API::API, api: true do expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project3.id) end - - it 'returns projects in the correct order when ci_enabled_first parameter is passed' do - [project, project2, project3].each do |project| - project.builds_enabled = false - project.build_missing_services - end - project2.builds_enabled = true - get api('/projects', user), { ci_enabled_first: 'true' } - expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(json_response.first['id']).to eq(project2.id) - end end end end diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb new file mode 100644 index 00000000000..314bd7ddc59 --- /dev/null +++ b/spec/requests/api/triggers_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + + describe 'POST /projects/:project_id/trigger' do + let!(:trigger_token) { 'secure token' } + let!(:project) { FactoryGirl.create(:project) } + let!(:project2) { FactoryGirl.create(:empty_project) } + let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } + let(:options) do + { + token: trigger_token + } + end + + before do + stub_ci_commit_to_return_yaml_file + end + + context 'Handles errors' do + it 'should return bad request if token is missing' do + post api("/projects/#{project.id}/trigger/builds"), ref: 'master' + expect(response.status).to eq(400) + end + + it 'should return not found if project is not found' do + post api('/projects/0/trigger/builds'), options.merge(ref: 'master') + expect(response.status).to eq(404) + end + + it 'should return unauthorized if token is for different project' do + post api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master') + expect(response.status).to eq(401) + end + end + + context 'Have a commit' do + let(:commit) { project.ci_commits.last } + + it 'should create builds' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master') + expect(response.status).to eq(201) + commit.builds.reload + expect(commit.builds.size).to eq(2) + end + + it 'should return bad request with no builds created if there\'s no commit for that ref' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') + expect(response.status).to eq(400) + expect(json_response['message']).to eq('No builds created') + end + + context 'Validates variables' do + let(:variables) do + { 'TRIGGER_KEY' => 'TRIGGER_VALUE' } + end + + it 'should validate variables to be a hash' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master') + expect(response.status).to eq(400) + expect(json_response['message']).to eq('variables needs to be a hash') + end + + it 'should validate variables needs to be a map of key-valued strings' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master') + expect(response.status).to eq(400) + expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') + end + + it 'create trigger request with variables' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master') + expect(response.status).to eq(201) + commit.builds.reload + expect(commit.builds.first.trigger_request.variables).to eq(variables) + end + end + end + end +end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index c2be045099d..c27e87c4acc 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -4,8 +4,7 @@ describe Ci::API::API do include ApiHelpers let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } - let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { project.gl_project } + let(:project) { FactoryGirl.create(:empty_project) } before do stub_ci_commit_to_return_yaml_file @@ -13,16 +12,15 @@ describe Ci::API::API do describe "Builds API for runners" do let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") } - let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } - let(:shared_gl_project) { shared_project.gl_project } + let(:shared_project) { FactoryGirl.create(:empty_project, name: "SharedProject") } before do - FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id + FactoryGirl.create :ci_runner_project, project: project, runner: runner end describe "POST /builds/register" do it "should start a build" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) build = commit.builds.first @@ -40,7 +38,7 @@ describe Ci::API::API do end it "should return 404 error if no builds for specific runner" do - commit = FactoryGirl.create(:ci_commit, gl_project: shared_gl_project) + commit = FactoryGirl.create(:ci_commit, project: shared_project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending') post ci_api("/builds/register"), token: runner.token @@ -49,7 +47,7 @@ describe Ci::API::API do end it "should return 404 error if no builds for shared runner" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending') post ci_api("/builds/register"), token: shared_runner.token @@ -58,7 +56,7 @@ describe Ci::API::API do end it "returns options" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -68,7 +66,7 @@ describe Ci::API::API do end it "returns variables" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") @@ -85,7 +83,7 @@ describe Ci::API::API do it "returns variables for triggers" do trigger = FactoryGirl.create(:ci_trigger, project: project) - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) commit.create_builds('master', false, nil, trigger_request) @@ -106,7 +104,7 @@ describe Ci::API::API do end describe "PUT /builds/:id" do - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project)} + let(:commit) { FactoryGirl.create(:ci_commit, project: project)} let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } it "should update a running build" do @@ -126,14 +124,14 @@ describe Ci::API::API do context "Artifacts" do let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") } let(:post_url) { ci_api("/builds/#{build.id}/artifacts") } let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") } let(:get_url) { ci_api("/builds/#{build.id}/artifacts") } let(:headers) { { "GitLab-Workhorse" => "1.0" } } - let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.project.token) } + let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) } describe "POST /builds/:id/artifacts/authorize" do context "should authorize posting artifact to running build" do @@ -142,7 +140,7 @@ describe Ci::API::API do end it "using token as parameter" do - post authorize_url, { token: build.project.token }, headers + post authorize_url, { token: build.token }, headers expect(response.status).to eq(200) expect(json_response["TempPath"]).to_not be_nil end @@ -161,7 +159,7 @@ describe Ci::API::API do it "using token as parameter" do stub_application_setting(max_artifacts_size: 0) - post authorize_url, { token: build.project.token, filesize: 100 }, headers + post authorize_url, { token: build.token, filesize: 100 }, headers expect(response.status).to eq(413) end @@ -241,7 +239,7 @@ describe Ci::API::API do end it do - post post_url, { token: build.project.token }, {} + post post_url, { token: build.token }, {} expect(response.status).to eq(403) end end @@ -281,12 +279,12 @@ describe Ci::API::API do describe "DELETE /builds/:id/artifacts" do before do build.run! - post delete_url, token: build.project.token, file: file_upload + post delete_url, token: build.token, file: file_upload end it "should delete artifact build" do build.success - delete delete_url, token: build.project.token + delete delete_url, token: build.token expect(response.status).to eq(200) end end @@ -298,12 +296,12 @@ describe Ci::API::API do it "should download artifact" do build.update_attributes(artifacts_file: file_upload) - get get_url, token: build.project.token + get get_url, token: build.token expect(response.status).to eq(200) end it "should fail to download if no artifact uploaded" do - get get_url, token: build.project.token + get get_url, token: build.token expect(response.status).to eq(404) end end diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb deleted file mode 100644 index aa51ba95bca..00000000000 --- a/spec/requests/ci/api/commits_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'spec_helper' - -describe Ci::API::API, 'Commits' do - include ApiHelpers - - let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { project.gl_project } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - - let(:options) do - { - project_token: project.token, - project_id: project.id - } - end - - describe "GET /commits" do - before { commit } - - it "should return commits per project" do - get ci_api("/commits"), options - - expect(response.status).to eq(200) - expect(json_response.count).to eq(1) - expect(json_response.first["project_id"]).to eq(project.id) - expect(json_response.first["sha"]).to eq(commit.sha) - end - end - - describe "POST /commits" do - let(:data) do - { - "before" => "95790bf891e76fee5e1747ab589903a6a1f80f22", - "after" => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "ref" => "refs/heads/master", - "commits" => [ - { - "id" => "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "message" => "Update Catalan translation to e38cb41.", - "timestamp" => "2011-12-12T14:27:31+02:00", - "url" => "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "author" => { - "name" => "Jordi Mallach", - "email" => "jordi@softcatala.org", - } - } - ] - } - end - - it "should create a build" do - post ci_api("/commits"), options.merge(data: data) - - expect(response.status).to eq(201) - expect(json_response['sha']).to eq("da1560886d4f094c3e6c9ef40349f7d38b5d27d7") - end - - it "should return 400 error if no data passed" do - post ci_api("/commits"), options - - expect(response.status).to eq(400) - expect(json_response['message']).to eq("400 (Bad request) \"data\" not given") - end - end -end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb deleted file mode 100644 index 893fd168d3e..00000000000 --- a/spec/requests/ci/api/projects_spec.rb +++ /dev/null @@ -1,232 +0,0 @@ -require 'spec_helper' - -describe Ci::API::API do - include ApiHelpers - - let(:gitlab_url) { GitlabCi.config.gitlab_ci.url } - let(:user) { create(:user) } - let(:private_token) { user.private_token } - - let(:options) do - { - private_token: private_token, - url: gitlab_url - } - end - - before do - stub_gitlab_calls - end - - context "requests for scoped projects" do - # NOTE: These ids are tied to the actual projects on demo.gitlab.com - describe "GET /projects" do - let!(:project1) { FactoryGirl.create(:ci_project) } - let!(:project2) { FactoryGirl.create(:ci_project) } - - before do - project1.gl_project.team << [user, :developer] - project2.gl_project.team << [user, :developer] - end - - it "should return all projects on the CI instance" do - get ci_api("/projects"), options - expect(response.status).to eq(200) - expect(json_response.count).to eq(2) - expect(json_response.first["id"]).to eq(project1.id) - expect(json_response.last["id"]).to eq(project2.id) - end - end - - describe "GET /projects/owned" do - let!(:gl_project1) {FactoryGirl.create(:empty_project, namespace: user.namespace)} - let!(:gl_project2) {FactoryGirl.create(:empty_project, namespace: user.namespace)} - let!(:project1) { gl_project1.ensure_gitlab_ci_project } - let!(:project2) { gl_project2.ensure_gitlab_ci_project } - - before do - project1.gl_project.team << [user, :developer] - project2.gl_project.team << [user, :developer] - end - - it "should return all projects on the CI instance" do - get ci_api("/projects/owned"), options - - expect(response.status).to eq(200) - expect(json_response.count).to eq(2) - end - end - end - - describe "POST /projects/:project_id/webhooks" do - let!(:project) { FactoryGirl.create(:ci_project) } - - context "Valid Webhook URL" do - let!(:webhook) { { web_hook: "http://example.com/sth/1/ala_ma_kota" } } - - before do - options.merge!(webhook) - end - - it "should create webhook for specified project" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(201) - expect(json_response["url"]).to eq(webhook[:web_hook]) - end - - it "fails to create webhook for non existsing project" do - post ci_api("/projects/non-existant-id/webhooks"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(401) - end - end - - context "Invalid Webhook URL" do - let!(:webhook) { { web_hook: "ala_ma_kota" } } - - before do - options.merge!(webhook) - end - - it "fails to create webhook for not valid url" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(400) - end - end - - context "Missed web_hook parameter" do - it "fails to create webhook for not provided url" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(400) - end - end - end - - describe "GET /projects/:id" do - let!(:project) { FactoryGirl.create(:ci_project) } - - before do - project.gl_project.team << [user, :developer] - end - - context "with an existing project" do - it "should retrieve the project info" do - get ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(200) - expect(json_response['id']).to eq(project.id) - end - end - - context "with a non-existing project" do - it "should return 404 error if project not found" do - get ci_api("/projects/non_existent_id"), options - expect(response.status).to eq(404) - end - end - end - - describe "PUT /projects/:id" do - let!(:project) { FactoryGirl.create(:ci_project) } - let!(:project_info) { { default_ref: "develop" } } - - before do - options.merge!(project_info) - end - - it "should update a specific project's information" do - project.gl_project.team << [user, :master] - put ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(200) - expect(json_response["default_ref"]).to eq(project_info[:default_ref]) - end - - it "fails to update a non-existing project" do - put ci_api("/projects/non-existant-id"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - put ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(401) - end - end - - describe "DELETE /projects/:id" do - let!(:project) { FactoryGirl.create(:ci_project) } - - it "should delete a specific project" do - project.gl_project.team << [user, :master] - delete ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(200) - expect { project.reload }. - to raise_error(ActiveRecord::RecordNotFound) - end - - it "non-manager is not authorized" do - delete ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(401) - end - - it "is getting not found error" do - delete ci_api("/projects/not-existing_id"), options - expect(response.status).to eq(404) - end - end - - describe "POST /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:ci_project) } - let(:runner) { FactoryGirl.create(:ci_runner) } - - it "should add the project to the runner" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(201) - - project.reload - expect(project.runners.first.id).to eq(runner.id) - end - - it "should fail if it tries to link a non-existing project or runner" do - post ci_api("/projects/#{project.id}/runners/non-existing"), options - expect(response.status).to eq(404) - - post ci_api("/projects/non-existing/runners/#{runner.id}"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(401) - end - end - - describe "DELETE /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:ci_project) } - let(:runner) { FactoryGirl.create(:ci_runner) } - - it "should remove the project from the runner" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - - expect(project.runners).to be_present - delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(200) - - project.reload - expect(project.runners).to be_empty - end - - it "non-manager is not authorized" do - delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(401) - end - end -end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index ef40d757665..567da013e6f 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -12,29 +12,6 @@ describe Ci::API::API do stub_application_setting(runners_registration_token: registration_token) end - describe "GET /runners" do - let(:gitlab_url) { GitlabCi.config.gitlab_ci.url } - let(:private_token) { create(:user).private_token } - let(:options) do - { - private_token: private_token, - url: gitlab_url - } - end - - before do - 5.times { FactoryGirl.create(:ci_runner) } - end - - it "should retrieve a list of all runners" do - get ci_api("/runners", nil), options - expect(response.status).to eq(200) - expect(json_response.count).to eq(5) - expect(json_response.last).to have_key("id") - expect(json_response.last).to have_key("token") - end - end - describe "POST /runners/register" do describe "should create a runner if token provided" do before { post ci_api("/runners/register"), token: registration_token } @@ -57,8 +34,8 @@ describe Ci::API::API do end describe "should create a runner if project token provided" do - let(:project) { FactoryGirl.create(:ci_project) } - before { post ci_api("/runners/register"), token: project.token } + let(:project) { FactoryGirl.create(:empty_project) } + before { post ci_api("/runners/register"), token: project.runners_token } it { expect(response.status).to eq(201) } it { expect(project.runners.size).to eq(1) } diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index a2b436d5811..0ef03f9371b 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,9 +5,8 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } - let!(:gl_project) { FactoryGirl.create(:project) } - let!(:project) { gl_project.ensure_gitlab_ci_project } - let!(:project2) { FactoryGirl.create(:ci_project) } + let!(:project) { FactoryGirl.create(:project, ci_id: 10) } + let!(:project2) { FactoryGirl.create(:empty_project, ci_id: 11) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) do { @@ -21,7 +20,7 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do - post ci_api("/projects/#{project.id}/refs/master/trigger") + post ci_api("/projects/#{project.ci_id}/refs/master/trigger") expect(response.status).to eq(400) end @@ -31,23 +30,23 @@ describe Ci::API::API do end it 'should return unauthorized if token is for different project' do - post ci_api("/projects/#{project2.id}/refs/master/trigger"), options + post ci_api("/projects/#{project2.ci_id}/refs/master/trigger"), options expect(response.status).to eq(401) end end context 'Have a commit' do - let(:commit) { project.commits.last } + let(:commit) { project.ci_commits.last } it 'should create builds' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do - post ci_api("/projects/#{project.id}/refs/other-branch/trigger"), options + post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options expect(response.status).to eq(400) expect(json_response['message']).to eq('No builds created') end @@ -58,19 +57,19 @@ describe Ci::API::API do end it 'should validate variables to be a hash' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value') expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: variables) expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.first.trigger_request.variables).to eq(variables) diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb deleted file mode 100644 index c2fafca2ad2..00000000000 --- a/spec/services/ci/create_commit_service_spec.rb +++ /dev/null @@ -1,172 +0,0 @@ -require 'spec_helper' - -module Ci - describe CreateCommitService, services: true do - let(:service) { CreateCommitService.new } - let(:project) { FactoryGirl.create(:ci_project) } - let(:user) { nil } - - before do - stub_ci_commit_to_return_yaml_file - end - - describe :execute do - context 'valid params' do - let(:commit) do - service.execute(project, user, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: [ { message: "Message" } ] - ) - end - - it { expect(commit).to be_kind_of(Commit) } - it { expect(commit).to be_valid } - it { expect(commit).to be_persisted } - it { expect(commit).to eq(project.commits.last) } - it { expect(commit.builds.first).to be_kind_of(Build) } - end - - context "skip tag if there is no build for it" do - it "creates commit if there is appropriate job" do - result = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: [ { message: "Message" } ] - ) - expect(result).to be_persisted - end - - it "creates commit if there is no appropriate job but deploy job has right ref setting" do - config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) - stub_ci_commit_yaml_file(config) - - result = service.execute(project, user, - ref: 'refs/heads/0_1', - before: '00000000', - after: '31das312', - commits: [ { message: "Message" } ] - ) - expect(result).to be_persisted - end - end - - it 'skips commits without .gitlab-ci.yml' do - stub_ci_commit_yaml_file(nil) - result = service.execute(project, user, - ref: 'refs/heads/0_1', - before: '00000000', - after: '31das312', - commits: [ { message: 'Message' } ] - ) - expect(result).to be_persisted - expect(result.builds.any?).to be_falsey - expect(result.status).to eq('skipped') - expect(result.yaml_errors).to be_nil - end - - it 'skips commits if yaml is invalid' do - message = 'message' - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } - stub_ci_commit_yaml_file('invalid: file: file') - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq('failed') - expect(commit.yaml_errors).to_not be_nil - end - - describe :ci_skip? do - let(:message) { "some message[ci skip]" } - - before do - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } - end - - it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq("skipped") - end - - it "does not skips builds creation if there is no [ci skip] tag in commit message" do - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" } - - commits = [{ message: "some message" }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - - expect(commit.builds.first.name).to eq("staging") - end - - it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - stub_ci_commit_yaml_file('invalid: file: fiile') - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq("skipped") - expect(commit.yaml_errors).to be_nil - end - end - - it "skips build creation if there are already builds" do - allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } - - commits = [{ message: "message" }] - commit = service.execute(project, user, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.count(:all)).to eq(2) - - commit = service.execute(project, user, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.count(:all)).to eq(2) - end - - it "creates commit with failed status if yaml is invalid" do - stub_ci_commit_yaml_file('invalid: file') - - commits = [{ message: "some message" }] - - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - - expect(commit.status).to eq("failed") - expect(commit.builds.any?).to be false - end - end - end -end diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index c80cb58163a..dbdc5370bd8 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -2,8 +2,7 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService, services: true do let(:service) { Ci::CreateTriggerRequestService.new } - let(:gl_project) { create(:project) } - let(:project) { gl_project.ensure_gitlab_ci_project } + let(:project) { create(:project) } let(:trigger) { create(:ci_trigger, project: project) } before do @@ -29,7 +28,7 @@ describe Ci::CreateTriggerRequestService, services: true do before do stub_ci_commit_yaml_file('{}') - FactoryGirl.create :ci_commit, gl_project: gl_project + FactoryGirl.create :ci_commit, project: project end it { expect(subject).to be_nil } diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb deleted file mode 100644 index 32516c75cf3..00000000000 --- a/spec/services/ci/event_service_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'spec_helper' - -describe Ci::EventService, services: true do - let(:project) { FactoryGirl.create :ci_project } - let(:user) { double(username: "root", id: 1) } - - before do - Event.destroy_all - end - - describe :remove_project do - it "creates event" do - Ci::EventService.new.remove_project(user, project) - - expect(Ci::Event.admin.last.description).to eq("Project \"#{project.name_with_namespace}\" has been removed by root") - end - end - - describe :create_project do - it "creates event" do - Ci::EventService.new.create_project(user, project) - - expect(Ci::Event.admin.last.description).to eq("Project \"#{project.name_with_namespace}\" has been created by root") - end - end - - describe :change_project_settings do - it "creates event" do - Ci::EventService.new.change_project_settings(user, project) - - expect(Ci::Event.last.description).to eq("User \"root\" updated projects settings") - end - end -end diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index b43cabb4ee4..870861ad20a 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -3,16 +3,16 @@ require 'spec_helper' module Ci describe ImageForBuildService, services: true do let(:service) { ImageForBuildService.new } - let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { FactoryGirl.create(:project, gitlab_ci_project: project) } - let(:commit_sha) { gl_project.commit('master').sha } - let(:commit) { gl_project.ensure_ci_commit(commit_sha) } + let(:project) { FactoryGirl.create(:empty_project) } + let(:commit_sha) { '01234567890123456789' } + let(:commit) { project.ensure_ci_commit(commit_sha) } let(:build) { FactoryGirl.create(:ci_build, commit: commit) } describe :execute do before { build } context 'branch name' do + before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) } before { build.run! } let(:image) { service.execute(project, ref: 'master') } diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 379e07982fb..e81f9e757ac 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' module Ci describe RegisterBuildService, services: true do let!(:service) { RegisterBuildService.new } - let!(:gl_project) { FactoryGirl.create :empty_project } - let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let!(:project) { FactoryGirl.create :empty_project, shared_runners_enabled: false } + let!(:commit) { FactoryGirl.create :ci_commit, project: project } let!(:pending_build) { FactoryGirl.create :ci_build, commit: commit } let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) } let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) } before do - specific_runner.assign_to(gl_project.ensure_gitlab_ci_project) + specific_runner.assign_to(project) end describe :execute do @@ -47,7 +47,7 @@ module Ci context 'allow shared runners' do before do - gl_project.gitlab_ci_project.update(shared_runners_enabled: true) + project.update(shared_runners_enabled: true) end context 'shared runner' do @@ -71,7 +71,7 @@ module Ci context 'disallow shared runners' do before do - gl_project.gitlab_ci_project.update(shared_runners_enabled: false) + project.update(shared_runners_enabled: false) end context 'shared runner' do diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb deleted file mode 100644 index e7d8ab30652..00000000000 --- a/spec/services/ci/web_hook_service_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'spec_helper' - -describe Ci::WebHookService, services: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - let(:hook) { FactoryGirl.create :ci_web_hook, project: project } - - describe :execute do - it "should execute successfully" do - stub_request(:post, hook.url).to_return(status: 200) - expect(Ci::WebHookService.new.build_end(build)).to be_truthy - end - end - - context 'build_data' do - it "contains all needed fields" do - expect(build_data(build)).to include( - :build_id, - :project_id, - :ref, - :build_status, - :build_started_at, - :build_finished_at, - :before_sha, - :project_name, - :gitlab_url, - :build_name - ) - end - end - - def build_data(build) - Ci::WebHookService.new.send :build_data, build - end -end diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb new file mode 100644 index 00000000000..798c480b81a --- /dev/null +++ b/spec/services/create_commit_builds_service_spec.rb @@ -0,0 +1,170 @@ +require 'spec_helper' + +describe CreateCommitBuildsService, services: true do + let(:service) { CreateCommitBuildsService.new } + let(:project) { FactoryGirl.create(:empty_project) } + let(:user) { nil } + + before do + stub_ci_commit_to_return_yaml_file + end + + describe :execute do + context 'valid params' do + let(:commit) do + service.execute(project, user, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: [{ message: "Message" }] + ) + end + + it { expect(commit).to be_kind_of(Ci::Commit) } + it { expect(commit).to be_valid } + it { expect(commit).to be_persisted } + it { expect(commit).to eq(project.ci_commits.last) } + it { expect(commit.builds.first).to be_kind_of(Ci::Build) } + end + + context "skip tag if there is no build for it" do + it "creates commit if there is appropriate job" do + result = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: [{ message: "Message" }] + ) + expect(result).to be_persisted + end + + it "creates commit if there is no appropriate job but deploy job has right ref setting" do + config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) + stub_ci_commit_yaml_file(config) + + result = service.execute(project, user, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + commits: [{ message: "Message" }] + ) + expect(result).to be_persisted + end + end + + it 'skips commits without .gitlab-ci.yml' do + stub_ci_commit_yaml_file(nil) + result = service.execute(project, user, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + commits: [{ message: 'Message' }] + ) + expect(result).to be_persisted + expect(result.builds.any?).to be_falsey + expect(result.status).to eq('skipped') + expect(result.yaml_errors).to be_nil + end + + it 'skips commits if yaml is invalid' do + message = 'message' + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + stub_ci_commit_yaml_file('invalid: file: file') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq('failed') + expect(commit.yaml_errors).to_not be_nil + end + + describe :ci_skip? do + let(:message) { "some message[ci skip]" } + + before do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + end + + it "skips builds creation if there is [ci skip] tag in commit message" do + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + end + + it "does not skips builds creation if there is no [ci skip] tag in commit message" do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" } + + commits = [{ message: "some message" }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(commit.builds.first.name).to eq("staging") + end + + it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do + stub_ci_commit_yaml_file('invalid: file: fiile') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + expect(commit.yaml_errors).to be_nil + end + end + + it "skips build creation if there are already builds" do + allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } + + commits = [{ message: "message" }] + commit = service.execute(project, user, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.count(:all)).to eq(2) + + commit = service.execute(project, user, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.count(:all)).to eq(2) + end + + it "creates commit with failed status if yaml is invalid" do + stub_ci_commit_yaml_file('invalid: file') + + commits = [{ message: "some message" }] + + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(commit.status).to eq("failed") + expect(commit.builds.any?).to be false + end + end +end diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb index 188fda6211f..449cecaa789 100644 --- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -11,7 +11,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do end let(:project) { create(:project) } - let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, gl_project: project) } + let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) } let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') } describe "#execute" do diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index 33d2b14583c..fce91015fd4 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -4,7 +4,7 @@ # - let(:backref_text) { "the way that +subject+ should refer to itself in backreferences " } # - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } } -def common_mentionable_setup +shared_context 'mentionable context' do let(:project) { subject.project } let(:author) { subject.author } @@ -56,7 +56,7 @@ def common_mentionable_setup end shared_examples 'a mentionable' do - common_mentionable_setup + include_context 'mentionable context' it 'generates a descriptive back-reference' do expect(subject.gfm_reference).to eq(backref_text) @@ -88,7 +88,7 @@ shared_examples 'a mentionable' do end shared_examples 'an editable mentionable' do - common_mentionable_setup + include_context 'mentionable context' it_behaves_like 'a mentionable' diff --git a/spec/workers/build_email_worker_spec.rb b/spec/workers/build_email_worker_spec.rb new file mode 100644 index 00000000000..98deae0a588 --- /dev/null +++ b/spec/workers/build_email_worker_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe BuildEmailWorker do + include RepoHelpers + + let(:build) { create(:ci_build) } + let(:user) { create(:user) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + + subject { BuildEmailWorker.new } + + before do + allow(build).to receive(:execute_hooks).and_return(false) + build.success + end + + describe "#perform" do + it "sends mail" do + subject.perform(build.id, [user.email], data.stringify_keys) + + email = ActionMailer::Base.deliveries.last + expect(email.subject).to include('Build success for') + expect(email.to).to eq([user.email]) + end + + it "gracefully handles an input SMTP error" do + ActionMailer::Base.deliveries.clear + allow(Notify).to receive(:build_success_email).and_raise(Net::SMTPFatalError) + + subject.perform(build.id, [user.email], data.stringify_keys) + + expect(ActionMailer::Base.deliveries.count).to eq(0) + end + end +end |