polisher 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README.rdoc +84 -14
  2. data/Rakefile +21 -19
  3. data/TODO +5 -4
  4. data/bin/server +1 -0
  5. data/config/polisher.yml +0 -15
  6. data/db/connection.rb +13 -10
  7. data/db/migrations/{002_create_managed_gems.rb → 001_create_projects.rb} +4 -5
  8. data/db/migrations/{001_create_sources.rb → 002_create_sources.rb} +1 -3
  9. data/db/migrations/003_create_project_source_versions.rb +28 -0
  10. data/db/migrations/{003_create_events.rb → 004_create_events.rb} +2 -2
  11. data/db/migrations/005_create_project_dependencies.rb +28 -0
  12. data/db/models/event.rb +47 -21
  13. data/db/models/project.rb +110 -0
  14. data/db/models/project_dependency.rb +27 -0
  15. data/db/models/project_source_version.rb +31 -0
  16. data/db/models/source.rb +82 -16
  17. data/lib/common.rb +37 -5
  18. data/lib/dsl.rb +292 -0
  19. data/lib/event_handlers.rb +139 -73
  20. data/lib/gem_adapter.rb +94 -0
  21. data/polisher.rb +302 -50
  22. data/public/stylesheets/style.css +15 -31
  23. data/spec/common_spec.rb +28 -0
  24. data/spec/dsl_spec.rb +357 -0
  25. data/spec/event_handlers_spec.rb +166 -30
  26. data/spec/gem_adapter_spec.rb +89 -0
  27. data/spec/models_spec.rb +635 -107
  28. data/spec/polisher_spec.rb +496 -56
  29. data/views/layout.haml +3 -5
  30. data/views/projects/index.haml +42 -0
  31. data/views/projects/index.html.haml +38 -0
  32. data/views/result.haml +9 -0
  33. data/views/sources/index.haml +24 -41
  34. data/views/sources/index.html.haml +26 -0
  35. metadata +131 -12
  36. data/db/models/managed_gem.rb +0 -111
  37. data/public/javascripts/jquery-1.2.6.min.js +0 -32
  38. data/public/javascripts/polisher.js +0 -15
  39. data/views/gems/index.haml +0 -106
@@ -1,35 +1,105 @@
1
- == Rubygem Polisher
1
+ == Polisher
2
2
  Copyright (C) 2010 Red Hat, Inc.
3
3
 
4
4
  Written by Mohammed Morsi <mmorsi@redhat.com>
5
5
 
6
6
  === Intro
