tara 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 99f4bc434bfa007f054a82265445cd9846937302
4
+ data.tar.gz: 270a01f1559d5747ecc0d50834ba1b0b98336a39
5
+ SHA512:
6
+ metadata.gz: dc93d8fc594db3d1c1f0a415166f3ac5b9151034b7f7d7fefff32b8f244c882a93c64709878b2f8d392659018592710a160457402efaafdd3b3e6819d2b43192
7
+ data.tar.gz: 156522d2729c5ca0ffe381d183030add5dd930ccda05eeb07d4efaae8cc4af147969b0397f4788231efdd694e8ec22c97a165adc33eec2a868ff4f0fa131bc8b
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'tara'
6
+
7
+
8
+ exit(Tara::Cli.new(ARGV).run)
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler'
4
+ require 'pathname'
5
+ require 'tmpdir'
6
+
7
+
8
+ module Tara
9
+ TaraError = Class.new(StandardError)
10
+ ExecError = Class.new(TaraError)
11
+ NotFoundError = Class.new(TaraError)
12
+ TooManyRedirectsError = Class.new(TaraError)
13
+ UnknownResponseError = Class.new(TaraError)
14
+ end
15
+
16
+ require 'tara/archive'
17
+ require 'tara/archiver'
18
+ require 'tara/cli'
19
+ require 'tara/executable'
20
+ require 'tara/fetcher'
21
+ require 'tara/installer'
22
+ require 'tara/shell'
@@ -0,0 +1,90 @@
1
+ # encoding: utf-8
2
+
3
+ module Tara
4
+ class Archive
5
+ def initialize(config={})
6
+ @config = config
7
+ @config[:app_dir] ||= Dir.pwd
8
+ @config[:app_name] ||= File.basename(@config[:app_dir])
9
+ @config[:build_dir] ||= File.join(@config[:app_dir], 'build')
10
+ @config[:target_dir] ||= @config[:build_dir]
11
+ @config[:download_dir] ||= File.join(@config[:build_dir], 'downloads')
12
+ @config[:archive_name] ||= @config[:app_name] + '.tgz'
13
+ @config[:files] ||= %w[lib/**/*.rb]
14
+ @config[:executables] ||= %w[bin/*]
15
+ @config[:target] ||= 'linux-x86_64'
16
+ @config[:traveling_ruby_version] ||= '20150210'
17
+ @config[:without_groups] ||= %w[development test]
18
+ end
19
+
20
+ def self.create(config={})
21
+ new(config).create
22
+ end
23
+
24
+ def create
25
+ Dir.mktmpdir do |tmp_dir|
26
+ project_dir = Pathname.new(@config[:app_dir])
27
+ package_dir = Pathname.new(tmp_dir)
28
+ target_dir = Pathname.new(@config[:target_dir])
29
+ install_dependencies(package_dir, fetcher)
30
+ copy_source(project_dir, package_dir)
31
+ copy_executables(project_dir, package_dir)
32
+ Dir.chdir(tmp_dir) do
33
+ create_archive(target_dir)
34
+ end
35
+ File.join(target_dir, @config[:archive_name])
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ DOT_PATH = Pathname.new('.')
42
+
43
+ def copy_source(project_dir, package_dir)
44
+ @config[:files].each do |glob_string|
45
+ Pathname.glob(project_dir.join(glob_string)).each do |file|
46
+ copy_file(project_dir, package_dir, file)
47
+ end
48
+ end
49
+ end
50
+
51
+ def copy_executables(project_dir, package_dir)
52
+ @config[:executables].each do |executable_glob|
53
+ Pathname.glob(project_dir.join(executable_glob)).each do |executable|
54
+ copy_file(project_dir, package_dir, executable)
55
+ FileUtils.chmod(0755, package_dir.join(executable))
56
+ create_exec_wrapper(package_dir, executable)
57
+ end
58
+ end
59
+ end
60
+
61
+ def create_archive(target_dir)
62
+ Shell.exec('tar -czf %s %s' % [@config[:archive_name], Dir['*'].join(' ')])
63
+ FileUtils.mkdir_p(target_dir)
64
+ FileUtils.cp(@config[:archive_name], target_dir)
65
+ end
66
+
67
+ def fetcher
68
+ @fetcher ||= Fetcher.new(@config[:download_dir], @config[:target], @config[:traveling_ruby_version], @config).setup
69
+ end
70
+
71
+ def install_dependencies(package_dir, fetcher)
72
+ Installer.new(package_dir, fetcher, @config).execute
73
+ end
74
+
75
+ def copy_file(project_dir, package_dir, file)
76
+ rel_file = file.relative_path_from(project_dir)
77
+ unless rel_file.dirname == DOT_PATH
78
+ FileUtils.mkdir_p(package_dir.join(rel_file.dirname))
79
+ end
80
+ FileUtils.cp(project_dir.join(rel_file), package_dir.join(rel_file))
81
+ end
82
+
83
+ def create_exec_wrapper(package_dir, executable)
84
+ wrapper_path = package_dir.join(executable.basename)
85
+ ex = Executable.new(executable.basename)
86
+ File.open(wrapper_path, 'w') { |f| ex.write(f) }
87
+ FileUtils.chmod(0755, wrapper_path)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ module Tara
4
+ class Archiver
5
+ def initialize(options={})
6
+ @options = options
7
+ end
8
+
9
+ def create(options={})
10
+ Archive.create(@options.merge(options))
11
+ end
12
+
13
+ def extension
14
+ @options[:extension] || 'tgz'
15
+ end
16
+
17
+ def content_type
18
+ 'application/x-gzip'
19
+ end
20
+
21
+ def metadata
22
+ @options[:metadata] || {}
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'optparse'
4
+
5
+
6
+ module Tara
7
+ class Cli
8
+ def initialize(argv=ARGV, io=$stderr)
9
+ @argv = argv
10
+ @io = io
11
+ end
12
+
13
+ def run
14
+ Archive.new(parse_argv).create
15
+ 0
16
+ rescue => e
17
+ @io.puts(%(Error during packaging: #{e.message} (#{e.class})))
18
+ 1
19
+ end
20
+
21
+ private
22
+
23
+ def parse_argv(options={})
24
+ parser = OptionParser.new do |opts|
25
+ opts.on('--app-name NAME', 'Name of the app') do |app_name|
26
+ options[:app_name] = app_name
27
+ end
28
+
29
+ opts.on('--app-dir APP_DIR', 'Root directory of the app') do |app_dir|
30
+ options[:app_dir] = app_dir
31
+ end
32
+
33
+ opts.on('--download-dir DOWNLOAD_DIR', 'Where to store Traveling Ruby archives') do |download_dir|
34
+ options[:download_dir] = download_dir
35
+ end
36
+
37
+ opts.on('--target TARGET', 'Target platform for archive') do |target|
38
+ options[:target] = target
39
+ end
40
+ end
41
+ parser.parse(@argv)
42
+ options
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Tara
4
+ class Executable
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def write(io)
10
+ io.puts(script(@name))
11
+ end
12
+
13
+ private
14
+
15
+ def script(name)
16
+ <<-EOH.gsub(/^\s+/, '')
17
+ #!/bin/bash
18
+ set -e
19
+ SELF_DIR=$(dirname "$0")
20
+ export BUNDLE_GEMFILE="$SELF_DIR/lib/vendor/Gemfile"
21
+ unset BUNDLE_IGNORE_CONFIG
22
+ exec "$SELF_DIR/lib/ruby/bin/ruby" -rbundler/setup "$SELF_DIR/bin/#{name}" "$@"
23
+ EOH
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+
5
+
6
+ module Tara
7
+ class Fetcher
8
+ def initialize(download_dir, target, tr_version, options={})
9
+ @download_dir = download_dir
10
+ @target = target
11
+ @tr_version = tr_version
12
+ @ruby_version = options[:ruby_version] || RUBY_VERSION
13
+ @release_url = options[:tr_release_url] || 'http://d6r77u77i8pq3.cloudfront.net/releases'
14
+ @shell = options[:shell] || Shell
15
+ end
16
+
17
+ def setup
18
+ FileUtils.mkdir_p(@download_dir)
19
+ self
20
+ end
21
+
22
+ def fetch_ruby
23
+ local_uri = %(#{@download_dir}/ruby-#{@tr_version}-#{@ruby_version}-#{@target}.tar.gz)
24
+ fetch(ruby_remote_uri, local_uri)
25
+ end
26
+
27
+ def fetch_native_gem(name, version)
28
+ remote_uri = native_gem_remote_uri(name, version)
29
+ local_uri = %(#{@download_dir}/#{name}-#{version}-#{@tr_version}-#{@ruby_version}-#{@target}.tar.gz)
30
+ fetch(remote_uri, local_uri)
31
+ end
32
+
33
+ private
34
+
35
+ def ruby_remote_uri
36
+ @ruby_remote_uri ||= [@release_url, %(traveling-ruby-#{@tr_version}-#{@ruby_version}-#{@target}.tar.gz)].join('/')
37
+ end
38
+
39
+ def native_gem_remote_uri(name, version)
40
+ [@release_url, %(traveling-ruby-gems-#{@tr_version}-#{@ruby_version}-#{@target}/#{name}-#{version}.tar.gz)].join('/')
41
+ end
42
+
43
+ def fetch(remote_uri, local_uri, limit=10)
44
+ unless File.exist?(local_uri)
45
+ uri = URI(remote_uri)
46
+ Net::HTTP.start(uri.host, uri.port) do |http|
47
+ http.request(Net::HTTP::Get.new(uri)) do |response|
48
+ case response
49
+ when Net::HTTPSuccess
50
+ File.open(local_uri, 'w') do |f|
51
+ response.read_body do |chunk|
52
+ f.write(chunk)
53
+ end
54
+ end
55
+ when Net::HTTPRedirection
56
+ if limit > 0
57
+ fetch(response['location'], local_uri, limit - 1)
58
+ else
59
+ raise TooManyRedirectsError, %(Exhausted redirect limit, ended up at #{remote_uri})
60
+ end
61
+ when Net::HTTPNotFound
62
+ raise NotFoundError, %(#{remote_uri} doesn't exist)
63
+ else
64
+ raise UnknownResponseError, %(#{response.code} '#{response.body}' returned when fetching #{remote_uri})
65
+ end
66
+ end
67
+ end
68
+ end
69
+ local_uri
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,160 @@
1
+ # encoding: utf-8
2
+
3
+ module Tara
4
+ class Installer
5
+ def initialize(package_dir, fetcher, options={})
6
+ @package_dir = package_dir
7
+ @fetcher = fetcher
8
+ @without_groups = options[:without_groups]
9
+ @app_dir = Pathname.new(options[:app_dir])
10
+ @shell = options[:shell] || Shell
11
+ end
12
+
13
+ def execute
14
+ bundle_gems
15
+ extract_ruby
16
+ extract_native_gems
17
+ strip_tests
18
+ strip_docs
19
+ strip_leftovers
20
+ strip_java_files
21
+ strip_git_files
22
+ create_bundler_config
23
+ end
24
+
25
+ private
26
+
27
+ def bundler_command
28
+ @bundler_command ||= begin
29
+ command = 'BUNDLE_IGNORE_CONFIG=1 bundle install --jobs 4 --path vendor'
30
+ command << %( --without #{@without_groups.join(' ')}) if @without_groups.any?
31
+ command
32
+ end
33
+ end
34
+
35
+ def bundle_gems
36
+ FileUtils.mkdir_p(lib_path)
37
+ Dir.mktmpdir do |tmpdir|
38
+ copy_gem_files(Pathname.new(tmpdir))
39
+ Dir.chdir(tmpdir) do
40
+ Bundler.with_clean_env do
41
+ @shell.exec(bundler_command)
42
+ end
43
+ Dir['vendor/*/*/cache/*'].each do |cached_file|
44
+ FileUtils.rm_rf(cached_file)
45
+ end
46
+ Dir['vendor/ruby/*/extensions/*'].each do |ext_file|
47
+ FileUtils.rm_rf(ext_file)
48
+ end
49
+ @shell.exec('find vendor/ruby/*/gems -name "*.o" -exec rm {} \; 2>&1 || true')
50
+ @shell.exec('find vendor/ruby/*/gems -name "*.so" -exec rm {} \; 2>&1 || true')
51
+ @shell.exec('find vendor/ruby/*/gems -name "*.bundle" -exec rm {} \; 2>&1 || true')
52
+ FileUtils.cp_r('vendor', lib_path, preserve: true)
53
+ end
54
+ end
55
+ end
56
+
57
+ def extract_ruby
58
+ FileUtils.mkdir_p(ruby_path)
59
+ FileUtils.mkdir_p(ruby_vendor_path)
60
+ ruby_archive_path = @fetcher.fetch_ruby
61
+ @shell.exec(%(tar -xzf #{ruby_archive_path} -C #{ruby_path}))
62
+ end
63
+
64
+ def extract_native_gems
65
+ native_gems = find_native_gems
66
+ native_gems.each do |name, version|
67
+ gem_archive_path = @fetcher.fetch_native_gem(name, version)
68
+ @shell.exec %(tar -xzf #{gem_archive_path} -C #{ruby_vendor_path})
69
+ end
70
+ end
71
+
72
+ def find_native_gems
73
+ gemspecs = Dir[ruby_vendor_path.join('*/specifications/*.gemspec')]
74
+ specs = gemspecs.map { |gemspec| Gem::Specification.load(gemspec) }
75
+ with_ext = specs.select { |s| s.extensions.any? }
76
+ with_ext.each_with_object({}) do |spec, hash|
77
+ hash[spec.name] = spec.version.to_s
78
+ end
79
+ end
80
+
81
+ def copy_gem_files(path)
82
+ Dir['Gemfile', 'Gemfile.lock', '*.gemspec'].each do |file|
83
+ if File.exist?(@app_dir.join(file))
84
+ FileUtils.cp(@app_dir.join(file), path.join(File.basename(file)))
85
+ end
86
+ end
87
+ end
88
+
89
+ def create_bundler_config
90
+ copy_gem_files(vendor_path)
91
+ FileUtils.mkdir_p(bundle_path)
92
+ File.open(bundle_path.join('config'), 'w+') do |f|
93
+ f.puts(%(BUNDLE_PATH: .))
94
+ f.puts(%(BUNDLE_WITHOUT: #{@without_groups.join(':')})) if @without_groups.any?
95
+ f.puts(%(BUNDLE_DISABLE_SHARED_GEMS: '1'))
96
+ end
97
+ end
98
+
99
+ def strip_tests
100
+ strip_from_gems(%w[tests test spec])
101
+ end
102
+
103
+ def strip_docs
104
+ strip_from_gems(%w[doc* example* *.txt *.md *.rdoc])
105
+ end
106
+
107
+ def strip_leftovers
108
+ %w[c cpp h rl].each do |ext|
109
+ @shell.exec(%(find #{ruby_vendor_path} -name "*.#{ext}" -exec rm {} \\; 2>&1))
110
+ end
111
+ @shell.exec(%(find #{ruby_vendor_path} -name "extconf.rb" -exec rm {} \\;))
112
+ @shell.exec(%(find #{vendor_gems_glob.join('*', 'ext')} -name "Makefile" -exec rm {} \\; 2>&1))
113
+ @shell.exec(%(find #{vendor_gems_glob.join('*', 'ext')} -name "tmp" -type d 2>&1 | xargs rm -rf))
114
+ end
115
+
116
+ def strip_java_files
117
+ @shell.exec(%(find #{vendor_gems_glob} -name "*.java" -exec rm {} \\;))
118
+ end
119
+
120
+ def strip_git_files
121
+ @shell.exec(%(find #{vendor_gems_glob} -name ".git" -type d | xargs rm -rf))
122
+ @shell.exec(%(find #{bundler_gems_glob} -name ".git" -type d | xargs rm -rf))
123
+ end
124
+
125
+ def strip_from_gems(things)
126
+ things.each do |thing|
127
+ FileUtils.rm_r(Dir[vendor_gems_glob.join('*', thing)])
128
+ FileUtils.rm_r(Dir[bundler_gems_glob.join('*', thing)])
129
+ end
130
+ end
131
+
132
+ def lib_path
133
+ @lib_path ||= Pathname.new(@package_dir).join('lib')
134
+ end
135
+
136
+ def vendor_path
137
+ @vendor_path ||= lib_path.join('vendor')
138
+ end
139
+
140
+ def ruby_vendor_path
141
+ @ruby_vendor_path ||= vendor_path.join('ruby')
142
+ end
143
+
144
+ def bundle_path
145
+ @bundle_path ||= vendor_path.join('.bundle')
146
+ end
147
+
148
+ def ruby_path
149
+ @ruby_path ||= lib_path.join('ruby')
150
+ end
151
+
152
+ def bundler_gems_glob
153
+ @bundler_gems_glob ||= ruby_vendor_path.join('*', 'bundler', 'gems')
154
+ end
155
+
156
+ def vendor_gems_glob
157
+ @vendor_gems_glob ||= ruby_vendor_path.join('*', 'gems')
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ module Tara
4
+ class Shell
5
+ def self.exec(command)
6
+ output = %x(#{command})
7
+ $stderr.puts(%(#{command}: #{output})) if ENV['TARA_DEBUG']
8
+ unless $?.success?
9
+ raise ExecError, %(Command `#{command}` failed with output: #{output})
10
+ end
11
+ output
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Tara
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tara
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mathias Söderberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Tara packs your Ruby app into a standalone archive with gems and a Ruby
14
+ runtime
15
+ email:
16
+ - mths@sdrbrg.se
17
+ executables:
18
+ - tara
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - bin/tara
23
+ - lib/tara.rb
24
+ - lib/tara/archive.rb
25
+ - lib/tara/archiver.rb
26
+ - lib/tara/cli.rb
27
+ - lib/tara/executable.rb
28
+ - lib/tara/fetcher.rb
29
+ - lib/tara/installer.rb
30
+ - lib/tara/shell.rb
31
+ - lib/tara/version.rb
32
+ homepage: http://github.com/mthssdrbrg/tara
33
+ licenses:
34
+ - BSD-3-Clause
35
+ metadata: {}
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 2.1.5
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 2.4.5
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Packs your Ruby app as a standalone archive
56
+ test_files: []
57
+ has_rdoc: