diff --git a/Gemfile b/Gemfile index 0d97a74990c5c..85077f37fd150 100644 --- a/Gemfile +++ b/Gemfile @@ -82,7 +82,7 @@ gem "seed-fu" # Markdown to HTML gem "redcarpet", "~> 2.2.2" -gem "github-markup", "~> 0.7.4", require: 'github/markup', git: 'https://github.com/gitlabhq/markup.git', ref: '61ade389c1e1c159359338f570d18464a44ddbc4' +gem "github-markup", "~> 0.7.4", require: 'github/markup', git: 'https://github.com/gitlabhq/markup.git', ref: '61ade389c1e1c159359338f570d18464a44ddbc4' # Asciidoc to HTML gem "asciidoctor" @@ -128,6 +128,9 @@ gem "hipchat", "~> 0.14.0" # Flowdock integration gem "gitlab-flowdock-git-hook", "~> 0.4.2" +# Gemnasium integration +gem "gemnasium-gitlab-service", "~> 0.2" + # d3 gem "d3_rails", "~> 3.1.4" diff --git a/Gemfile.lock b/Gemfile.lock index a501aefcae2b5..734339333816b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -151,6 +151,8 @@ GEM dotenv (>= 0.7) thor (>= 0.13.6) formatador (0.2.4) + gemnasium-gitlab-service (0.2.1) + rugged (~> 0.19) gemoji (1.3.1) gherkin-ruby (0.3.1) racc @@ -423,6 +425,7 @@ GEM ruby-hmac (0.4.0) ruby-progressbar (1.2.0) rubyntlm (0.1.1) + rugged (0.19.0) safe_yaml (0.9.7) sanitize (2.0.6) nokogiri (>= 1.4.4) @@ -576,6 +579,7 @@ DEPENDENCIES fog (~> 1.3.1) font-awesome-rails (~> 3.2) foreman + gemnasium-gitlab-service (~> 0.2) gemoji (~> 1.3.0) github-markup (~> 0.7.4)! gitlab-flowdock-git-hook (~> 0.4.2) diff --git a/app/models/project.rb b/app/models/project.rb index da5f25f3cbc1d..7436e7636337c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -53,6 +53,7 @@ class Project < ActiveRecord::Base has_one :hipchat_service, dependent: :destroy has_one :flowdock_service, dependent: :destroy has_one :assembla_service, dependent: :destroy + has_one :gemnasium_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link @@ -256,7 +257,7 @@ def build_missing_services end def available_services_names - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push) + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium) end def gitlab_ci? diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index 66ecf394784d6..b89ea54a2aae6 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # class AssemblaService < Service diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index fb2a49fd586ed..f9247e054c7df 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # class CampfireService < Service diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index 2a46eff7846ed..0a4531663425f 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # class EmailsOnPushService < Service diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index f72d9fa90151c..2603a1f67a47b 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # require "flowdock-git-hook" diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb new file mode 100644 index 0000000000000..0b8e7bad353bd --- /dev/null +++ b/app/models/project_services/gemnasium_service.rb @@ -0,0 +1,54 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean default(FALSE), not null +# project_url :string(255) +# subdomain :string(255) +# room :string(255) +# api_key :string(255) +# + +require "gemnasium/gitlab_service" + +class GemnasiumService < Service + validates :token, :api_key, presence: true, if: :activated? + + def title + 'Gemnasium' + end + + def description + 'Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.' + end + + def to_param + 'gemnasium' + end + + def fields + [ + { type: 'text', name: 'api_key', placeholder: 'Your personal API KEY on gemnasium.com ' }, + { type: 'text', name: 'token', placeholder: 'The project\'s slug on gemnasium.com' } + ] + end + + def execute(push_data) + repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git") + Gemnasium::GitlabService.execute( + ref: push_data[:ref], + before: push_data[:before], + after: push_data[:after], + token: token, + api_key: api_key, + repo: repo_path + ) + end +end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 7f5380a4551c2..017cd9eeaab2e 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # class GitlabCiService < Service diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index ea2169fb1688a..e3ea142c2c24f 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # class HipchatService < Service diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index c5b1b9ab8d30c..877b9a7740466 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # class PivotaltrackerService < Service diff --git a/app/models/service.rb b/app/models/service.rb index 540aaad1ce586..07153bea1f1fe 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -13,12 +13,13 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # # To add new service you should build a class inherited from Service # and implement a set of methods class Service < ActiveRecord::Base - attr_accessible :title, :token, :type, :active + attr_accessible :title, :token, :type, :active, :api_key belongs_to :project has_one :service_hook @@ -53,4 +54,13 @@ def execute def can_test? !project.empty_repo? end + + def doc + begin + Gitlab::ServiceDoc.get(to_param) + rescue + Rails.logger.error "Unable to get Service documentation" + nil + end + end end diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index a3b5296ed1e3c..802fc30d175ac 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -43,3 +43,11 @@   - if @service.valid? && @service.activated? && @service.can_test? = link_to 'Test settings', test_project_service_path(@project, @service.to_param), class: 'btn btn-small' + + +- unless @service.doc.nil? + %h4 + Help + %i.icon-question.clgray + + %p= markdown(@service.doc) diff --git a/config/initializers/load_services_doc.rb b/config/initializers/load_services_doc.rb new file mode 100644 index 0000000000000..17746fae2718e --- /dev/null +++ b/config/initializers/load_services_doc.rb @@ -0,0 +1 @@ +Gitlab::ServiceDoc.load diff --git a/db/migrate/20140214102325_add_api_key_to_services.rb b/db/migrate/20140214102325_add_api_key_to_services.rb new file mode 100644 index 0000000000000..30eeca2c1f65c --- /dev/null +++ b/db/migrate/20140214102325_add_api_key_to_services.rb @@ -0,0 +1,5 @@ +class AddApiKeyToServices < ActiveRecord::Migration + def change + add_column :services, :api_key, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 73d0a92e1cc95..78ac5a0be113e 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: 20140116231608) do +ActiveRecord::Schema.define(version: 20140214102325) do create_table "broadcast_messages", force: true do |t| t.text "message", null: false @@ -222,6 +222,7 @@ t.string "subdomain" t.string "room" t.text "recipients" + t.string "api_key" end add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree diff --git a/doc/services/gemnasium b/doc/services/gemnasium new file mode 100644 index 0000000000000..cebe374f9599e --- /dev/null +++ b/doc/services/gemnasium @@ -0,0 +1,13 @@ +To setup the service you'll need to register an account on [gemnasium.com](https://gemnasium.com/) and add your project. +Then proceed with the following steps: + +---- + +1. Grab your personnal API key from your [account's settings page](https://gemnasium.com/settings) +2. Grab the project's slug from the project's settings page and paste it in **token** field +3. Check "Active" +4. Click "Save" +5. Click "Update Settings" to manually trigger the first sync + +Now you can check your project's status on [gemnasium.com](https://gemnasium.com/). +It will be updated automatically each time you push commits on this repository. diff --git a/features/project/service.feature b/features/project/service.feature index 46b983e8f9a36..9dcc966d6ab99 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -42,3 +42,10 @@ Feature: Project Services And I click email on push service link And I fill email on push settings Then I should see email on push service settings saved + + Scenario: Activate Gemnasium service + When I visit project "Shop" services page + And I click Gemnasium service link + Then I should see Gemnasium service help text + And I fill Gemnasium settings + Then I should see Gemnasium service settings saved diff --git a/features/steps/project/project_services.rb b/features/steps/project/project_services.rb index 54b3f18e0845a..a885f58451a87 100644 --- a/features/steps/project/project_services.rb +++ b/features/steps/project/project_services.rb @@ -13,6 +13,7 @@ class ProjectServices < Spinach::FeatureSteps page.should have_content 'Hipchat' page.should have_content 'GitLab CI' page.should have_content 'Assembla' + page.should have_content 'Gemnasium' end step 'I click gitlab-ci service link' do @@ -100,4 +101,25 @@ class ProjectServices < Spinach::FeatureSteps step 'I should see email on push service settings saved' do find_field('Recipients').value.should == 'qa@company.name' end + + step 'I click Gemnasium service link' do + click_link 'Gemnasium' + end + + step 'I fill Gemnasium settings' do + check 'Active' + fill_in 'Api key', with: 'verySecretApiKey' + fill_in 'Token', with: 'verySecret' + click_button 'Save' + end + + step 'I should see Gemnasium service settings saved' do + find_field('Api key').value.should == 'verySecretApiKey' + find_field('Token').value.should == 'verySecret' + end + + step 'I should see Gemnasium service help text' do + page.should have_content "To setup the service you'll need to register an account on gemnasium.com and add your project." + end + end diff --git a/lib/gitlab/service_doc.rb b/lib/gitlab/service_doc.rb new file mode 100644 index 0000000000000..34922feecbd97 --- /dev/null +++ b/lib/gitlab/service_doc.rb @@ -0,0 +1,20 @@ +module Gitlab + class ServiceDoc + DOC_DIR = Rails.root.join("doc","services").freeze + class << self + def load + @docs = {} + if File.exists? DOC_DIR + Dir.foreach(DOC_DIR) do |name| + next if name == '.' or name == '..' + @docs[name] = File.read(File.join(DOC_DIR, name)) + end + end + end + + def get(name) + @docs[name] + end + end + end +end diff --git a/spec/fixtures/doc/services/example b/spec/fixtures/doc/services/example new file mode 100644 index 0000000000000..58568b4b431c2 --- /dev/null +++ b/spec/fixtures/doc/services/example @@ -0,0 +1 @@ +This is a sample documentation file diff --git a/spec/lib/gitlab/service_doc_spec.rb b/spec/lib/gitlab/service_doc_spec.rb new file mode 100644 index 0000000000000..6691746e58755 --- /dev/null +++ b/spec/lib/gitlab/service_doc_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe Gitlab::ServiceDoc do + + describe 'load' do + context "without doc folder" do + before do + Gitlab::ServiceDoc.send(:remove_const, 'DOC_DIR') + Gitlab::ServiceDoc::DOC_DIR = "imaginary_dir" + end + it { expect{ Gitlab::ServiceDoc.load }.to_not raise_error } + it { Gitlab::ServiceDoc.instance_variable_get(:@docs).should be_empty } + end + + context "without doc files" do + before do + Dir.stub(:foreach).and_return([]) + end + it { expect{ Gitlab::ServiceDoc.load }.to_not raise_error } + it { Gitlab::ServiceDoc.instance_variable_get(:@docs).should be_empty } + end + + context "with doc files" do + before do + Gitlab::ServiceDoc.send(:remove_const, 'DOC_DIR') + Gitlab::ServiceDoc::DOC_DIR = Rails.root.join("spec","fixtures","doc","services") + Gitlab::ServiceDoc.load + end + it { Gitlab::ServiceDoc.instance_variable_get(:@docs).should_not be_empty } + it { Gitlab::ServiceDoc.get("example").should == "This is a sample documentation file" } + end + end + +end diff --git a/spec/models/assembla_service_spec.rb b/spec/models/assembla_service_spec.rb index 0b961c81ac115..3ac67a8bb40a6 100644 --- a/spec/models/assembla_service_spec.rb +++ b/spec/models/assembla_service_spec.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # require 'spec_helper' diff --git a/spec/models/flowdock_service_spec.rb b/spec/models/flowdock_service_spec.rb index 636aba2f0124e..c7f9e187c11a1 100644 --- a/spec/models/flowdock_service_spec.rb +++ b/spec/models/flowdock_service_spec.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # require 'spec_helper' diff --git a/spec/models/gemnasium_service_spec.rb b/spec/models/gemnasium_service_spec.rb new file mode 100644 index 0000000000000..dfc99849d6063 --- /dev/null +++ b/spec/models/gemnasium_service_spec.rb @@ -0,0 +1,47 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean default(FALSE), not null +# project_url :string(255) +# subdomain :string(255) +# room :string(255) +# api_key :string(255) +# + +require 'spec_helper' + +describe GemnasiumService do + describe "Associations" do + it { should belong_to :project } + it { should have_one :service_hook } + end + + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + + before do + @gemnasium_service = GemnasiumService.new + @gemnasium_service.stub( + project_id: project.id, + project: project, + service_hook: true, + token: 'verySecret', + api_key: 'GemnasiumUserApiKey' + ) + @sample_data = GitPushService.new.sample_data(project, user) + end + it "should call Gemnasium service" do + Gemnasium::GitlabService.should_receive(:execute).with(an_instance_of(Hash)).once + @gemnasium_service.execute(@sample_data) + end + end +end diff --git a/spec/models/gitlab_ci_service_spec.rb b/spec/models/gitlab_ci_service_spec.rb index 56efa9df45782..8ec15cb346606 100644 --- a/spec/models/gitlab_ci_service_spec.rb +++ b/spec/models/gitlab_ci_service_spec.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # require 'spec_helper' diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 667c80bcf19e9..35925abdf92ac 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -13,6 +13,7 @@ # project_url :string(255) # subdomain :string(255) # room :string(255) +# api_key :string(255) # require 'spec_helper' @@ -63,4 +64,37 @@ end end end + + describe :doc do + before do + @service = Service.new + @service.stub(:to_param).and_return("example") + end + + context "without documentation file" do + before do + Gitlab::ServiceDoc.instance_variable_set(:@docs, {"another_service" => "Some service documentation"}) + end + + it { @service.doc.should be_nil } + end + + context "with documentation file provided" do + before do + Gitlab::ServiceDoc.instance_variable_set(:@docs, {@service.to_param => "Some service documentation"}) + end + + it { @service.doc.should == "Some service documentation" } + end + + context "when something bad happens" do + before do + Gitlab::ServiceDoc.stub(:get).and_raise("something bad") + end + + it { expect { @service.doc }.to_not raise_error } + it { @service.doc.should be_nil } + end + + end end