jenkins-plugin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ .idea
4
+ Gemfile.lock
5
+ pkg/*
6
+ target/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jenkins-plugins.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # Jenkins plugins
2
+
3
+ Provide the facility to create, develop and release extensions for [Jenkins](http://jenkins-ci.org) with nothing but knowledge of the language, tools and best practices of the Ruby community.
4
+
5
+ [read more](http://blog.thefrontside.net/2011/05/12/what-it-take-to-bring-ruby-to-jenkins)...
6
+
7
+ # Get started
8
+
9
+ > This is all very theoretical. Don't try this at home.
10
+
11
+ $ gem install jenkins-plugins
12
+ $ jpi create my-awesome-jenkins-plugin
13
+ $ cd my-awesome-jenkins-plugin
14
+ $ jpi gen build_wrapper MyBuildWrapper
15
+ $ rake server
16
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
data/bin/jpi ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jpi/cli'
4
+
5
+ JPI::CLI.start
@@ -0,0 +1,90 @@
1
+
2
+ Feature: Generating a new Jenkins Ruby Plugin
3
+
4
+ Creating a new Ruby plugin for Jenkins needs to be as simple as running a single command
5
+ that will generate a project skeleton. This skeleton will come complete with git repository and all
6
+ the goodies that you need to do your plugin develompent.
7
+
8
+ Background: Creating a brand new Jenkins Ruby Plugin
9
+ Given I've run "jenkins-plugin create newplugin"
10
+
11
+ Scenario: The directory skeleton is generated
12
+ Then I should see this structure
13
+ """
14
+ [-] newplugin
15
+ | [+] .git
16
+ | .gitignore
17
+ | Gemfile
18
+ | Rakefile
19
+ | [-] lib
20
+ | [-] newplugin
21
+ | | | version.rb
22
+ | | newplugin.rb
23
+ | [-] spec
24
+ | | spec_helper.rb
25
+ | [-] plugin
26
+ | | [+] models
27
+ | | [+] views
28
+ """
29
+
30
+ Scenario: The .gitignore contents
31
+ When I open ".gitignore"
32
+ Then I should see
33
+ """
34
+ .bundle
35
+ .rvmrc
36
+ .rspec
37
+ """
38
+
39
+ Scenario: The Gemfile contents
40
+ When I open "Gemfile"
41
+ Then I should see
42
+ """
43
+ source :rubygems
44
+
45
+ gem "jenkins-war"
46
+ gem "jenkins-plugins"
47
+ """
48
+
49
+ Scenario: The Rakefile contents
50
+ When I open "Rakefile"
51
+ Then I should see
52
+ """
53
+ require 'jenkins/rake'
54
+ Jenkins::Rake.install_tasks
55
+ """
56
+
57
+ Scenario: The plugin module contents
58
+ When I open "lib/newplugin.rb"
59
+ Then I should see
60
+ """
61
+ module Newplugin
62
+
63
+ def self.start
64
+ #do any startup when this plugin initializes
65
+ end
66
+
67
+ def self.stop
68
+ #perform any necessary cleanup when this plugin is shut down.
69
+ end
70
+ end
71
+ """
72
+
73
+ Scenario: The version file is generated
74
+ When I open "lib/newplugin/version.rb"
75
+ Then I should see
76
+ """
77
+
78
+ module Newplugin
79
+ VERSION = 0.0.1
80
+ end
81
+ """
82
+
83
+ Scenario: The spec_helper is created
84
+ When I open "spec/spec_helper.rb"
85
+ Then I should see
86
+ """
87
+ $:.unshift(Pathname(__FILE__).dirname.join('../lib'))
88
+ require 'newplugin'
89
+ """
90
+
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "jenkins/plugin/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "jenkins-plugin"
7
+ s.version = Jenkins::Plugin::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Charles Lowell"]
10
+ s.email = ["cowboyd@thefrontside.net"]
11
+ s.homepage = "http://github.com/cowboyd/jenkins-plugin"
12
+ s.summary = %q{Tools for creating and building Jenkins Ruby plugins}
13
+ s.description = %q{I'll think of a better description later, but if you're reading this, then I haven't}
14
+
15
+ s.rubyforge_project = "jenkins-plugin"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "rubyzip"
23
+ s.add_dependency "thor"
24
+ s.add_dependency "jenkins-war", ">= 1.427"
25
+ s.add_dependency "bundler", ">= 1.1.pre.8"
26
+
27
+ s.add_development_dependency "rspec", "~> 2.0"
28
+ s.add_development_dependency "cucumber", "~> 1.0"
29
+ end
@@ -0,0 +1,106 @@
1
+ require 'zip/zip'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'fileutils'
5
+
6
+ module Jenkins
7
+ class Plugin
8
+ module Tools
9
+ # class for parsing hpi file and its manifests
10
+ class Hpi
11
+ attr_reader :file, :manifest
12
+
13
+ # take the path name to the plugin file
14
+ def initialize(file)
15
+ @file = file
16
+
17
+ # load and parse manifests
18
+ Zip::ZipFile.open(@file) do |zip|
19
+ zip.get_input_stream("META-INF/MANIFEST.MF") do |m|
20
+ # main section of the manifest
21
+ @manifest = parse_manifest(m.read)[0]
22
+ end
23
+ end
24
+
25
+ # parse dependencies into hash
26
+ @dependencies = {}
27
+ deps = @manifest["Plugin-Dependencies"]
28
+ if deps
29
+ deps.split(",").each do |token|
30
+ token = token.gsub(/;.+/,"") # trim off the optional portions
31
+ name,ver = token.split(":")
32
+ @dependencies[name] = ver
33
+ end
34
+ end
35
+ end
36
+
37
+ # given the plugin short name and the version number,
38
+ # return the path name of the .hpi file by either locating the plugin locally or downloading it.
39
+ def self.resolve(short_name,version)
40
+ # this is where we expect the retrieved file to be
41
+ cache = File.expand_path "~/.jenkins/cache/plugins/#{short_name}/#{version}/#{short_name}.hpi"
42
+
43
+ return cache if File.exists?(cache)
44
+
45
+ # now we start looking for places to find them
46
+
47
+ # is it in the local maven2 repository?
48
+ maven = File.expand_path "~/.m2/repository/org/jenkins-ci/plugins/#{short_name}/#{version}/#{short_name}-#{version}.hpi"
49
+ return maven if File.exists?(maven)
50
+
51
+ # download from the community update center
52
+ FileUtils.mkdir_p(File.dirname(cache))
53
+ open(cache+".tmp","wb") do |f|
54
+ puts "Downloading #{short_name} #{version}"
55
+ url = "https://updates.jenkins-ci.org/download/plugins/#{short_name}/#{version}/#{short_name}.hpi?for=ruby-plugin"
56
+ f.write fetch(url).body
57
+ end
58
+ FileUtils.mv cache+".tmp", cache
59
+
60
+ return cache
61
+ end
62
+
63
+ # download with redirect support
64
+ def fetch(uri, limit = 10)
65
+ # You should choose better exception.
66
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
67
+
68
+ response = Net::HTTP.get_response(URI.parse(uri))
69
+ case response
70
+ when Net::HTTPSuccess then response
71
+ when Net::HTTPRedirection then fetch(response['location'], limit - 1)
72
+ else
73
+ response.error!
74
+ end
75
+ end
76
+
77
+ # parse manifest file text into a hash
78
+ def parse_manifest(txt)
79
+ # separators
80
+ nl = /\r\n|\n|\r[^\n]/
81
+ secsep = /(#{nl}){2}/
82
+
83
+ txt.split(secsep).reject { |s| s.chomp.length==0 }.map do |section|
84
+ lines = []
85
+ section.split(nl).each do |line|
86
+ if line[0]==0x20
87
+ lines.last << line[1..-1] # continuation of the previous line
88
+ else
89
+ lines << line
90
+ end
91
+ end
92
+
93
+ # convert to hash
94
+ hash = {}
95
+ lines.each do |l|
96
+ (k,v) = l.split(/: /,2)
97
+ hash[k] = v
98
+ end
99
+
100
+ hash
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,29 @@
1
+ module Jenkins
2
+ class Plugin
3
+ module Tools
4
+ class Loadpath
5
+ def initialize(*groups)
6
+ require 'bundler'
7
+ @groups = groups.empty? ? [:default] : groups
8
+ end
9
+
10
+ def to_path
11
+ to_a.join(File::PATH_SEPARATOR)
12
+ end
13
+
14
+ def to_a
15
+ [].tap do |paths|
16
+ specs = Bundler.definition.specs_for @groups.map {|g| g.to_sym}
17
+ for spec in specs
18
+ next if spec.name == "bundler"
19
+ for path in spec.require_paths
20
+ paths << File.join(spec.full_gem_path, path)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ module Jenkins
2
+ class Plugin
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,140 @@
1
+ require 'jenkins/plugin/version'
2
+ require 'jenkins/plugin/tools/hpi'
3
+ require 'jenkins/plugin/tools/loadpath'
4
+ require 'zip/zip'
5
+
6
+ module Jenkins
7
+ # given the IO handle, produce the basic manifest entries that are common between hpi and hpl formats
8
+ def self.generate_manifest(f)
9
+ f.puts "Manifest-Version: 1.0"
10
+ f.puts "Created-By: #{Jenkins::Plugin::VERSION}"
11
+ f.puts "Build-Ruby-Platform: #{RUBY_PLATFORM}"
12
+ f.puts "Build-Ruby-Version: #{RUBY_VERSION}"
13
+
14
+ f.puts "Group-Id: org.jenkins-ci.plugins"
15
+ f.puts "Short-Name: #{::PluginName}"
16
+ f.puts "Long-Name: #{::PluginName}" # TODO: better name
17
+ f.puts "Url: http://jenkins-ci.org/" # TODO: better value
18
+ # f.puts "Compatible-Since-Version:"
19
+ f.puts "Plugin-Class: ruby.RubyPlugin"
20
+ f.puts "Plugin-Version: #{::PluginVersion}"
21
+ f.puts "Jenkins-Version: 1.426"
22
+
23
+ f.puts "Plugin-Dependencies: " + ::PluginDeps.map{|k,v| "#{k}:#{v}"}.join(",")
24
+ # f.puts "Plugin-Developers:"
25
+ end
26
+
27
+
28
+ class Rake
29
+ def self.install_tasks
30
+ self.new.install
31
+ end
32
+
33
+ include ::Rake::DSL if defined? ::Rake::DSL
34
+
35
+ def install
36
+ desc "Directory used as JENKINS_HOME during 'rake server'"
37
+ directory work = "work"
38
+
39
+ desc "remove built artifacts"
40
+ task :clean do
41
+ sh "rm -rf pkg"
42
+ sh "rm -rf vendor"
43
+ end
44
+
45
+ # verify that necessary metadata constants are defined
46
+ task :verify_constants do
47
+ ["PluginName","PluginVersion"].each do |n|
48
+ fail("Constant #{n} is not defined") unless Object.const_defined?(n)
49
+ end
50
+ end
51
+
52
+ desc "output the development servers loadpath"
53
+ task :loadpath do
54
+ loadpath = Jenkins::Plugin::Tools::Loadpath.new(:default)
55
+ puts loadpath.to_path
56
+ end
57
+
58
+ directory target = "pkg"
59
+ desc "bundle gems"
60
+ task :bundle => [target] do
61
+ require 'java'
62
+ require 'bundler'
63
+ require 'bundler/cli'
64
+
65
+ puts "bundling..."
66
+ ENV['BUNDLE_APP_CONFIG'] = "#{target}/vendor/bundle"
67
+ Bundler::CLI.start ["--standalone", "--path", "#{target}/vendor/gems", "--without", "development"]
68
+ end
69
+
70
+ desc "package up stuff into HPI file"
71
+ task :package => [:verify_constants, target, :bundle] do
72
+
73
+ file_name = "#{target}/#{::PluginName}.hpi"
74
+ File.delete file_name if File.exists?(file_name)
75
+
76
+ Zip::ZipFile.open(file_name, Zip::ZipFile::CREATE) do |zipfile|
77
+ zipfile.get_output_stream("META-INF/MANIFEST.MF") do |f|
78
+ Jenkins.generate_manifest(f)
79
+ f.puts "Bundle-Path: vendor/gems"
80
+ end
81
+ zipfile.mkdir("WEB-INF/classes")
82
+
83
+ ["lib","models","views", "#{target}/vendor"].each do |d|
84
+ Dir.glob("#{d}/**/*") do |f|
85
+ if !File.directory? f
86
+ zipfile.add("WEB-INF/classes/#{f.gsub("#{target}/",'')}",f)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ puts "#{::PluginName} plugin #{::PluginVersion} built to #{file_name}"
92
+ end
93
+
94
+ desc "resolve dependency plugins into #{work}/plugins"
95
+ task :'resolve-dependency-plugins' => [work] do
96
+ FileUtils.mkdir_p("#{work}/plugins")
97
+
98
+ puts "Copying plugin dependencies into #{work}/plugins"
99
+ ::PluginDeps.each do |short_name,version|
100
+ FileUtils.cp Jenkins::Plugin::Tools::Hpi::resolve(short_name,version), "#{work}/plugins/#{short_name}.hpi", :verbose=>true
101
+ end
102
+ end
103
+
104
+ desc "run a Jenkins server with this plugin"
105
+ task :server => :'resolve-dependency-plugins' do
106
+ require 'jenkins/war'
107
+ require 'zip/zip'
108
+ require 'fileutils'
109
+
110
+ loadpath = Jenkins::Plugin::Tools::Loadpath.new
111
+
112
+ # generate the plugin manifest
113
+ FileUtils.mkdir_p("#{work}/plugins")
114
+ File.open("#{work}/plugins/#{::PluginName}.hpl",mode="w+") do |f|
115
+ Jenkins.generate_manifest f
116
+
117
+ # f.puts "Libraries: "+["lib","models","pkg/vendor"].collect{|r| Dir.pwd+'/'+r}.join(",")
118
+ # TODO: where do we put views?
119
+ # TODO: where do we put static resources?
120
+ f.puts "Load-Path: #{loadpath.to_a.join(':')}"
121
+ f.puts "Resource-Path: #{Dir.pwd}/views"
122
+ f.puts "Lib-Path: #{Dir.pwd}/lib/"
123
+ f.puts "Models-Path: #{Dir.pwd}/models"
124
+ end
125
+
126
+ # TODO: assemble dependency plugins
127
+
128
+ # execute Jenkins
129
+ args = []
130
+ args << "java"
131
+ args << "-Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
132
+ args << "-DJENKINS_HOME=#{work}"
133
+ args << "-jar"
134
+ args << Jenkins::War::LOCATION
135
+ exec *args
136
+ end
137
+
138
+ end
139
+ end
140
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jenkins-plugin
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Charles Lowell
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-02 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rubyzip
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: thor
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: jenkins-war
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 857
58
+ segments:
59
+ - 1
60
+ - 427
61
+ version: "1.427"
62
+ type: :runtime
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
65
+ name: bundler
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 1923831889
73
+ segments:
74
+ - 1
75
+ - 1
76
+ - pre
77
+ - 8
78
+ version: 1.1.pre.8
79
+ type: :runtime
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ name: rspec
83
+ prerelease: false
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 2
92
+ - 0
93
+ version: "2.0"
94
+ type: :development
95
+ version_requirements: *id005
96
+ - !ruby/object:Gem::Dependency
97
+ name: cucumber
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ hash: 15
105
+ segments:
106
+ - 1
107
+ - 0
108
+ version: "1.0"
109
+ type: :development
110
+ version_requirements: *id006
111
+ description: I'll think of a better description later, but if you're reading this, then I haven't
112
+ email:
113
+ - cowboyd@thefrontside.net
114
+ executables:
115
+ - jpi
116
+ extensions: []
117
+
118
+ extra_rdoc_files: []
119
+
120
+ files:
121
+ - .gitignore
122
+ - Gemfile
123
+ - README.md
124
+ - Rakefile
125
+ - bin/jpi
126
+ - features/create-new-plugin.feature
127
+ - jenkins-plugin.gemspec
128
+ - lib/jenkins/plugin/tools/hpi.rb
129
+ - lib/jenkins/plugin/tools/loadpath.rb
130
+ - lib/jenkins/plugin/version.rb
131
+ - lib/jenkins/rake.rb
132
+ has_rdoc: true
133
+ homepage: http://github.com/cowboyd/jenkins-plugin
134
+ licenses: []
135
+
136
+ post_install_message:
137
+ rdoc_options: []
138
+
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ hash: 3
156
+ segments:
157
+ - 0
158
+ version: "0"
159
+ requirements: []
160
+
161
+ rubyforge_project: jenkins-plugin
162
+ rubygems_version: 1.6.2
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: Tools for creating and building Jenkins Ruby plugins
166
+ test_files:
167
+ - features/create-new-plugin.feature