├── .gitignore
├── .travis.yml
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── lib
├── rbenv.rb
└── rbenv
│ ├── errors.rb
│ ├── invoke.rb
│ ├── rack.rb
│ ├── scm.rb
│ └── semaphore.rb
├── models
└── rbenv_wrapper.rb
├── pom.xml
├── rbenv.pluginspec
├── spec
├── lib
│ ├── rbenv
│ │ ├── invoke_spec.rb
│ │ └── semaphore_spec.rb
│ └── rbenv_spec.rb
└── spec_helper.rb
└── views
└── rbenv_wrapper
├── config.erb
├── help-configure_opts.html
├── help-gem_list.html
├── help-ignore_local_version.html
├── help-rbenv_repository.html
├── help-rbenv_revision.html
├── help-rbenv_root.html
├── help-ruby_build_repository.html
├── help-ruby_build_revision.html
├── help-ruby_configure_opts.html
└── help-version.html
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swo
2 | *.swp
3 | /.bundle
4 | /.rbenv-version
5 | /.ruby-version
6 | /Gemfile.lock
7 | /pkg
8 | /work
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - jruby-19mode
4 | script:
5 | - bundle exec rake spec
6 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "jenkins-plugin-runtime", "~> 0.2.3"
4 | gem "sinatra", "~> 1.4.2"
5 |
6 | group :development do
7 | gem "jpi", "~> 0.4.0"
8 | gem "jruby-openssl", "~> 0.8.8"
9 | gem "rake", "~> 10.0.4"
10 | gem "rspec", "~> 2.13.0"
11 | end
12 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Jenkins CI
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jenkins rbenv plugin
2 |
3 | [](https://travis-ci.org/jenkinsci/rbenv-plugin)
4 |
5 | rbenv build wrapper for Jenkins
6 |
7 | ## Contributing
8 |
9 | 1. Fork it
10 | 2. Create your feature branch (`git checkout -b my-new-feature`)
11 | 3. Commit your changes (`git commit -am 'Add some feature'`)
12 | 4. Push to the branch (`git push origin my-new-feature`)
13 | 5. Create new Pull Request
14 |
15 | ## Building the plugin from source
16 |
17 | Follow these steps if you are interested in hacking on the plugin.
18 |
19 | Find a version of JRuby to install via `rbenv-install -l`
20 |
21 | Install JRuby
22 |
23 | rbenv install jruby-1.6.7
24 | rbenv local jruby-1.6.7
25 |
26 | Install the development gems
27 |
28 | bundle install
29 | rbenv rehash
30 |
31 | Build the plugin
32 |
33 | rake package
34 |
35 |
36 | Look at [Getting Started with Ruby Plugins](https://github.com/jenkinsci/jenkins.rb/wiki/Getting-Started-With-Ruby-Plugins) to get up to speed on things.
37 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "rake/testtask"
2 | require "rake/clean"
3 |
4 | ## jpi ##
5 | begin
6 | require "jenkins/rake"
7 | Jenkins::Rake.install_tasks
8 | task :default => :package
9 | rescue LoadError
10 | end
11 |
12 | ## rspec ##
13 | begin
14 | require "rspec/core/rake_task"
15 | RSpec::Core::RakeTask.new(:spec)
16 | rescue LoadError
17 | end
18 |
--------------------------------------------------------------------------------
/lib/rbenv.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "delegate"
4 | require "rbenv/errors"
5 | require "rbenv/invoke"
6 | require "rbenv/scm"
7 | require "rbenv/semaphore"
8 |
9 | module Rbenv
10 | class Environment < SimpleDelegator
11 | include Rbenv::InvokeCommand
12 | include Rbenv::Semaphore
13 |
14 | def initialize(build_wrapper)
15 | @build_wrapper = build_wrapper
16 | super(build_wrapper)
17 | end
18 |
19 | def setup!
20 | install!
21 | detect_version!
22 |
23 | # To avoid starting multiple build jobs, acquire lock during installation
24 | synchronize("#{rbenv_root}/.lock") do
25 | versions = capture(rbenv("versions", "--bare")).strip.split
26 | unless versions.include?(version)
27 | update!
28 | listener << "Installing #{version}..."
29 | run(rbenv("install", version), {out: listener})
30 | listener << "Installed #{version}."
31 | end
32 | gem_install!
33 | end
34 |
35 | build.env["RBENV_ROOT"] = rbenv_root
36 | build.env['RBENV_VERSION'] = version
37 | # Set ${RBENV_ROOT}/bin in $PATH to allow invoke rbenv from shell
38 | build.env["PATH+RBENV_BIN"] = "#{rbenv_root}/bin"
39 | # Set ${RBENV_ROOT}/bin in $PATH to allow invoke binstubs from shell
40 | build.env["PATH+RBENV_SHIMS"] = "#{rbenv_root}/shims"
41 | end
42 |
43 | private
44 | def install!
45 | unless test("[ -d #{rbenv_root.shellescape} ]")
46 | listener << "Installing rbenv..."
47 | run(Rbenv::SCM::Git.new(rbenv_repository, rbenv_revision, rbenv_root).checkout, {out: listener})
48 | listener << "Installed rbenv."
49 | end
50 |
51 | unless test("[ -d #{plugin_path("ruby-build").shellescape} ]")
52 | listener << "Installing ruby-build..."
53 | run(Rbenv::SCM::Git.new(ruby_build_repository, ruby_build_revision, plugin_path("ruby-build")).checkout, {out: listener})
54 | listener << "Installed ruby-build..."
55 | end
56 | end
57 |
58 | def detect_version!
59 | if ignore_local_version
60 | listener << "Just ignoring local Ruby version."
61 | else
62 | # Respect local Ruby version if defined in the workspace
63 | get_local_version(build.workspace.to_s).tap do |version|
64 | if version
65 | listener << "Use local Ruby version #{version}."
66 | self.version = version # call RbenvWrapper's accessor
67 | end
68 | end
69 | end
70 | end
71 |
72 | def get_local_version(path)
73 | str = capture("cd #{path.shellescape} && #{rbenv("local")} 2>/dev/null || true").strip
74 | not(str.empty?) ? str : nil
75 | end
76 |
77 | def update!
78 | # To update definitions, update rbenv before installing ruby
79 | listener << "Updating rbenv..."
80 | run(Rbenv::SCM::Git.new(rbenv_repository, rbenv_revision, rbenv_root).sync, {out: listener})
81 | listener << "Updated rbenv."
82 |
83 | listener << "Updating ruby-build..."
84 | run(Rbenv::SCM::Git.new(ruby_build_repository, ruby_build_revision, plugin_path("ruby-build")).sync, {out: listener})
85 | listener << "Updated ruby-build."
86 | end
87 |
88 | def gem_install!
89 | # Run rehash everytime before invoking gem
90 | run(rbenv("rehash"), {out: listener})
91 |
92 | list = capture(rbenv("exec", "gem", "list")).strip.split
93 | gem_list.split(",").each do |gem|
94 | unless list.include?(gem)
95 | listener << "Installing #{gem}..."
96 | run(rbenv("exec", "gem", "install", gem), {out: listener})
97 | listener << "Installed #{gem}."
98 | end
99 | end
100 |
101 | # Run rehash everytime after invoking gem
102 | run(rbenv("rehash"), {out: listener})
103 | end
104 |
105 | def rbenv(*args)
106 | (["env", "RBENV_ROOT=#{rbenv_root}",
107 | "RBENV_VERSION=#{version}",
108 | "CONFIGURE_OPTS=#{configure_opts}",
109 | "RUBY_CONFIGURE_OPTS=#{ruby_configure_opts}",
110 | "#{rbenv_root}/bin/rbenv"] + args).shelljoin
111 | end
112 |
113 | def plugin_path(name)
114 | File.join(rbenv_root, "plugins", name)
115 | end
116 | end
117 | end
118 |
--------------------------------------------------------------------------------
/lib/rbenv/errors.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | module Rbenv
4 | class RbenvError < StandardError
5 | end
6 |
7 | class CommandError < RbenvError
8 | end
9 |
10 | class LockError < RbenvError
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/rbenv/invoke.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "rbenv/errors"
4 | require "stringio"
5 |
6 | module Rbenv
7 | module InvokeCommand
8 | def capture(command, options={})
9 | options = {out: StringIO.new}.merge(options)
10 | out = options[:out]
11 | run(command, options)
12 | out.rewind
13 | out.read
14 | end
15 |
16 | def run(command, options={})
17 | unless test(command, options)
18 | raise(CommandError.new("failed: #{command.inspect}"))
19 | end
20 | end
21 |
22 | def test(command, options={})
23 | invoke(command, options) == 0
24 | end
25 |
26 | def invoke(command, options={})
27 | launcher.execute("bash", "-c", command, options)
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/rbenv/rack.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "java"
4 | require "sinatra"
5 |
6 | module Rbenv
7 | class RackApplication < Sinatra::Base
8 | # GET /descriptorByName/rbenv-RbenvWrapper/ping
9 | get "/ping" do
10 | "pong"
11 | end
12 |
13 | get "/checkVersion" do
14 | value = params[:value]
15 | if value.nil? or value.to_s.strip.empty?
16 | response.body = Java.hudson.util.FormValidation.error("The version string must not be empty.").renderHtml
17 | else
18 | response.body = Java.hudson.util.FormValidation.ok().renderHtml
19 | end
20 | end
21 |
22 | get "/checkRbenvRoot" do
23 | value = params[:value]
24 | if value.nil? or value.to_s.strip.empty?
25 | response.body = Java.hudson.util.FormValidation.error("The RBENV_ROOT must not be empty.").renderHtml
26 | else
27 | response.body = Java.hudson.util.FormValidation.ok().renderHtml
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/rbenv/scm.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "shellwords"
4 |
5 | module Rbenv
6 | module SCM
7 | class Base
8 | def initialize(repository, revision, destination)
9 | @repository = repository
10 | @revision = revision
11 | @destination = destination
12 | end
13 | attr_reader :repository
14 | attr_reader :revision
15 | attr_reader :destination
16 | end
17 |
18 | class Git < Base
19 | def checkout
20 | execute = []
21 | execute << "git clone #{repository.shellescape} #{destination.shellescape}"
22 | execute << "cd #{destination.shellescape}"
23 | execute << "git checkout #{revision.shellescape}"
24 | execute.join(" && ")
25 | end
26 |
27 | def sync
28 | execute = []
29 | execute << "cd #{destination.shellescape}"
30 | execute << "git checkout #{revision.shellescape}"
31 | execute << "git pull"
32 | execute.join(" && ")
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/rbenv/semaphore.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "rbenv/errors"
4 |
5 | module Rbenv
6 | module Semaphore
7 | DEFAULT_ACQUIRE_MAX = 100
8 | DEFAULT_ACQUIRE_WAIT = 10
9 | DEFAULT_RELEASE_MAX = 100
10 | DEFAULT_RELEASE_WAIT = 10
11 |
12 | def synchronize(dir, options={})
13 | begin
14 | acquire_lock(dir, options)
15 | yield
16 | ensure
17 | release_lock(dir, options)
18 | end
19 | end
20 |
21 | def acquire_lock(dir, options={})
22 | max = options.fetch(:acquire_max, DEFAULT_ACQUIRE_MAX)
23 | wait = options.fetch(:acquire_wait, DEFAULT_ACQUIRE_WAIT)
24 | max.times do
25 | if test("mkdir #{dir.shellescape}")
26 | return true
27 | else
28 | sleep(wait)
29 | end
30 | end
31 | raise(LockError.new("could not acquire lock in #{max * wait} seconds."))
32 | end
33 |
34 | def release_lock(dir, options={})
35 | max = options.fetch(:release_max, DEFAULT_RELEASE_MAX)
36 | wait = options.fetch(:release_wait, DEFAULT_RELEASE_WAIT)
37 | max.times do
38 | if test("rm -rf #{dir.shellescape}")
39 | return true
40 | else
41 | sleep(wait)
42 | end
43 | end
44 | raise(LockError.new("could not release lock in #{max * wait} secs."))
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/models/rbenv_wrapper.rb:
--------------------------------------------------------------------------------
1 | require "java"
2 | require "rbenv"
3 | require "rbenv/rack"
4 | require "jenkins/rack"
5 |
6 | class RbenvDescriptor < Jenkins::Model::DefaultDescriptor
7 | DEFAULT_VERSION = "2.2.0"
8 | DEFAULT_GEM_LIST = "bundler,rake"
9 | DEFAULT_IGNORE_LOCAL_VERSION = false
10 | DEFAULT_RBENV_ROOT = "$HOME/.rbenv"
11 | DEFAULT_RBENV_REPOSITORY = "https://github.com/sstephenson/rbenv.git"
12 | DEFAULT_RBENV_REVISION = "master"
13 | DEFAULT_RUBY_BUILD_REPOSITORY = "https://github.com/sstephenson/ruby-build.git"
14 | DEFAULT_RUBY_BUILD_REVISION = "master"
15 | DEFAULT_CONFIGURE_OPTS = ""
16 | DEFAULT_RUBY_CONFIGURE_OPTS = ""
17 |
18 | include Jenkins::RackSupport
19 | def call(env)
20 | Rbenv::RackApplication.new.call(env)
21 | end
22 | end
23 |
24 | class RbenvWrapper < Jenkins::Tasks::BuildWrapper
25 | TRANSIENT_INSTANCE_VARIABLES = [:build, :launcher, :listener]
26 | class << self
27 | def transient?(x)
28 | # return true for a variable which should not be serialized
29 | TRANSIENT_INSTANCE_VARIABLES.include?(x.to_s.to_sym)
30 | end
31 | end
32 |
33 | describe_as Java.hudson.tasks.BuildWrapper, :with => RbenvDescriptor
34 | display_name "rbenv build wrapper"
35 |
36 | # The default values should be set on both instantiation and deserialization.
37 | def initialize(attrs={})
38 | from_hash(attrs)
39 | end
40 |
41 | attr_reader :build
42 | attr_reader :launcher
43 | attr_reader :listener
44 |
45 | attr_accessor :version
46 | attr_accessor :gem_list
47 | attr_accessor :ignore_local_version
48 | attr_accessor :rbenv_root
49 | attr_accessor :rbenv_repository
50 | attr_accessor :rbenv_revision
51 | attr_accessor :ruby_build_repository
52 | attr_accessor :ruby_build_revision
53 | attr_accessor :configure_opts
54 | attr_accessor :ruby_configure_opts
55 |
56 | # Will be invoked by jruby-xstream after deserialization from configuration file.
57 | def read_completed()
58 | from_hash({})
59 | end
60 |
61 | def setup(build, launcher, listener)
62 | @build = build
63 | @launcher = launcher
64 | @listener = listener
65 | Rbenv::Environment.new(self).setup!
66 | end
67 |
68 | def to_hash()
69 | {
70 | "version" => @version,
71 | "gem_list" => @gem_list,
72 | "ignore_local_version" => @ignore_local_version,
73 | "rbenv_root" => @rbenv_root,
74 | "rbenv_repository" => @rbenv_repository,
75 | "rbenv_revision" => @rbenv_revision,
76 | "ruby_build_repository" => @ruby_build_repository,
77 | "ruby_build_revision" => @ruby_build_revision,
78 | "configure_opts" => @configure_opts,
79 | "ruby_configure_opts" => @ruby_configure_opts,
80 | }
81 | end
82 |
83 | private
84 | def from_hash(hash={})
85 | @version = string(hash.fetch("version", @version), RbenvDescriptor::DEFAULT_VERSION)
86 | @gem_list = string(hash.fetch("gem_list", @gem_list), RbenvDescriptor::DEFAULT_GEM_LIST)
87 | @ignore_local_version = boolean(hash.fetch("ignore_local_version", @ignore_local_version), RbenvDescriptor::DEFAULT_IGNORE_LOCAL_VERSION)
88 | @rbenv_root = string(hash.fetch("rbenv_root", @rbenv_root), RbenvDescriptor::DEFAULT_RBENV_ROOT)
89 | @rbenv_repository = string(hash.fetch("rbenv_repository", @rbenv_repository), RbenvDescriptor::DEFAULT_RBENV_REPOSITORY)
90 | @rbenv_revision = string(hash.fetch("rbenv_revision", @rbenv_revision), RbenvDescriptor::DEFAULT_RBENV_REVISION)
91 | @ruby_build_repository = string(hash.fetch("ruby_build_repository", @ruby_build_repository), RbenvDescriptor::DEFAULT_RUBY_BUILD_REPOSITORY)
92 | @ruby_build_revision = string(hash.fetch("ruby_build_revision", @ruby_build_revision), RbenvDescriptor::DEFAULT_RUBY_BUILD_REVISION)
93 | @configure_opts = string(hash.fetch("configure_opts", @configure_opts), RbenvDescriptor::DEFAULT_CONFIGURE_OPTS)
94 | @ruby_configure_opts = string(hash.fetch("ruby_configure_opts", @ruby_configure_opts), RbenvDescriptor::DEFAULT_RUBY_CONFIGURE_OPTS)
95 | end
96 |
97 | # Jenkins may return empty string as attribute value which we must ignore
98 | def string(value, default_value=nil)
99 | s = value.to_s
100 | if s.empty?
101 | default_value
102 | else
103 | s
104 | end
105 | end
106 |
107 | def boolean(value, default_value=false)
108 | if FalseClass === value or TrueClass === value
109 | value
110 | else
111 | # rbenv plugin (<= 0.0.15) stores boolean values as String
112 | case value.to_s
113 | when /false/i then false
114 | when /true/i then true
115 | else
116 | default_value
117 | end
118 | end
119 | end
120 | end
121 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
--prefix
3 | --enable-shared
3 |