polisher 0.3 → 0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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