jpi 0.3.0

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.
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,76 @@
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
+ Using JRuby, install the plugin tools
10
+
11
+ $ gem install jpi
12
+
13
+ The gem provides the `jpi` executeable
14
+
15
+ $ jpi -h
16
+
17
+ jpi- tools to create, build, develop and release Jenkins plugins
18
+
19
+ Usage: jpi command [arguments] [options]
20
+
21
+ Commands:
22
+ jpi help [COMMAND] # get help for COMMAND, or for jpi itself
23
+ jpi new NAME # create a new plugin called NAME
24
+ jpi generate # generate code for extensions points
25
+ jpi build # build plugin into .hpi file suitable for distribution
26
+ jpi server # run a test server with plugin
27
+ jpi version # show jpi version information
28
+
29
+ The first thing you'll probably want to do is create a new ruby plugin.
30
+
31
+ $ jpi new one-great-plugin
32
+ create one-great-plugin/Gemfile
33
+ create one-great-plugin/one-great-plugin.pluginspec
34
+
35
+ This will create a minimal plugin project structure, to which you can add later.
36
+ Once you have your plugin created, you can run a server with it loaded
37
+
38
+ $ cd one-great-plugin
39
+ $ jpi server
40
+
41
+ Listening for transport dt_socket at address: 8000
42
+ webroot: System.getProperty("JENKINS_HOME")
43
+ [Winstone 2011/09/19 12:01:36] - Beginning extraction from war file
44
+ [Winstone 2011/09/19 12:01:37] - HTTP Listener started: port=8080
45
+ [Winstone 2011/09/19 12:01:37] - AJP13 Listener started: port=8009
46
+ [Winstone 2011/09/19 12:01:37] - Winstone Servlet Engine v0.9.10 running: controlPort=disabled
47
+ Sep 19, 2011 12:01:37 PM jenkins.model.Jenkins$6 onAttained
48
+ INFO: Started initialization
49
+ Sep 19, 2011 12:01:38 PM hudson.PluginManager$1$3$1 isDuplicate
50
+ Sep 19, 2011 12:01:39 PM jenkins.model.Jenkins$6 onAttained
51
+ INFO: Listed all plugins
52
+ Sep 19, 2011 12:01:39 PM ruby.RubyRuntimePlugin start
53
+ INFO: Injecting JRuby into XStream
54
+ Sep 19, 2011 12:01:49 PM jenkins.model.Jenkins$6 onAttained
55
+ INFO: Prepared all plugins
56
+ Sep 19, 2011 12:01:49 PM jenkins.model.Jenkins$6 onAttained
57
+ INFO: Started all plugins
58
+ Sep 19, 2011 12:01:49 PM jenkins.model.Jenkins$6 onAttained
59
+ INFO: Augmented all extensions
60
+ Sep 19, 2011 12:01:49 PM jenkins.model.Jenkins$6 onAttained
61
+ INFO: Loaded all jobs
62
+ Sep 19, 2011 12:01:51 PM jenkins.model.Jenkins$6 onAttained
63
+ INFO: Completed initialization
64
+ Sep 19, 2011 12:01:51 PM hudson.TcpSlaveAgentListener <init>
65
+ INFO: JNLP slave agent listener started on TCP port 52262
66
+ Sep 19, 2011 12:02:01 PM hudson.WebAppMain$2 run
67
+ INFO: Jenkins is fully up and running
68
+
69
+ Of course, this plugin isn't actually doing anything because we haven't defined any extension
70
+ points. Let's go ahead and create one of the most common extension points: a `Builder`
71
+
72
+ $ jpi generate builder logging
73
+
74
+
75
+
76
+
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 'jenkins/plugin/cli'
4
+
5
+ Jenkins::Plugin::CLI.start ARGV
@@ -0,0 +1,18 @@
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
+ Scenario: The directory skeleton is generated
9
+ When I run "jpi new newplugin"
10
+ # Then I should see this structure
11
+ # """
12
+ # [-] newplugin
13
+ # | [+] .git
14
+ # | .gitignore
15
+ # | Gemfile
16
+ # | Rakefile
17
+ # | newplugin.pluginspec
18
+ # """
@@ -0,0 +1,7 @@
1
+ When /^I run "([^"]*)"$/ do |cmd|
2
+ run cmd
3
+ end
4
+
5
+ Then /^I should see this structure$/ do |structure|
6
+ fail "no match" unless DirectoryStructure.new(structure).matches? work_dir
7
+ end
@@ -0,0 +1,49 @@
1
+
2
+ class DirectoryStructure
3
+ def initialize(structure)
4
+
5
+ @root = context = DirChild.new('.')
6
+
7
+ structure.each_line do |line|
8
+ if line =~ /(\[[-+]\]|\|)?\s+(\.?\w+)$/
9
+ op, name = $1, $2
10
+ case op
11
+ when "[+]"
12
+ context.add(DirChild.new name)
13
+ when "[-]"
14
+ new_context = DirChild.new name
15
+ context.add(new_context)
16
+ context = new_context
17
+ when "|"
18
+ context.add(FileChild.new name)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def matches?(dir)
25
+ @root.matches?(dir)
26
+ end
27
+
28
+ Entry = Struct.new(:name)
29
+
30
+ class DirChild < Entry
31
+ def initialize(name)
32
+ super(name)
33
+ @entries = []
34
+ end
35
+
36
+ def add(entry)
37
+ @entries << entries
38
+ end
39
+
40
+ def matches?(realdir)
41
+ entries = Dir.new(realdir).entries
42
+ !@entries.detect {|e| !entries.map{|e| File.basename(e)}.member?(e)}
43
+ end
44
+ end
45
+
46
+ class FileChild < Entry
47
+
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ module Work
2
+ def run cmd
3
+ Dir.chdir(work_dir) do
4
+ root = Pathname(__FILE__).join('..', '..', '..')
5
+ full_cmd = "ruby -rubygems -I #{root.join('lib')} -S #{root.join('bin',cmd)}"
6
+ system(full_cmd) or fail "failed to run command #{cmd}"
7
+ end
8
+ end
9
+
10
+ def work_dir
11
+ @work_dir ||= File.expand_path("tmp/work")
12
+ end
13
+ end
14
+
15
+ Before do
16
+ FileUtils.rm_rf work_dir
17
+ FileUtils.mkdir_p work_dir
18
+ end
19
+
20
+ World(Work)
data/jpi.gemspec ADDED
@@ -0,0 +1,28 @@
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 = "jpi"
7
+ s.version = Jenkins::Plugin::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Charles Lowell", "Jørgen P. Tjernø", "Kohsuke Kawaguchi"]
10
+ s.email = ["cowboyd@thefrontside.net"]
11
+ s.homepage = "https://github.com/jenkinsci/jpi.rb"
12
+ s.summary = %q{Tools for creating and building Jenkins Ruby plugins}
13
+ s.description = %q{Allows you to generate a new Ruby plugin project, build it, test it in Jenkins and release it to the Jenkins Update Center.}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency "rubyzip"
21
+ s.add_dependency "thor"
22
+ s.add_dependency "jenkins-war", ">= 1.427"
23
+ s.add_dependency "bundler", "~> 1.1.rc2"
24
+ s.add_dependency "jenkins-plugin-runtime", "~> 0.1.13"
25
+
26
+ s.add_development_dependency "rspec", "~> 2.0"
27
+ s.add_development_dependency "cucumber", "~> 1.0"
28
+ end
@@ -0,0 +1,39 @@
1
+ module Jenkins
2
+ class CiOrg
3
+ # credential to access jenkins-ci.org
4
+ # TODO: move it elsewhere
5
+ class Credential
6
+ CREDENTIAL = File.expand_path("~/.jenkins-ci.org")
7
+
8
+ def initialize
9
+ @props = {}
10
+
11
+ if File.exists?(CREDENTIAL) then
12
+ File.open(CREDENTIAL,'r') do |f|
13
+ f.each_line do |l|
14
+ if l[0]=='#' then
15
+ return # comment
16
+ end
17
+
18
+ k,v = l.split("=",2)
19
+ @props[k]=v.strip
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # do we already have the credential?
26
+ def has_credential?
27
+ @props["userName"] && @props["password"]
28
+ end
29
+
30
+ def user_name
31
+ @props["userName"]
32
+ end
33
+
34
+ def password
35
+ @props["password"]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,66 @@
1
+
2
+ require 'thor'
3
+ require 'jenkins/plugin/specification'
4
+ require 'jenkins/plugin/cli/formatting'
5
+ require 'jenkins/plugin/cli/new'
6
+ require 'jenkins/plugin/cli/generate'
7
+
8
+
9
+ module Jenkins
10
+ class Plugin
11
+ class CLI < Thor
12
+ extend Formatting
13
+
14
+ register New, "new", "new NAME", "create a new plugin called NAME"
15
+ register Generate, "generate", "generate [options] [arguments]", "add new classes/templates and views to your project"
16
+ map "g" => "generate"
17
+
18
+
19
+ desc "build", "build plugin into .hpi file suitable for distribution"
20
+ def build
21
+ require 'jenkins/plugin/tools/package'
22
+ pkg = Tools::Package.new(spec, "pkg")
23
+ pkg.build
24
+ pkg
25
+ end
26
+
27
+ desc "server", "run a test server with plugin"
28
+ method_option :home, :desc => "set server work directory", :default => 'work'
29
+ method_option :port, :desc => "server http port (currently ignored)", :default => 8080
30
+ method_option :war, :desc => "specify a custom jenkins.war to run the plugin with"
31
+ def server
32
+ require 'jenkins/plugin/tools/server'
33
+ server = Tools::Server.new(spec, options[:home], options[:war])
34
+ server.run!
35
+ end
36
+ map "s" => "server"
37
+
38
+ desc "release", "release to jenkins-ci.org"
39
+ method_option :release, :desc => "deploy as a release (as opposed to a snapshot)", :type => :boolean
40
+ def release
41
+ require 'jenkins/plugin/tools/release'
42
+
43
+ Tools::Release.new(spec,build().file_name, !options[:release]).run
44
+ end
45
+
46
+ desc "version", "show jpi version information"
47
+ def version
48
+ require 'jenkins/plugin/version'
49
+ shell.say Jenkins::Plugin::VERSION
50
+ end
51
+ map ["-v","--version"] => "version"
52
+
53
+ desc "help [COMMAND]", "get help for COMMAND, or for jpi itself"
54
+ def help(command = nil)
55
+ super
56
+ end
57
+
58
+ private
59
+
60
+ def spec
61
+ Specification.find!
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,54 @@
1
+
2
+ module Jenkins
3
+ class Plugin
4
+ class CLI < Thor
5
+ module Formatting
6
+ def task_help(shell, task_name)
7
+ meth = normalize_task_name(task_name)
8
+ task = all_tasks[meth]
9
+ handle_no_task_error(meth) unless task
10
+
11
+ shell.say "usage: #{banner(task)}"
12
+ shell.say
13
+ class_options_help(shell, nil => task.options.map { |_, o| o })
14
+ end
15
+
16
+
17
+ def print_options(shell, options, grp = nil)
18
+ return if options.empty?
19
+ table = options.map do |option|
20
+ prototype = if option.default
21
+ " [#{option.default}]"
22
+ elsif option.type == :boolean
23
+ ""
24
+ elsif option.required?
25
+ " #{option.banner}"
26
+ else
27
+ " [#{option.banner}]"
28
+ end
29
+ aliases = option.aliases.empty? ? "" : option.aliases.join(" ") + ","
30
+ [aliases, "--#{option.name}#{prototype}", "\t",option.description]
31
+ end
32
+ shell.print_table(table, :ident => 2)
33
+ shell.say
34
+ end
35
+
36
+ def help(shell, task)
37
+ list = printable_tasks
38
+ print shell.set_color("jpi", :black, true)
39
+ shell.say <<-USEAGE
40
+ - tools to create, build, develop and release Jenkins plugins
41
+
42
+ Usage: jpi command [arguments] [options]
43
+
44
+ USEAGE
45
+
46
+ shell.say "Commands:"
47
+ shell.print_table(list, :ident => 2, :truncate => true)
48
+ shell.say
49
+ class_options_help(shell)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+
2
+ module Jenkins
3
+ class Plugin
4
+ class CLI
5
+ class Generate < Thor
6
+ include Thor::Actions
7
+
8
+ source_root File.dirname(__FILE__)
9
+
10
+ argument :name
11
+
12
+ desc "publisher", "publisher NAME", :desc => "generate a publish step definition"
13
+ def publisher
14
+ @step_class = "Publisher"
15
+ template('templates/build_step.tt', "models/#{name.downcase}_publisher.rb")
16
+ end
17
+
18
+ desc "builder", "builder NAME", :desc => "generate a build step definition"
19
+ def builder
20
+ @step_class = "Builder"
21
+ template('templates/build_step.tt', "models/#{name.downcase}_builder.rb")
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+
2
+ require 'thor/group'
3
+
4
+ module Jenkins
5
+ class Plugin
6
+ class CLI
7
+ class New < Thor::Group
8
+ include Thor::Actions
9
+
10
+ source_root File.dirname(__FILE__)
11
+
12
+ argument :name
13
+
14
+ def create_gemfile
15
+ template('templates/Gemfile.tt', "#{name}/Gemfile")
16
+ end
17
+
18
+ def create_pluginspec
19
+ git_name = %x[git config user.name].chomp
20
+ git_email = %x[git config user.email].chomp
21
+
22
+ developer_id = git_email.split('@', 2).first || ''
23
+
24
+ # Fallback values.
25
+ git_name = 'TODO: Put your realname here' if git_name.empty?
26
+ git_email = 'email@example.com' if git_email.empty?
27
+
28
+ opts = {
29
+ :developer_id => developer_id.empty? ? 'TODO: Put your jenkins-ci.org username here.' : developer_id,
30
+ :developer_name => "#{git_name} <#{git_email}>"
31
+ }
32
+
33
+ template('templates/pluginspec.tt', "#{name}/#{name}.pluginspec", opts)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+
2
+ source :rubygems
3
+
4
+ gem "jenkins-plugin-runtime", "~> 0.1.13"
@@ -0,0 +1,25 @@
1
+
2
+ class <%= name.capitalize %><%= @step_class %> < Jenkins::Tasks::<%= @step_class %>
3
+
4
+ display_name "<%= name.capitalize %> <%= @step_class.downcase %>"
5
+
6
+ ##
7
+ # Runs before the build begins
8
+ #
9
+ # @param [Jenkins::Model::Build] build the build which will begin
10
+ # @param [Jenkins::Model::Listener] listener the listener for this build.
11
+ def prebuild(build, listener)
12
+ # do any setup that needs to be done before this build runs.
13
+ end
14
+
15
+ ##
16
+ # Runs the step over the given build and reports the progress to the listener.
17
+ #
18
+ # @param [Jenkins::Model::Build] build on which to run this step
19
+ # @param [Jenkins::Launcher] launcher the launcher that can run code on the node running this build
20
+ # @param [Jenkins::Model::Listener] listener the listener for this build.
21
+ def perform(build, launcher, listener)
22
+ # actually perform the build step
23
+ end
24
+
25
+ end
@@ -0,0 +1,28 @@
1
+
2
+ Jenkins::Plugin::Specification.new do |plugin|
3
+ plugin.name = <%= name.inspect %>
4
+ plugin.display_name = <%= (name.capitalize + " Plugin").inspect %>
5
+ plugin.version = '0.0.1'
6
+ plugin.description = 'enter description here'
7
+
8
+ # You should create a wiki-page for your plugin when you publish it, see
9
+ # https://wiki.jenkins-ci.org/display/JENKINS/Hosting+Plugins#HostingPlugins-AddingaWikipage
10
+ # This line makes sure it's listed in your POM.
11
+ plugin.url = 'https://wiki.jenkins-ci.org/display/JENKINS/My+Plugin'
12
+
13
+ # The first argument is your user name for jenkins-ci.org.
14
+ plugin.developed_by <%= config[:developer_id].inspect %>, <%= config[:developer_name].inspect %>
15
+
16
+ # This specifies where your code is hosted.
17
+ # Alternatives include:
18
+ # :github => 'myuser/my-plugin' (without myuser it defaults to jenkinsci)
19
+ # :git => 'git://repo.or.cz/my-plugin.git'
20
+ # :svn => 'https://svn.jenkins-ci.org/trunk/hudson/plugins/my-plugin'
21
+ plugin.uses_repository :github => 'my-plugin'
22
+
23
+ # This is a required dependency for every ruby plugin.
24
+ plugin.depends_on 'ruby-runtime', '0.4'
25
+
26
+ # This is a sample dependency for a Jenkins plugin, 'git'.
27
+ plugin.depends_on 'git', '1.1.11'
28
+ end
@@ -0,0 +1,30 @@
1
+
2
+ module Jenkins
3
+ class Plugin
4
+ module Tools
5
+ class Bundle
6
+
7
+ def initialize(target)
8
+ @target = target
9
+ end
10
+
11
+ def install
12
+ require 'java'
13
+ require 'bundler'
14
+ puts "bundling..."
15
+
16
+ # We set these in ENV instead of passing the --without and --path
17
+ # options because the CLI options are remembered in .bundle/config and
18
+ # will interfere with regular usage of bundle exec / install.
19
+ Bundler.with_clean_env {
20
+ ENV['BUNDLE_APP_CONFIG'] = "#{@target}/vendor/bundle"
21
+ ENV['BUNDLE_WITHOUT'] = "development"
22
+ ENV['BUNDLE_PATH'] = "#{@target}/vendor/gems"
23
+ ENV.delete 'RUBYOPT'
24
+ system('bundle --standalone')
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,107 @@
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 = "http://updates.jenkins-ci.org/download/plugins/#{short_name}/#{version}/#{short_name}.hpi?for=ruby-plugin"
56
+ puts " from #{url}"
57
+ f.write fetch(url).body
58
+ end
59
+ FileUtils.mv cache+".tmp", cache
60
+
61
+ return cache
62
+ end
63
+
64
+ # download with redirect support
65
+ def self.fetch(uri, limit = 10)
66
+ # You should choose better exception.
67
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
68
+
69
+ response = Net::HTTP.get_response(URI.parse(uri))
70
+ case response
71
+ when Net::HTTPSuccess then response
72
+ when Net::HTTPRedirection then fetch(response['location'], limit - 1)
73
+ else
74
+ response.error!
75
+ end
76
+ end
77
+
78
+ # parse manifest file text into a hash
79
+ def parse_manifest(txt)
80
+ # separators
81
+ nl = /\r\n|\n|\r[^\n]/
82
+ secsep = /(#{nl}){2}/
83
+
84
+ txt.split(secsep).reject { |s| s.chomp.length==0 }.map do |section|
85
+ lines = []
86
+ section.split(nl).each do |line|
87
+ if line[0]==0x20
88
+ lines.last << line[1..-1] # continuation of the previous line
89
+ else
90
+ lines << line
91
+ end
92
+ end
93
+
94
+ # convert to hash
95
+ hash = {}
96
+ lines.each do |l|
97
+ (k,v) = l.split(/: /,2)
98
+ hash[k] = v
99
+ end
100
+
101
+ hash
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ 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,80 @@
1
+
2
+ #require 'jenkins/plugin/version'
3
+
4
+ require 'jenkins/plugin/version'
5
+ require 'etc'
6
+
7
+ module Jenkins
8
+ class Plugin
9
+ module Tools
10
+ class Manifest
11
+
12
+ def initialize(spec)
13
+ @spec = spec
14
+ end
15
+
16
+ def write_hpi(io)
17
+ w = Writer.new(io)
18
+ w.put "Manifest-Version", "1.0"
19
+ w.put "Created-By", Jenkins::Plugin::VERSION
20
+ w.put "Build-Ruby-Platform", RUBY_PLATFORM
21
+ w.put "Build-Ruby-Version", RUBY_VERSION
22
+ w.put "Built-By", Etc.getlogin()
23
+
24
+ w.put "Group-Id", "org.jenkins-ci.plugins"
25
+ w.put "Short-Name", @spec.name
26
+ w.put "Long-Name", @spec.display_name
27
+ w.put "Url", @spec.url if @spec.url
28
+
29
+ w.put "Plugin-Class", "ruby.RubyPlugin"
30
+ w.put "Plugin-Version", @spec.version
31
+ w.put "Jenkins-Version", "1.432"
32
+
33
+ w.put "Plugin-Dependencies", @spec.dependencies.map{|k,v| "#{k}:#{v}"}.join(",")
34
+ end
35
+
36
+ def write_hpl(io, loadpath)
37
+ write_hpi(io)
38
+
39
+ w = Writer.new(io)
40
+ w.put "Load-Path", loadpath.to_a.join(':')
41
+ w.put "Lib-Path", "#{Dir.pwd}/lib/"
42
+ w.put "Models-Path", "#{Dir.pwd}/models"
43
+ # Stapler expects view erb/haml scripts to be in the JVM ClassPath
44
+ w.put "Class-Path", "#{Dir.pwd}/views" if File.exists?("#{Dir.pwd}/views")
45
+ # Directory for static images, javascript, css, etc. of this plugin.
46
+ # The static resources are mapped under #CONTEXTPATH/plugin/SHORTNAME/
47
+ w.put "Resource-Path", "#{Dir.pwd}/static"
48
+ end
49
+
50
+ class Writer
51
+
52
+ MAX_LENGTH = 72.to_i
53
+
54
+ def initialize(io)
55
+ @io = io
56
+ end
57
+
58
+ def put(key, value)
59
+ @io.puts "#{key}: #{manifest_truncate(value)}"
60
+ end
61
+
62
+ def manifest_truncate(message)
63
+ if message.length < MAX_LENGTH
64
+ return message
65
+ end
66
+
67
+ line = message[0 ... MAX_LENGTH] + "\n"
68
+ offset = MAX_LENGTH
69
+
70
+ while offset < message.length
71
+ line += " #{message[offset ... (offset + MAX_LENGTH - 1)]}\n"
72
+ offset += (MAX_LENGTH - 1)
73
+ end
74
+ return line
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,60 @@
1
+ require 'jenkins/plugin/tools/bundle'
2
+ require 'jenkins/plugin/tools/manifest'
3
+ require 'zip/zip'
4
+
5
+ module Jenkins
6
+ class Plugin
7
+ module Tools
8
+ class Package
9
+
10
+ def initialize(spec,target)
11
+ @target = target
12
+ @spec = spec
13
+ end
14
+
15
+ # where to generate the package?
16
+ def file_name
17
+ file_name = "#{@target}/#{@spec.name}.hpi"
18
+ end
19
+
20
+ def build
21
+ FileUtils.mkdir_p @target
22
+
23
+ Bundle.new(@target).install
24
+
25
+ manifest = Manifest.new(@spec)
26
+
27
+ File.delete file_name if File.exists?(file_name)
28
+
29
+ Zip::ZipFile.open(file_name, Zip::ZipFile::CREATE) do |zipfile|
30
+ zipfile.get_output_stream("META-INF/MANIFEST.MF") do |f|
31
+ manifest.write_hpi(f)
32
+ f.puts "Bundle-Path: vendor/gems"
33
+ end
34
+ zipfile.mkdir("WEB-INF/classes")
35
+
36
+ ["lib","models","#{@target}/vendor"].each do |d|
37
+ Dir.glob("#{d}/**/*") do |f|
38
+ if !File.directory? f
39
+ p = f.gsub("#{@target}/",'')
40
+ if p !~ %r{/cache/}
41
+ zipfile.add("WEB-INF/classes/#{p}",f)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # stapler expects views to be directly in the classpath without any prefix
48
+ Dir.glob("views/**/*") do |f|
49
+ if !File.directory? f
50
+ zipfile.add("WEB-INF/classes/#{f[6..-1]}",f)
51
+ end
52
+ end
53
+ end
54
+ puts "#{@spec.name} plugin #{@spec.version} built to #{file_name}"
55
+
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,70 @@
1
+ require 'jenkins/plugin/tools/bundle'
2
+ require 'jenkins/plugin/tools/manifest'
3
+ require 'jenkins/jenkins-ci.org/credential'
4
+ require 'net/http'
5
+ require 'erb'
6
+
7
+ module Jenkins
8
+ class Plugin
9
+ module Tools
10
+ # task for deploying a plugin
11
+ class Release
12
+
13
+ def initialize(spec,hpi,snapshot)
14
+ @spec = spec
15
+ @hpi = hpi # hpi file to release
16
+ @snapshot = snapshot # if true, deploy as a snapshot, otherwise as release
17
+ end
18
+
19
+ def check_error(rsp)
20
+ # in case of 401 Unauthorized, the server just resets the connection and Net::HTTP fails to parse the response,
21
+ # so we don't really get any meaningful error message.
22
+ rsp.value # TODO: is this how we check for the error?
23
+ end
24
+
25
+ def each_developer
26
+ @spec.developers.each do |id, name|
27
+ email = ''
28
+ if name =~ /^(.*)<([^>]+)>$/
29
+ name = $1
30
+ email = $2.strip
31
+ end
32
+
33
+ yield id, name.strip, email
34
+ end
35
+ end
36
+
37
+ def run
38
+ cred = Jenkins::CiOrg::Credential.new
39
+ if !cred.has_credential? then
40
+ raise Exception.new("no credential available to connect to jenkins-ci.org. Please create ~/.jenkins-ci.org. See https://wiki.jenkins-ci.org/display/JENKINS/Dot+Jenkins+Ci+Dot+Org")
41
+ end
42
+
43
+ http = Net::HTTP.new("maven.jenkins-ci.org",8081)
44
+
45
+ puts @snapshot ? "deploying as a snapshot" : "deploying as a release"
46
+ puts "Generating POM"
47
+ version = @snapshot ? @spec.version+"-SNAPSHOT" : @spec.version
48
+ pom = ERB.new(File.read(File.dirname(__FILE__)+"/templates/release-pom.xml.erb")).result(binding)
49
+
50
+ path = "/content/repositories/#{@snapshot?'snapshots':'releases'}/org/jenkins-ci/ruby-plugins/#{@spec.name}/#{version}/#{@spec.name}-#{version}"
51
+ req = Net::HTTP::Put.new("#{path}.pom")
52
+ req.body = pom
53
+ req.basic_auth(cred.user_name,cred.password)
54
+ check_error(http.request(req))
55
+
56
+ puts "Uploading #{@hpi}"
57
+ File.open(@hpi,'r') do |f|
58
+ req = Net::HTTP::Put.new("#{path}.hpi")
59
+ req.body_stream = f
60
+ req.basic_auth(cred.user_name,cred.password)
61
+ req.content_length = File.size(@hpi)
62
+ check_error(http.request(req))
63
+ end
64
+
65
+ puts "See http://maven.jenkins-ci.org"+File.dirname(path)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,61 @@
1
+ require 'net/http'
2
+
3
+ module Jenkins
4
+ class Plugin
5
+ module Tools
6
+ class Resolver
7
+
8
+ def initialize(spec, dir)
9
+ @spec = spec
10
+ @dir = dir
11
+ FileUtils.mkdir_p(dir) unless File.directory? @dir
12
+ end
13
+
14
+ def resolve!
15
+ @spec.dependencies.each do |name, version|
16
+ FileUtils.cp resolve(name, version), @dir
17
+ end
18
+ end
19
+
20
+ def resolve(short_name,version)
21
+ # this is where we expect the retrieved file to be
22
+ cache = File.expand_path "~/.jenkins/cache/plugins/#{short_name}/#{version}/#{short_name}.hpi"
23
+
24
+ return cache if File.exists?(cache)
25
+
26
+ # now we start looking for places to find them
27
+
28
+ # is it in the local maven2 repository?
29
+ maven = File.expand_path "~/.m2/repository/org/jenkins-ci/plugins/#{short_name}/#{version}/#{short_name}-#{version}.hpi"
30
+ return maven if File.exists?(maven)
31
+
32
+ # download from the community update center
33
+ FileUtils.mkdir_p(File.dirname(cache))
34
+ open(cache+".tmp","wb") do |f|
35
+ puts "Downloading #{short_name} #{version}"
36
+ url = "http://updates.jenkins-ci.org/download/plugins/#{short_name}/#{version}/#{short_name}.hpi?for=ruby-plugin"
37
+ puts " from #{url}"
38
+ f.write fetch(url).body
39
+ end
40
+ FileUtils.mv cache+".tmp", cache
41
+
42
+ return cache
43
+ end
44
+
45
+ # download with redirect support
46
+ def fetch(uri, limit = 10)
47
+ # You should choose better exception.
48
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
49
+
50
+ response = Net::HTTP.get_response(URI.parse(uri))
51
+ case response
52
+ when Net::HTTPSuccess then response
53
+ when Net::HTTPRedirection then fetch(response['location'], limit - 1)
54
+ else
55
+ response.error!
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,49 @@
1
+ require 'jenkins/plugin/tools/loadpath'
2
+ require 'jenkins/plugin/tools/resolver'
3
+ require 'jenkins/plugin/tools/manifest'
4
+ require 'jenkins/war'
5
+ require 'fileutils'
6
+
7
+ module Jenkins
8
+ class Plugin
9
+ module Tools
10
+ class Server
11
+
12
+ def initialize(spec, workdir, war)
13
+ @spec = spec
14
+ @workdir = workdir
15
+ @plugindir = "#{workdir}/plugins"
16
+ @war = war || Jenkins::War::LOCATION
17
+ end
18
+
19
+ def run!
20
+ FileUtils.mkdir_p(@plugindir)
21
+ loadpath = Jenkins::Plugin::Tools::Loadpath.new
22
+ manifest = Jenkins::Plugin::Tools::Manifest.new(@spec)
23
+ resolver = Jenkins::Plugin::Tools::Resolver.new(@spec, @plugindir)
24
+
25
+ resolver.resolve!
26
+ # generate the plugin manifest
27
+
28
+ File.open("#{@plugindir}/#{@spec.name}.hpl",mode="w+") do |f|
29
+ manifest.write_hpl(f, loadpath)
30
+ end
31
+
32
+
33
+ # execute Jenkins
34
+ args = []
35
+ args << "java"
36
+ args << "-Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
37
+ args << "-DJENKINS_HOME=#{@workdir}"
38
+ args << "-Dstapler.trace=true"
39
+ args << "-Ddebug.YUI=true"
40
+ # args << "-Djruby.debug.loadService=true"
41
+ # args << "-Djruby.debug.loadService.timing=true"
42
+ args << "-jar"
43
+ args << @war
44
+ exec *args
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2
+ <modelVersion>4.0.0</modelVersion>
3
+ <parent>
4
+ <groupId>org.jenkins-ci.plugins</groupId>
5
+ <artifactId>plugin</artifactId>
6
+ <version>1.420</version>
7
+ </parent>
8
+
9
+ <groupId>org.jenkins-ci.ruby-plugins</groupId>
10
+ <artifactId><%= @spec.name %></artifactId>
11
+ <version><%= version %></version>
12
+ <name><%= @spec.display_name %></name>
13
+ <description><%= @spec.description %></description>
14
+ <packaging>hpi</packaging>
15
+
16
+ <% if @spec.url %>
17
+ <url><%= @spec.url %></url>
18
+ <% end %>
19
+
20
+ <repositories>
21
+ <repository>
22
+ <id>m.g.o-public</id>
23
+ <url>http://maven.glassfish.org/content/groups/public/</url>
24
+ </repository>
25
+ </repositories>
26
+
27
+ <developers>
28
+ <% each_developer do |id, name, email| %>
29
+ <developer>
30
+ <id><%= id %></id>
31
+ <name><%= name %></name>
32
+ <% if not email.empty? %>
33
+ <email><%= email %></email>
34
+ <% end %>
35
+ </developer>
36
+ <% end %>
37
+ </developers>
38
+
39
+ <dependencies>
40
+ <% @spec.dependencies.each do |k,v| %>
41
+ <dependency>
42
+ <groupId>org.jenkins-ci.plugins</groupId><!-- TODO: needs to figure out correct groupId -->
43
+ <artifactId><%= k %></artifactId>
44
+ <version><%= v %></version>
45
+ </dependency>
46
+ <% end %>
47
+ </dependencies>
48
+
49
+ <% if @spec.repository %>
50
+ <scm>
51
+ <connection>scm:<%= @spec.repository[:type] %>:<%= @spec.repository[:url] %></connection>
52
+ </scm>
53
+ <% end %>
54
+ </project>
@@ -0,0 +1,5 @@
1
+ module Jenkins
2
+ class Plugin
3
+ VERSION = "0.3.0"
4
+ end
5
+ end
@@ -0,0 +1,52 @@
1
+ require 'jenkins/plugin/version'
2
+ require 'jenkins/plugin/specification'
3
+ require 'jenkins/plugin/tools/hpi'
4
+ require 'jenkins/plugin/tools/loadpath'
5
+ require 'zip/zip'
6
+
7
+ module Jenkins
8
+ # given the IO handle, produce the basic manifest entries that are common between hpi and hpl formats
9
+
10
+ def self.spec
11
+ @spec ||= Jenkins::Plugin::Specification.load(Dir['*.pluginspec'].first)
12
+ end
13
+
14
+ class Rake
15
+ def self.install_tasks
16
+ self.new.install
17
+ end
18
+
19
+ include ::Rake::DSL if defined? ::Rake::DSL
20
+
21
+ def install
22
+ desc "Directory used as JENKINS_HOME during 'rake server'"
23
+ directory work = "work"
24
+
25
+ desc "remove built artifacts"
26
+ task :clean do
27
+ sh "rm -rf pkg"
28
+ sh "rm -rf vendor"
29
+ end
30
+
31
+ desc "output the development servers loadpath"
32
+ task :loadpath do
33
+ loadpath = Jenkins::Plugin::Tools::Loadpath.new(:default)
34
+ puts loadpath.to_path
35
+ end
36
+
37
+ desc "package up stuff into HPI file"
38
+ task :package do
39
+ require 'jenkins/plugin/tools/package'
40
+ Jenkins::Plugin::Tools::Package.new(Jenkins.spec,"pkg").build
41
+ end
42
+
43
+ desc "run a Jenkins server with this plugin"
44
+ task :server do
45
+ require 'jenkins/plugin/tools/server'
46
+
47
+ server = Jenkins::Plugin::Tools::Server.new(Jenkins.spec, "work")
48
+ server.run!
49
+ end
50
+ end
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jpi
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.3.0
6
+ platform: ruby
7
+ authors:
8
+ - Charles Lowell
9
+ - "J\xC3\xB8rgen P. Tjern\xC3\xB8"
10
+ - Kohsuke Kawaguchi
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+
15
+ date: 2011-12-19 00:00:00 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rubyzip
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: "0"
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: thor
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :runtime
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: jenkins-war
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "1.427"
48
+ type: :runtime
49
+ version_requirements: *id003
50
+ - !ruby/object:Gem::Dependency
51
+ name: bundler
52
+ prerelease: false
53
+ requirement: &id004 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ version: 1.1.rc2
59
+ type: :runtime
60
+ version_requirements: *id004
61
+ - !ruby/object:Gem::Dependency
62
+ name: jenkins-plugin-runtime
63
+ prerelease: false
64
+ requirement: &id005 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.1.13
70
+ type: :runtime
71
+ version_requirements: *id005
72
+ - !ruby/object:Gem::Dependency
73
+ name: rspec
74
+ prerelease: false
75
+ requirement: &id006 !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: "2.0"
81
+ type: :development
82
+ version_requirements: *id006
83
+ - !ruby/object:Gem::Dependency
84
+ name: cucumber
85
+ prerelease: false
86
+ requirement: &id007 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ version: "1.0"
92
+ type: :development
93
+ version_requirements: *id007
94
+ description: Allows you to generate a new Ruby plugin project, build it, test it in Jenkins and release it to the Jenkins Update Center.
95
+ email:
96
+ - cowboyd@thefrontside.net
97
+ executables:
98
+ - jpi
99
+ extensions: []
100
+
101
+ extra_rdoc_files: []
102
+
103
+ files:
104
+ - .gitignore
105
+ - Gemfile
106
+ - README.md
107
+ - Rakefile
108
+ - bin/jpi
109
+ - features/create-new-plugin.feature
110
+ - features/support/create_new_plugin_steps.rb
111
+ - features/support/directory_structure.rb
112
+ - features/support/work.rb
113
+ - jpi.gemspec
114
+ - lib/jenkins/jenkins-ci.org/credential.rb
115
+ - lib/jenkins/plugin/cli.rb
116
+ - lib/jenkins/plugin/cli/formatting.rb
117
+ - lib/jenkins/plugin/cli/generate.rb
118
+ - lib/jenkins/plugin/cli/new.rb
119
+ - lib/jenkins/plugin/cli/templates/Gemfile.tt
120
+ - lib/jenkins/plugin/cli/templates/build_step.tt
121
+ - lib/jenkins/plugin/cli/templates/pluginspec.tt
122
+ - lib/jenkins/plugin/tools/bundle.rb
123
+ - lib/jenkins/plugin/tools/hpi.rb
124
+ - lib/jenkins/plugin/tools/loadpath.rb
125
+ - lib/jenkins/plugin/tools/manifest.rb
126
+ - lib/jenkins/plugin/tools/package.rb
127
+ - lib/jenkins/plugin/tools/release.rb
128
+ - lib/jenkins/plugin/tools/resolver.rb
129
+ - lib/jenkins/plugin/tools/server.rb
130
+ - lib/jenkins/plugin/tools/templates/release-pom.xml.erb
131
+ - lib/jenkins/plugin/version.rb
132
+ - lib/jenkins/rake.rb
133
+ homepage: https://github.com/jenkinsci/jpi.rb
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
+ version: "0"
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: "0"
153
+ requirements: []
154
+
155
+ rubyforge_project:
156
+ rubygems_version: 1.8.9
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: Tools for creating and building Jenkins Ruby plugins
160
+ test_files:
161
+ - features/create-new-plugin.feature
162
+ - features/support/create_new_plugin_steps.rb
163
+ - features/support/directory_structure.rb
164
+ - features/support/work.rb