7
- Polisher is a webapp utility greated towards processing rubygems after they
8
- have been published. It allows the end user to configure events to be run when
9
- specified gems are updated.
7
+ Polisher is a project release management tool which can be used to register various event
8
+ handlers to be invoked on specific versions of project / source releases. Polisher provides
9
+ a simple {REST}[http://en.wikipedia.org/wiki/Representational_State_Transfer] interface which
10
+ to create projects w/ sources and dependencies, and to download and transform them into any
11
+ target output format.
10
12
 
11
- Polisher uses the gemcutter webhook API in conjunction with the sinatra/rack
12
- web framework to subscribe to gem updates and run any specified/arbitrary event
13
- handler callbacks.
13
+ Polisher also provides a simple {DSL}[http://en.wikipedia.org/wiki/Domain-specific_language]
14
+ frontend to the REST interface, able to be used to create and trigger elegant release
15
+ workflows for any number of projects simultaneously. Workflows for many major Ruby based projects
16
+ have been setup, transforming various versions into {Fedora}[http://fedoraproject.org/] repos and
17
+ can be found at [http://github.com/movitto/polisher-scripts].
14
18
 
15
- === Running
19
+
20
+ == Running
16
21
  To install simply run:
17
22
 
18
23
  'gem install polisher'
19
24
 
25
+
20
26
  Alternatively checkout the source from github via
21
27
 
22
28
  'git clone git://github.com/movitto/polisher.git'
23
29
 
24
- To run the server, run 'bin/server' in the project root.
30
+
31
+ Polisher requires various rubygems to work. gem install should pull in all dependencies
32
+ but if running polisher from source, be sure to gem install:
33
+
34
+ * rubygems
35
+ * sinatra
36
+ * libxml
37
+ * curb
38
+ * activerecord
39
+ * libxml
40
+
41
+ For the complete list of dependencies, see the
42
+ {Rakefile}[http://github.com/movitto/polisher/blob/master/Rakefile].
43
+
44
+
45
+ Polisher currently pulls config from the config/ subdir, the database
46
+ params are defined in database.yml (currently defaulting to a sqlite flat file
47
+ db residing in db/data), and the general application config resides in
48
+ polisher.yml. The only general app config currently used is 'gem_api_key',
49
+ which will be read from ~/.gem/credentials if missing from polisher.yml.
50
+
51
+ To run the server, run './bin/server' in the project root.
25
52
 
26
53
  To run the spec suite simply run 'rake spec'
27
54
 
28
- Before running anything make sure to set any neccessary configuration
29
- in config/polisher.yml (particularily your gem api key)
55
+ Config is environment specific, the server runs in the 'development' environment
56
+ by default and the spec suite runs in 'test' by default. To change the environment
57
+ simply run
58
+
59
+ 'export RACK_ENV="production"'
60
+
61
+ before './bin/server' or 'rake spec', etc
30
62
 
31
63
  === Using
32
- See the generated API docs and spec suite for more detailed usage.
64
+ Run 'rake spec' in the project root to generate the API docs in the doc/ subdir.
65
+ Also see the spec suite and polisher scripts for more detailed usage.
66
+
67
+ When running, the server can be accessed by navigating to 'http://localhost:3000'.
68
+ REST requests may be issued against that URI and the actions defined in polisher.rb.
69
+ The DSL sits on top of this REST interface and provides a nice / simple means which
70
+ to setup projects, sources, release workflows, etc, in an easily reproducable manner.
71
+
72
+ At typical DSL use case might look like:
73
+
74
+ project :name => "ruby" do |proj|
75
+ proj.add_archive :name => 'ruby_source', :uri => "ftp://ftp.ruby-lang.org/pub/ruby/%{rubyxver}/ruby-%{arcver}.tar.bz2" do |archive|
76
+ archive.version "1.8.6", :rubyxver => "1.8", :arcver => "1.8.7-p311", :corresponds_to => proj.version("1.8.6")
77
+ proj.version "1.9.1", :corresponds_to => archive.version("1.9.1", :rubyxver => "1.9", :arcver => "1.8.7-p300")
78
+ end
79
+ proj.add_patch "http://cvs.fedoraproject.org/viewvc/rpms/ruby/F-13/ruby-deadcode.patch?view=markup"
80
+ # etc...
81
+
82
+ proj.on_version "*", "create package"
83
+ proj.on_version "=", "1.8.6", "update repo", "stable"
84
+ proj.on_version ">=", "1.9.1", "update repo", "devel"
85
+ end
86
+
87
+ Polisher is a work in progress, and features are being added / removed as neccessary.
88
+ Comments, bug reports, and patches are all very welcome.
89
+
90
+ == Extending
91
+ Out of the box Polisher is able to handle {Rubygem}[http://rubygems.org/] based projects and
92
+ generate rpm packages and yum repos on project releases. The
93
+ {Gemcutter API}[http://rubygems.org/pages/api_docs],
94
+ {gem2rpm}[http://rubyforge.org/projects/gem2rpm/], and rpm / yum tools are used to
95
+ accomplish this, though Polisher can be easily extended to support any input / output format.
96
+
97
+ The code itself currently has to be modified (most likely will be changed in the future),
98
+ but to add support for downloading another input source format, extend
99
+ {source.rb::download_to}[http://github.com/movitto/polisher/blob/master/db/models/source.rb]
100
+ and optionally add another add_source_type method to the
101
+ {Polisher::Project DSL}[http://github.com/movitto/polisher/blob/master/lib/dsl.rb].
33
102
 
34
- Polisher is a work in progress, and features are being added / removed as neccessary.
35
- Comments, bug reports, and patches are all very welcome.
103
+ To add another event handler, simple add a new method to the
104
+ {EventHandlers}[http://github.com/movitto/polisher/blob/master/lib/event_handlers.rb] module.
105
+ Polisher will take care of the rest.
data/Rakefile CHANGED
@@ -14,23 +14,23 @@ env ||= 'development'
14
14
  logger = Logger.new(STDOUT)
15
15
 
16
16
  GEM_NAME='polisher'
17
- PKG_VERSION='0.3'
17
+ PKG_VERSION='0.4'
18
18
 
19
19
  namespace :db do
20
+ desc "Migrate the database"
20
21
  task :migrate do
21
- desc "Migrate the database"
22
22
  Polisher::DB.connect Polisher::DB.load_config('./config/database.yml', env), logger
23
23
  Polisher::DB.migrate './db/migrations'
24
24
  end
25
25
 
26
+ desc "Rollback the database"
26
27
  task :rollback do
27
- desc "Rollback the database"
28
28
  Polisher::DB.connect Polisher::DB.load_config('./config/database.yml', env), logger
29
29
  Polisher::DB.rollback './db/migrations'
30
30
  end
31
31
 
32
+ desc "Drop all tables in the database"
32
33
  task :drop_tables do
33
- desc "Drop all tables in the database"
34
34
  Polisher::DB.connect Polisher::DB.load_config('./config/database.yml', env), logger
35
35
  Polisher::DB.drop_tables './db/migrations'
36
36
  end
@@ -46,24 +46,14 @@ Spec::Rake::SpecTask.new('spec' => ['test_env', 'db:drop_tables', 'db:migrate'])
46
46
  t.spec_files = FileList['spec/*_spec.rb']
47
47
  end
48
48
 
49
- task :rdoc do
50
- desc "Create RDoc documentation"
51
- system "rdoc --title 'Polisher documentation' lib/"
52
- end
53
-
54
49
  Rake::RDocTask.new do |rd|
55
50
  rd.main = "README.rdoc"
56
51
  rd.rdoc_dir = "doc/site/api"
57
- rd.rdoc_files.include("README.rdoc", "polisher.rb", "db/**/*.rb", "lib/**/*.rb")
52
+ rd.rdoc_files.include("README.rdoc", "polisher.rb", "db/models/*.rb", "lib/**/*.rb")
58
53
  end
59
54
 
60
- task :create_gem do
61
- desc "Create a new gem"
62
- system "gem build polisher.gemspec"
63
- end
64
-
65
- PKG_FILES = FileList['bin/*', 'config/*.yml', 'config.ru', 'COPYING',
66
- 'db/**/*.rb', 'lib/**/*.rb', 'LICENSE', 'polisher.rb', 'public/**/*', 'Rakefile',
55
+ PKG_FILES = FileList['bin/*', 'config/*.yml', 'config.ru', 'COPYING',
56
+ 'db/**/*.rb', 'lib/**/*.rb', 'LICENSE', 'polisher.rb', 'public/**/*', 'Rakefile',
67
57
  'README.rdoc', 'spec/**/*.rb', 'TODO', 'views/**/*.haml']
68
58
 
69
59
  DIST_FILES = FileList[
@@ -78,11 +68,23 @@ SPEC = Gem::Specification.new do |s|
78
68
  s.required_ruby_version = '>= 1.8.1'
79
69
  s.required_rubygems_version = Gem::Requirement.new(">= 1.3.3")
80
70
 
71
+ s.add_dependency('sinatra', '>= 0.9.4')
72
+ s.add_dependency('thin', '>= 1.2.7')
73
+ s.add_dependency('activerecord', '>= 2.3.5')
74
+ s.add_dependency('haml', '>= 2.2.20')
75
+ s.add_dependency('curb', '>= 0.6.7') # eg curl
76
+ s.add_dependency('libxml-ruby', '>= 1.1.3')
77
+ s.add_dependency('rest-client', '>= 1.4.2')
78
+ s.add_dependency('json_pure', '>= 1.2.0')
79
+ s.add_dependency('gem2rpm', '>= 0.6.0')
80
+ s.add_dependency('rspec', '>= 1.3.0')
81
+ s.add_dependency('rack-test', '>= 0.5.3')
82
+
81
83
  s.author = "Mohammed Morsi"
82
84
  s.email = "mmorsi@redhat.com"
83
85
  s.date = %q{2010-03-11}
84
- s.description = "Ruby gem polisher"
85
- s.summary = "Ruby gem polisher"
86
+ s.summary = "A project release management tool"
87
+ s.description = "Polisher provides simple REST and DSL interfaces allowing you to configure event workflows to be invoked on specific versions of project/source releases"
86
88
  s.homepage = "http://github.com/movitto/polisher"
87
89
  end
88
90
 
data/TODO CHANGED
@@ -1,6 +1,7 @@
1
1
  - Remaining TODO's, FIXME's, etc
2
- - Syncronization locks needed! (on all shared artifacts, db objects)
3
- - More event handlers, generic handler interface,
2
+ - Parallelism w/ syncronization locks needed! (on all shared artifacts, db objects)
3
+ - More event handlers, generic handler interface,
4
4
  workflow integration to automatically chain handlers together
5
- - multiple user support, RBAC and ACL mechanisms
6
- - simple command line utility / api interface
5
+ - Generic source download interface, easily be able to download sources via and
6
+ number of mechanisms, and perform pre-release transformations (taring a source
7
+ checkout for example)
data/bin/server CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/bin/sh
2
+ mkdir -p db/data/
2
3
  rake db:migrate
3
4
  thin start --debug -R config.ru
@@ -1,20 +1,5 @@
1
1
  development:
2
- gem_api_key: "YOUR GEMCUTTER API KEY HERE"
3
- email_from: "foo@example.host"
4
- email_server: "localhost"
5
- email_subject: "gem #{gem_name} updated"
6
- email_body: "gem #{gem_name} updated"
7
2
 
8
3
  test:
9
- gem_api_key: "YOUR GEMCUTTER API KEY HERE"
10
- email_from: "foo@example.host"
11
- email_server: "localhost"
12
- email_subject: "gem #{gem_name} updated"
13
- email_body: "gem #{gem_name} updated"
14
4
 
15
5
  production:
16
- gem_api_key: "YOUR GEMCUTTER API KEY HERE"
17
- email_from: "foo@example.host"
18
- email_server: "localhost"
19
- email_subject: "gem #{gem_name} updated"
20
- email_body: "gem #{gem_name} updated"
@@ -7,9 +7,9 @@
7
7
  # it under the terms of the GNU Affero General Public License
8
8
  # as published by the Free Software Foundation, either version 3
9
9
  # of the License, or (at your option) any later version.
10
- #
10
+ #
11
11
  # You should have received a copy of the the GNU Affero
12
- # General Public License, along with Polisher. If not, see
12
+ # General Public License, along with Polisher. If not, see
13
13
  # <http://www.gnu.org/licenses/>
14
14
 
15
15
  require 'active_record'
@@ -18,28 +18,31 @@ Dir[File.dirname(__FILE__) + '/models/*.rb'].each { |model| require model }
18
18
 
19
19
  module Polisher
20
20
  module DB
21
-
21
+
22
+ # Load db config from specified file / environment
22
23
  def self.load_config(file_path, env = 'development')
23
24
  YAML::load(File.open(file_path))[env.to_s]
24
25
  end
25
-
26
+
27
+ # Connect to database specified in config
26
28
  def self.connect(config, logger)
27
29
  ActiveRecord::Base.logger = logger
28
30
  ActiveRecord::Base.establish_connection config
29
31
  end
30
-
32
+
33
+ # Perform any outstanding db migrations
31
34
  def self.migrate(migrations_dir)
32
35
  ActiveRecord::Migration.verbose = true
33
36
  ActiveRecord::Migrator.migrate(migrations_dir)
34
- end
35
-
36
- # perform a single polisher db rollback
37
+ end
38
+
39
+ # Perform a single polisher db rollback
37
40
  def self.rollback(migrations_dir)
38
41
  ActiveRecord::Migration.verbose = true
39
42
  ActiveRecord::Migrator.rollback(migrations_dir)
40
- end
43
+ end
41
44
 
42
- # drop all tables in the db
45
+ # Drop all tables in the db
43
46
  def self.drop_tables(migrations_dir)
44
47
  ActiveRecord::Migration.verbose = true
45
48
  ActiveRecord::Migrator.down(migrations_dir, 0)
@@ -10,15 +10,14 @@
10
10
  # General Public License, along with Polisher. If not, see
11
11
  # <http://www.gnu.org/licenses/>
12
12
 
13
- class CreateManagedGems < ActiveRecord::Migration
13
+ class CreateProjects < ActiveRecord::Migration
14
14
  def self.up
15
- create_table :managed_gems do |t|
16
- t.string :name
17
- t.references :source
15
+ create_table :projects do |t|
16
+ t.string :name
18
17
  end
19
18
  end
20
19
 
21
20
  def self.down
22
- drop_table :managed_gems
21
+ drop_table :projects
23
22
  end
24
23
  end
@@ -14,11 +14,9 @@ class CreateSources < ActiveRecord::Migration
14
14
  def self.up
15
15
  create_table :sources do |t|
16
16
  t.string :name
17
+ t.string :source_type
17
18
  t.string :uri
18
19
  end
19
-
20
- # create entry for the official gemcutter repo
21
- Source.create :name => "gemcutter", :uri => "http://gemcutter.org"
22
20
  end
23
21
 
24
22
  def self.down
@@ -0,0 +1,28 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ # Written by Mohammed Morsi <mmorsi@redhat.com>
3
+ #
4
+ # This program is free software, you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License
6
+ # as published by the Free Software Foundation, either version 3
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # You should have received a copy of the the GNU Affero
10
+ # General Public License, along with Polisher. If not, see
11
+ # <http://www.gnu.org/licenses/>
12
+
13
+ class CreateProjectSourceVersions < ActiveRecord::Migration
14
+ def self.up
15
+ create_table :project_source_versions do |t|
16
+ t.references :project
17
+ t.references :source
18
+ t.boolean :primary_source, :default => false
19
+ t.string :project_version
20
+ t.string :source_version
21
+ t.string :source_uri_params
22
+ end
23
+ end
24
+
25
+ def self.down
26
+ drop_table :project_source_versions
27
+ end
28
+ end
@@ -13,9 +13,9 @@
13
13
  class CreateEvents < ActiveRecord::Migration
14
14
  def self.up
15
15
  create_table :events do |t|
16
- t.references :managed_gem
16
+ t.references :project
17
17
  t.string :process
18
- t.string :gem_version
18
+ t.string :version
19
19
  t.string :version_qualifier
20
20
  t.string :process_options
21
21
  end
@@ -0,0 +1,28 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ # Written by Mohammed Morsi <mmorsi@redhat.com>
3
+ #
4
+ # This program is free software, you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License
6
+ # as published by the Free Software Foundation, either version 3
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # You should have received a copy of the the GNU Affero
10
+ # General Public License, along with Polisher. If not, see
11
+ # <http://www.gnu.org/licenses/>
12
+
13
+ class CreateProjectDependencies < ActiveRecord::Migration
14
+ def self.up
15
+ create_table :project_dependencies do |t|
16
+ t.references :project
17
+ t.string :project_version, :default => nil
18
+
19
+ t.integer :depends_on_project_id
20
+ t.string :depends_on_project_version, :default => nil
21
+ t.string :depends_on_project_params
22
+ end
23
+ end
24
+
25
+ def self.down
26
+ drop_table :project_dependencies
27
+ end
28
+ end
@@ -10,32 +10,48 @@
10
10
  # General Public License, along with Polisher. If not, see
11
11
  # <http://www.gnu.org/licenses/>
12
12
 
13
+ require 'lib/event_handlers'
14
+
13
15
  class Event < ActiveRecord::Base
14
- belongs_to :managed_gem
15
- alias :gem :managed_gem
16
+ include EventHandlers
17
+
18
+ belongs_to :project
16
19
 
17
- validates_presence_of :managed_gem_id, :process
20
+ validates_presence_of :project_id
21
+ validates_presence_of :process
18
22
 
19
- # TODO right mow just returning a fixed list, at some point dynamically generate
23
+ # Dynamically generate from methods we define in the lib/event_handlers module.
24
+ # Add more methods to that module for them to appear here
20
25
  def self.processes
21
- ["create_package", "update_repo", "run_test_suite", "notify_subscribers"]
26
+ EventHandlers.public_instance_methods.collect { |m| m.to_s }
22
27
  end
23
28
 
24
- # version qualifiers
29
+ # XXX FIXME we need this for security
30
+ #validates_inclusion_of :process, :in => Event.processes
31
+
32
+ # Version qualifiers
25
33
  VERSION_QUALIFIERS = ['', '=', '>', '<', '>=', '<=']
26
34
 
27
35
  validates_inclusion_of :version_qualifier, :in => VERSION_QUALIFIERS,
28
36
  :if => Proc.new { |e| !e.version_qualifier.nil? }
29
37
 
30
- validates_presence_of :gem_version,
38
+ # nil version and version_qualifier indicates event will be run for _all_ versons
39
+
40
+ validates_presence_of :version,
31
41
  :if => Proc.new { |e| !e.version_qualifier.nil? }
32
42
 
33
43
  validates_presence_of :version_qualifier,
34
- :if => Proc.new { |e| !e.gem_version.nil? }
44
+ :if => Proc.new { |e| !e.version.nil? }
45
+
46
+ # Determine if event applies to specified version
47
+ def applies_to_version?(cversion)
48
+ raise ArgumentError, "valid event version #{version} and version #{cversion} required" unless (version.nil? || version.class == String) && cversion.class == String
49
+
50
+ # XXX remove any non-numeric chars from the version number (eg if a version has a '-beta' or whatnot, not pretty but works for now
51
+ cversion.gsub(/[a-zA-Z]*/, '')
35
52
 
36
- # determine if event applies to specified version
37
- def applies_to_version?(version)
38
- gv, ev = version.to_f, gem_version.to_f
53
+ # TODO this will evaluate to false "1.1" = "1.1.0" here, is this correct? (implement this in a more robust way, eg split version into array around delims, compare each array elements w/ special case handling)
54
+ gv, ev = cversion, version
39
55
  return (["", nil].include? version_qualifier ) ||
40
56
  (version_qualifier == "=" && gv == ev) ||
41
57
  (version_qualifier == ">" && gv > ev) ||
@@ -44,18 +60,28 @@ class Event < ActiveRecord::Base
44
60
  (version_qualifier == "<=" && gv <= ev)
45
61
  end
46
62
 
47
- # run the event
48
- def run
49
- # covert process to method name
50
- handler = method(process.intern)
63
+ # Run the event, :version should be passed in via args; any other keys/values are optional
64
+ # and will be forwarded onto the event handler
65
+ def run(args = {})
66
+ version = args[:version]
67
+ raise ArgumentError, "must specify version when running event" if version.nil?
51
68
 
52
- # generate array of params from semi-colon seperated options
53
- params = process_options.nil? ? [] : process_options.split(';')
69
+ handler = nil
70
+ begin
71
+ # covert process to method name
72
+ handler = method(process.intern)
73
+ rescue NameError => e
74
+ raise ArgumentError, "could not find event handler #{process}"
75
+ end
54
76
 
55
- # first param is always the gem
56
- params.unshift gem
77
+ # generate array of event params from project, the event itself, the version being run, and any optional params passed in
78
+ event_params = [self, version, args]
57
79
 
58
- # invoke
59
- handler.call *params
80
+ begin
81
+ # invoke
82
+ handler.call *event_params
83
+ rescue Exception => e
84
+ raise RuntimeError, "error when running event handler #{process}: #{e}"
85
+ end
60
86
  end
61
87
  end