shoes-package 4.0.0.pre3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +1 -0
- data/lib/shoes/package/configuration.rb +156 -0
- data/lib/shoes/package/jar.rb +17 -0
- data/lib/shoes/package/jar_app.rb +18 -0
- data/lib/shoes/package/version.rb +5 -0
- data/lib/warbler/traits/shoes.rb +53 -0
- data/shoes-package.gemspec +21 -0
- data/spec/.furoshiki/cache/shoes-app-template.zip +0 -0
- data/spec/configuration_spec.rb +207 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/support/shared_config.rb +15 -0
- data/spec/swt_app_spec.rb +131 -0
- data/spec/swt_jar_spec.rb +66 -0
- data/spec/test_app/app.yaml +17 -0
- data/spec/test_app/bin/hello_world +3 -0
- data/spec/test_app/dir_to_ignore/file_to_ignore.txt +1 -0
- data/spec/test_app/img/boots.icns +0 -0
- data/spec/test_app/img/boots.ico +0 -0
- data/spec/test_app/img/boots_512x512x32.png +0 -0
- data/spec/test_app/sibling.rb +1 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e85a08b46b6fcdaa86a5b1609c14fb2e58c08677
|
4
|
+
data.tar.gz: b655bde5a2d94c020ed7a99a7eed8337cbb36d2f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d425a176814a1adfb819ed1296ad7d5f4308ce13b5928bdbbe933ad99a87c82714f179e37a2b2f5c22abcb43d052ef00d01e153d3f4ccdb54481b99351a4707c
|
7
|
+
data.tar.gz: 31d06640d06a1932e8826727d62720d499b9a1ae391a611818d98607d6aada43dc98b7fd4f4b6b7d903c2753bfeb7472b5a578b47d92a61afb0609e83a2e4ff1
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
require 'furoshiki/configuration'
|
4
|
+
require 'furoshiki/validator'
|
5
|
+
require 'furoshiki/warbler_extensions'
|
6
|
+
require 'warbler/traits/shoes'
|
7
|
+
|
8
|
+
class Shoes
|
9
|
+
module Package
|
10
|
+
# Configuration for Shoes packagers.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# config_file = '/path/to/app.yaml'
|
14
|
+
# config = Shoes::Package::Configuration.load(config_file)
|
15
|
+
#
|
16
|
+
module Configuration
|
17
|
+
extend ::Furoshiki::Util
|
18
|
+
|
19
|
+
JAR_APP_TEMPLATE_URL = 'https://s3.amazonaws.com/net.wasnotrice.shoes/wrappers/shoes-app-template-0.0.1.zip'
|
20
|
+
|
21
|
+
# Convenience method for loading config from a file. Note that you
|
22
|
+
# can pass four kinds of paths to the loader. Given the following
|
23
|
+
# file structure:
|
24
|
+
#
|
25
|
+
# ├── a
|
26
|
+
# │ ├── app.yaml
|
27
|
+
# │ └── shoes-app-a.rb
|
28
|
+
# └── b
|
29
|
+
# └── shoes-app-b.rb
|
30
|
+
#
|
31
|
+
# To package an app that has an `app.yaml`, like `shoes-app-a.rb`,
|
32
|
+
# you can call the loader with any of:
|
33
|
+
#
|
34
|
+
# - a/app.yaml
|
35
|
+
# - a
|
36
|
+
# - a/shoes-app-a.rb
|
37
|
+
#
|
38
|
+
# These will all find and use your configuration in `a/app.yaml`.
|
39
|
+
# To package an app that does not have an `app.yaml`, like
|
40
|
+
# `b/shoes-app-b.rb`, you must call the loader with the path of
|
41
|
+
# the script itself. Note that without an `app.yaml`, you will
|
42
|
+
# only bundle a single file, and your app will simply use the
|
43
|
+
# Shoes app icon.
|
44
|
+
#
|
45
|
+
# @overload load(path, base_config)
|
46
|
+
# @param [String] path location of the app's 'app.yaml'
|
47
|
+
# @param [Hash] base_config default values
|
48
|
+
# @overload load(path)
|
49
|
+
# @param [String] path location of the directory that
|
50
|
+
# contains the app's 'app.yaml'
|
51
|
+
# @param [Hash] base_config default values
|
52
|
+
# @overload load(path)
|
53
|
+
# @param [String] path location of the app
|
54
|
+
# @param [Hash] base_config default values
|
55
|
+
def self.load(path, base_config = {})
|
56
|
+
pathname = Pathname.new(path)
|
57
|
+
app_yaml = Pathname.new('app.yaml')
|
58
|
+
|
59
|
+
if pathname.basename == app_yaml
|
60
|
+
file, dir = pathname, pathname.dirname
|
61
|
+
elsif pathname.directory?
|
62
|
+
file, dir = pathname.join(app_yaml), pathname
|
63
|
+
elsif pathname.file? && pathname.parent.children.include?(pathname.parent.join app_yaml)
|
64
|
+
file, dir = pathname.parent.join(app_yaml), pathname.parent
|
65
|
+
else
|
66
|
+
file, dir = config_for_single_file_app(pathname)
|
67
|
+
end
|
68
|
+
|
69
|
+
config = YAML.load(file.read)
|
70
|
+
config[:working_dir] = dir
|
71
|
+
config[:gems] = merge_gems(base_config, config)
|
72
|
+
create(config)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param [Hash] config user options
|
76
|
+
def self.create(config = {})
|
77
|
+
defaults = {
|
78
|
+
name: 'Shoes App',
|
79
|
+
version: '0.0.0',
|
80
|
+
release: 'Rookie',
|
81
|
+
run: nil,
|
82
|
+
ignore: 'pkg',
|
83
|
+
# TODO: Add actual paths. Keep these keys for compatibility with Shoes 3
|
84
|
+
icons: {
|
85
|
+
#osx: 'path/to/default/App.icns',
|
86
|
+
#gtk: 'path/to/default/app.png',
|
87
|
+
#win32: 'path/to/default/App.ico',
|
88
|
+
},
|
89
|
+
# TODO: Add actual paths. Keep these keys for compatibility with Shoes 3
|
90
|
+
dmg: {
|
91
|
+
#ds_store: 'path/to/default/.DS_Store',
|
92
|
+
#background: 'path/to/default/background.png'
|
93
|
+
},
|
94
|
+
working_dir: Dir.pwd,
|
95
|
+
gems: ['shoes-core'],
|
96
|
+
validator: Validator,
|
97
|
+
warbler_extensions: WarblerExtensions
|
98
|
+
}
|
99
|
+
|
100
|
+
symbolized_config = deep_symbolize_keys(config)
|
101
|
+
symbolized_config[:gems] = merge_gems(defaults, symbolized_config)
|
102
|
+
Furoshiki::Configuration.new defaults.merge(symbolized_config)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
# If it exists, load default options. If not, let the filesystem raise an
|
107
|
+
# error.
|
108
|
+
def self.config_for_single_file_app(pathname)
|
109
|
+
default_options = {
|
110
|
+
run: pathname.basename.to_s,
|
111
|
+
name: pathname.basename(pathname.extname).to_s.gsub(/\W/, '-')
|
112
|
+
}.to_yaml
|
113
|
+
options = pathname.exist? ? default_options : pathname
|
114
|
+
dummy_file = Struct.new(:read)
|
115
|
+
[dummy_file.new(options), pathname.parent]
|
116
|
+
end
|
117
|
+
|
118
|
+
# We want to retain all of the gems, but simply merging the hash will
|
119
|
+
# replace the whole array, so we handle the gems separately. Note that
|
120
|
+
# keys may not have been symbolized yet
|
121
|
+
def self.merge_gems(base, additional)
|
122
|
+
base_gems = base.fetch(:gems) rescue base['gems']
|
123
|
+
additional_gems = additional.fetch(:gems) rescue additional['gems']
|
124
|
+
Array(base_gems).concat(Array(additional_gems)).uniq
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Validator < Furoshiki::Validator
|
129
|
+
def validate
|
130
|
+
unless config.run && config.working_dir.join(config.run).exist?
|
131
|
+
add_missing_file_error(config.run, "Run file")
|
132
|
+
end
|
133
|
+
|
134
|
+
if config.icons[:osx] && !config.working_dir.join(config.icons[:osx]).exist?
|
135
|
+
add_missing_file_error(config.icons[:osx], "OS X icon file")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class WarblerExtensions < Furoshiki::WarblerExtensions
|
141
|
+
def customize(warbler_config)
|
142
|
+
warbler_config.tap do |warbler|
|
143
|
+
warbler.pathmaps.application = ['shoes-app/%p']
|
144
|
+
warbler.extend ShoesWarblerConfig
|
145
|
+
warbler.run = @config.run.split(/\s/).first
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
# Adds Shoes-specific functionality to the Warbler Config
|
151
|
+
module ShoesWarblerConfig
|
152
|
+
attr_accessor :run
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'furoshiki/jar'
|
3
|
+
|
4
|
+
class Shoes
|
5
|
+
module Package
|
6
|
+
class Jar
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@packager, :package, :default_dir, :filename
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
config.gems << 'shoes-swt'
|
13
|
+
@packager = Furoshiki::Jar.new(config)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'furoshiki/jar_app'
|
3
|
+
|
4
|
+
class Shoes
|
5
|
+
module Package
|
6
|
+
class JarApp
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@packager, :package, :default_package_dir, :cache_dir,
|
10
|
+
:remote_template_url, :template_path
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
config.gems << 'shoes-swt'
|
14
|
+
@packager = Furoshiki::JarApp.new(config)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'warbler'
|
2
|
+
require 'warbler/traits'
|
3
|
+
require 'warbler/traits/furoshiki'
|
4
|
+
|
5
|
+
module Warbler
|
6
|
+
module Traits
|
7
|
+
# Hack to control the executable
|
8
|
+
class NoGemspec
|
9
|
+
def update_archive(jar); end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Disable this trait, since we are subclassing it (don't run twice)
|
13
|
+
class Furoshiki
|
14
|
+
def self.detect?; false; end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Shoes < Furoshiki
|
18
|
+
def self.detect?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.requires?(trait)
|
23
|
+
# Actually, it would be better to dump the NoGemspec trait, but since
|
24
|
+
# we can't do that, we can at least make sure that this trait gets
|
25
|
+
# processed later by declaring that it requires NoGemspec.
|
26
|
+
[Traits::Jar, Traits::NoGemspec].include? trait
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_configure
|
30
|
+
config.init_contents << StringIO.new("require 'shoes'\nShoes.configuration.backend = :swt\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_archive(jar)
|
34
|
+
super
|
35
|
+
add_main_rb(jar, apply_pathmaps(config, default_executable, :application))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Uses the `@config.run` if it exists. Otherwise, looks in the
|
39
|
+
# application's `bin` directory for an executable with the same name as
|
40
|
+
# the jar. If this also fails, defaults to the first executable (alphabetically) in the
|
41
|
+
# applications `bin` directory.
|
42
|
+
#
|
43
|
+
# @return [String] filename of the executable to run
|
44
|
+
def default_executable
|
45
|
+
return @config.run if @config.run
|
46
|
+
exes = Dir['bin/*'].sort
|
47
|
+
exe = exes.grep(/#{config.jar_name}/).first || exes.first
|
48
|
+
raise "No executable script found" unless exe
|
49
|
+
exe
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('lib/shoes/package/version')
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "shoes-package"
|
6
|
+
s.version = Shoes::Package::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Team Shoes"]
|
9
|
+
s.email = ["shoes@librelist.com"]
|
10
|
+
s.homepage = "https://github.com/shoes/shoes4"
|
11
|
+
s.summary = %q{Application packaging for Shoes, the best little GUI toolkit for Ruby.}
|
12
|
+
s.description = %q{Application packaging for Shoes, the best little GUI toolkit for Ruby. Shoes makes building for Mac, Windows, and Linux super simple.}
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split($/)
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "shoes-core", Shoes::Package::VERSION
|
20
|
+
s.add_dependency "furoshiki", "~> 0.3.0"
|
21
|
+
end
|
Binary file
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'shoes/package/configuration'
|
3
|
+
|
4
|
+
describe Shoes::Package::Configuration do
|
5
|
+
context "defaults" do
|
6
|
+
subject { Shoes::Package::Configuration.create }
|
7
|
+
|
8
|
+
its(:name) { should eq('Shoes App') }
|
9
|
+
its(:shortname) { should eq('shoesapp') }
|
10
|
+
its(:ignore) { should eq(['pkg']) }
|
11
|
+
its(:gems) { should include('shoes-core') }
|
12
|
+
its(:version) { should eq('0.0.0') }
|
13
|
+
its(:release) { should eq('Rookie') }
|
14
|
+
its(:icons) { should be_an_instance_of(Hash) }
|
15
|
+
its(:dmg) { should be_an_instance_of(Hash) }
|
16
|
+
its(:run) { should be_nil }
|
17
|
+
its(:working_dir) { should eq(Pathname.new(Dir.pwd)) }
|
18
|
+
it { is_expected.not_to be_valid } # no :run
|
19
|
+
|
20
|
+
# TODO: Implement
|
21
|
+
describe "#icons" do
|
22
|
+
it 'osx is nil' do
|
23
|
+
expect(subject.icons[:osx]).to be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'gtk is nil' do
|
27
|
+
expect(subject.icons[:gtk]).to be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'win32 is nil' do
|
31
|
+
expect(subject.icons[:win32]).to be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: Implement
|
36
|
+
describe "#dmg" do
|
37
|
+
it "ds_store is nil" do
|
38
|
+
expect(subject.dmg[:ds_store]).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "background is nil" do
|
42
|
+
expect(subject.dmg[:background]).to be_nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#to_hash" do
|
47
|
+
it "round-trips" do
|
48
|
+
expect(Shoes::Package::Configuration.create(subject.to_hash)).to eq(subject)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with options" do
|
54
|
+
include_context 'config'
|
55
|
+
subject { Shoes::Package::Configuration.load(@config_filename) }
|
56
|
+
|
57
|
+
its(:name) { should eq('Sugar Clouds') }
|
58
|
+
its(:shortname) { should eq('sweet-nebulae') }
|
59
|
+
its(:ignore) { should include('pkg') }
|
60
|
+
its(:run) { should eq('bin/hello_world') }
|
61
|
+
its(:gems) { should include('rspec') }
|
62
|
+
its(:gems) { should include('shoes-core') }
|
63
|
+
its(:version) { should eq('0.0.1') }
|
64
|
+
its(:release) { should eq('Mindfully') }
|
65
|
+
its(:icons) { should be_an_instance_of(Hash) }
|
66
|
+
its(:dmg) { should be_an_instance_of(Hash) }
|
67
|
+
its(:working_dir) { should eq(@config_filename.dirname) }
|
68
|
+
it { is_expected.to be_valid }
|
69
|
+
|
70
|
+
describe "#icons" do
|
71
|
+
it 'has osx' do
|
72
|
+
expect(subject.icons[:osx]).to eq('img/boots.icns')
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'has gtk' do
|
76
|
+
expect(subject.icons[:gtk]).to eq('img/boots_512x512x32.png')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'has win32' do
|
80
|
+
expect(subject.icons[:win32]).to eq('img/boots.ico')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#dmg" do
|
85
|
+
it "has ds_store" do
|
86
|
+
expect(subject.dmg[:ds_store]).to eq('path/to/custom/.DS_Store')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "has background" do
|
90
|
+
expect(subject.dmg[:background]).to eq('path/to/custom/background.png')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "incorporates custom features" do
|
95
|
+
expect(subject.custom).to eq('my custom feature')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "round-trips" do
|
99
|
+
expect(Shoes::Package::Configuration.create(subject.to_hash)).to eq(subject)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "with name, but without explicit shortname" do
|
104
|
+
let(:options) { {:name => "Sugar Clouds"} }
|
105
|
+
subject { Shoes::Package::Configuration.create options }
|
106
|
+
|
107
|
+
its(:name) { should eq("Sugar Clouds") }
|
108
|
+
its(:shortname) { should eq("sugarclouds") }
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when the file to run doens't exist" do
|
112
|
+
let(:options) { {:run => "path/to/non-existent/file"} }
|
113
|
+
subject { Shoes::Package::Configuration.create options }
|
114
|
+
|
115
|
+
it { is_expected.not_to be_valid }
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when osx icon is not specified" do
|
119
|
+
include_context 'config'
|
120
|
+
let(:valid_config) { Shoes::Package::Configuration.load(@config_filename) }
|
121
|
+
let(:options) { valid_config.to_hash.merge(:icons => {}) }
|
122
|
+
subject { Shoes::Package::Configuration.create(options) }
|
123
|
+
|
124
|
+
it "sets osx icon path to nil" do
|
125
|
+
expect(subject.icons[:osx]).to be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
it "is valid" do
|
129
|
+
expect(subject).to be_valid
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when osx icon is specified, but doesn't exist" do
|
134
|
+
let(:options) { ({:icons => {:osx => "path/to/non-existent/file"}}) }
|
135
|
+
subject { Shoes::Package::Configuration.create options }
|
136
|
+
|
137
|
+
it "sets osx icon path" do
|
138
|
+
expect(subject.icons[:osx]).to eq("path/to/non-existent/file")
|
139
|
+
end
|
140
|
+
|
141
|
+
it { is_expected.not_to be_valid }
|
142
|
+
end
|
143
|
+
|
144
|
+
context "auto-loading" do
|
145
|
+
include_context 'config'
|
146
|
+
|
147
|
+
shared_examples "config with path" do
|
148
|
+
it "finds the config" do
|
149
|
+
Dir.chdir File.dirname(__FILE__) do
|
150
|
+
config = Shoes::Package::Configuration.load(path)
|
151
|
+
expect(config.shortname).to eq('sweet-nebulae')
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "with an 'app.yaml'" do
|
157
|
+
let(:path) { @config_filename }
|
158
|
+
it_behaves_like "config with path"
|
159
|
+
end
|
160
|
+
|
161
|
+
context "with a path to a directory containing an 'app.yaml'" do
|
162
|
+
let(:path) { @config_filename.parent }
|
163
|
+
it_behaves_like "config with path"
|
164
|
+
end
|
165
|
+
|
166
|
+
context "with a path to a file that is siblings with an 'app.yaml'" do
|
167
|
+
let(:path) { @config_filename.parent.join('sibling.rb') }
|
168
|
+
it_behaves_like "config with path"
|
169
|
+
end
|
170
|
+
|
171
|
+
context "with a path that exists, but no 'app.yaml'" do
|
172
|
+
let(:path) { @config_filename.parent.join('bin/hello_world') }
|
173
|
+
subject { Shoes::Package::Configuration.load(path) }
|
174
|
+
|
175
|
+
its(:name) { should eq('hello_world') }
|
176
|
+
its(:shortname) { should eq('hello_world') }
|
177
|
+
end
|
178
|
+
|
179
|
+
context "when the file doesn't exist" do
|
180
|
+
it "blows up" do
|
181
|
+
expect { Shoes::Package::Configuration.load('some/bogus/path') }.to raise_error
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "with additional gems" do
|
186
|
+
let(:path) { @config_filename }
|
187
|
+
let(:additional_config) { { gems: gems } }
|
188
|
+
let(:config) { config = Shoes::Package::Configuration.load(path, additional_config) }
|
189
|
+
|
190
|
+
context "one gem" do
|
191
|
+
let(:gems) { 'shoes-swt' }
|
192
|
+
|
193
|
+
it "adds one additional" do
|
194
|
+
expect(config.gems).to include('shoes-swt')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "multiple gems" do
|
199
|
+
let(:gems) { ['shoes-swt', 'shoes-extras'] }
|
200
|
+
|
201
|
+
it "adds multiple additional gems" do
|
202
|
+
expect(config.gems & gems).to eq(gems)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Packaging caches files in $HOME/.furoshiki/cache by default.
|
2
|
+
# For testing, we override $HOME using $FUROSHIKI_HOME
|
3
|
+
FUROSHIKI_SPEC_DIR = Pathname.new(__FILE__).dirname.expand_path.to_s
|
4
|
+
ENV['FUROSHIKI_HOME'] = FUROSHIKI_SPEC_DIR
|
5
|
+
|
6
|
+
SHOES_PACKAGE_SPEC_ROOT= File.expand_path('..', __FILE__)
|
7
|
+
|
8
|
+
$LOAD_PATH << File.expand_path(SHOES_PACKAGE_SPEC_ROOT)
|
9
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
10
|
+
$LOAD_PATH << File.expand_path('../../../shoes-core/lib', __FILE__)
|
11
|
+
|
12
|
+
require 'pathname'
|
13
|
+
require 'rspec/its'
|
14
|
+
|
15
|
+
Dir["#{SHOES_PACKAGE_SPEC_ROOT}/support/**/*.rb"].each {|f| require f}
|
16
|
+
|
17
|
+
module PackageHelpers
|
18
|
+
# need these values from a context block, so let doesn't work
|
19
|
+
def spec_dir
|
20
|
+
Pathname.new(__FILE__).join('..').cleanpath
|
21
|
+
end
|
22
|
+
|
23
|
+
def input_dir
|
24
|
+
spec_dir.join 'support', 'zip'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Guards for running or not running specs. Specs in the guarded block only
|
29
|
+
# run if the guard conditions are met.
|
30
|
+
#
|
31
|
+
module Guard
|
32
|
+
# Runs specs only if platform matches
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# platform_is :windows do
|
36
|
+
# it "does something only on windows" do
|
37
|
+
# # specification
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
def platform_is(platform)
|
41
|
+
yield if self.send "platform_is_#{platform.to_s}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Runs specs only if platform does not match
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# platform_is_not :windows do
|
48
|
+
# it "does something only on posix systems" do
|
49
|
+
# # specification
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
def platform_is_not(platform)
|
53
|
+
yield unless self.send "platform_is_#{platform.to_s}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def platform_is_windows
|
57
|
+
return RbConfig::CONFIG['host_os'] =~ /windows|mswin/i
|
58
|
+
end
|
59
|
+
|
60
|
+
def platform_is_linux
|
61
|
+
return RbConfig::CONFIG['host_os'] =~ /linux/i
|
62
|
+
end
|
63
|
+
|
64
|
+
def platform_is_osx
|
65
|
+
return RbConfig::CONFIG['host_os'] =~ /darwin/i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
include Guard
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
shared_context 'config' do
|
5
|
+
before :all do
|
6
|
+
@config_filename = Pathname.new(__FILE__).join('../../test_app/app.yaml').cleanpath
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
shared_context 'package' do
|
11
|
+
before :all do
|
12
|
+
@app_dir = Pathname.new(__FILE__).join '../../test_app'
|
13
|
+
@output_dir = @app_dir.join 'pkg'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'shoes/package/configuration'
|
3
|
+
require 'shoes/package/jar_app'
|
4
|
+
|
5
|
+
include PackageHelpers
|
6
|
+
|
7
|
+
describe Shoes::Package::JarApp do
|
8
|
+
include_context 'config'
|
9
|
+
include_context 'package'
|
10
|
+
|
11
|
+
let(:config) { Shoes::Package::Configuration.load @config_filename }
|
12
|
+
subject { Shoes::Package::JarApp.new config }
|
13
|
+
|
14
|
+
let(:launcher) { @output_file.join('Contents/MacOS/JavaAppLauncher') }
|
15
|
+
let(:icon) { @output_file.join('Contents/Resources/boots.icns') }
|
16
|
+
let(:jar) { @output_file.join('Contents/Java/sweet-nebulae.jar') }
|
17
|
+
|
18
|
+
# $FUROSHIKI_HOME is set in spec_helper.rb for testing purposes,
|
19
|
+
# but should default to $HOME
|
20
|
+
context "when not setting $FUROSHIKI_HOME" do
|
21
|
+
before do
|
22
|
+
@old_furoshiki_home = ENV['FUROSHIKI_HOME']
|
23
|
+
ENV['FUROSHIKI_HOME'] = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
its(:cache_dir) { should eq(Pathname.new(Dir.home).join('.furoshiki', 'cache')) }
|
28
|
+
|
29
|
+
after do
|
30
|
+
ENV['FUROSHIKI_HOME'] = @old_furoshiki_home
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "default" do
|
35
|
+
let(:cache_dir) { Pathname.new(SHOES_PACKAGE_SPEC_ROOT).join('.furoshiki', 'cache') }
|
36
|
+
its(:cache_dir) { should eq(cache_dir) }
|
37
|
+
|
38
|
+
it "sets package dir to {pwd}/pkg" do
|
39
|
+
Dir.chdir @app_dir do
|
40
|
+
expect(subject.default_package_dir).to eq(@app_dir.join 'pkg')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
its(:template_path) { should eq(cache_dir.join('shoes-app-template.zip')) }
|
45
|
+
its(:remote_template_url) { should eq(Shoes::Package::Configuration::JAR_APP_TEMPLATE_URL) }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when creating a .app" do
|
49
|
+
before :all do
|
50
|
+
@output_dir.rmtree if @output_dir.exist?
|
51
|
+
@output_dir.mkpath
|
52
|
+
app_name = 'Sugar Clouds.app'
|
53
|
+
@output_file = @output_dir.join app_name
|
54
|
+
config = Shoes::Package::Configuration.load @config_filename
|
55
|
+
@subject = Shoes::Package::JarApp.new config
|
56
|
+
@subject.package
|
57
|
+
end
|
58
|
+
|
59
|
+
subject { @subject }
|
60
|
+
|
61
|
+
its(:template_path) { should exist }
|
62
|
+
|
63
|
+
it "creates a .app" do
|
64
|
+
expect(@output_file).to exist
|
65
|
+
end
|
66
|
+
|
67
|
+
it "includes launcher" do
|
68
|
+
expect(launcher).to exist
|
69
|
+
end
|
70
|
+
|
71
|
+
# Windows can't test this
|
72
|
+
platform_is_not :windows do
|
73
|
+
it "makes launcher executable" do
|
74
|
+
expect(launcher).to be_executable
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "deletes generic icon" do
|
79
|
+
expect(icon.parent.join('GenericApp.icns')).not_to exist
|
80
|
+
end
|
81
|
+
|
82
|
+
it "injects icon" do
|
83
|
+
expect(icon).to exist
|
84
|
+
end
|
85
|
+
|
86
|
+
it "injects jar" do
|
87
|
+
expect(jar).to exist
|
88
|
+
end
|
89
|
+
|
90
|
+
it "removes any extraneous jars" do
|
91
|
+
jar_dir_contents = @output_file.join("Contents/Java").children
|
92
|
+
expect(jar_dir_contents.reject {|f| f == jar }).to be_empty
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "Info.plist" do
|
96
|
+
require 'plist'
|
97
|
+
before :all do
|
98
|
+
@plist = Plist.parse_xml(@output_file.join 'Contents/Info.plist')
|
99
|
+
end
|
100
|
+
|
101
|
+
it "sets identifier" do
|
102
|
+
expect(@plist['CFBundleIdentifier']).to eq('com.hackety.shoes.sweet-nebulae')
|
103
|
+
end
|
104
|
+
|
105
|
+
it "sets display name" do
|
106
|
+
expect(@plist['CFBundleDisplayName']).to eq('Sugar Clouds')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "sets bundle name" do
|
110
|
+
expect(@plist['CFBundleName']).to eq('Sugar Clouds')
|
111
|
+
end
|
112
|
+
|
113
|
+
it "sets icon" do
|
114
|
+
expect(@plist['CFBundleIconFile']).to eq('boots.icns')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "sets version" do
|
118
|
+
expect(@plist['CFBundleVersion']).to eq('0.0.1')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "with an invalid configuration" do
|
124
|
+
let(:config) { Shoes::Package::Configuration.create }
|
125
|
+
subject { Shoes::Package::JarApp.new config }
|
126
|
+
|
127
|
+
it "fails to initialize" do
|
128
|
+
expect { subject }.to raise_error(Furoshiki::ConfigurationError)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'shoes/package/configuration'
|
3
|
+
require 'shoes/package/jar'
|
4
|
+
|
5
|
+
include PackageHelpers
|
6
|
+
|
7
|
+
describe Furoshiki::Jar do
|
8
|
+
include_context 'config'
|
9
|
+
include_context 'package'
|
10
|
+
|
11
|
+
context "when creating a .jar" do
|
12
|
+
before :all do
|
13
|
+
@output_dir.rmtree if @output_dir.exist?
|
14
|
+
@output_dir.mkpath
|
15
|
+
config = Shoes::Package::Configuration.load(@config_filename)
|
16
|
+
@subject = Shoes::Package::Jar.new(config)
|
17
|
+
@jar_path = @subject.package
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:jar_name) { 'sweet-nebulae.jar' }
|
21
|
+
let(:output_file) { Pathname.new(@output_dir.join jar_name) }
|
22
|
+
subject { @subject }
|
23
|
+
|
24
|
+
it "creates a .jar" do
|
25
|
+
expect(output_file).to exist
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns path to .jar" do
|
29
|
+
expect(@jar_path).to eq(output_file.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "creates .jar smaller than 60MB" do
|
33
|
+
expect(File.size(output_file)).to be < 60 * 1024 * 1024
|
34
|
+
end
|
35
|
+
|
36
|
+
context "inspecting contents" do
|
37
|
+
let (:jar) { Zip::File.new(output_file) }
|
38
|
+
|
39
|
+
it "includes shoes-core" do
|
40
|
+
shoes_core = jar.glob "gems/shoes-core*"
|
41
|
+
expect(shoes_core.length).to equal(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "includes shoes-swt" do
|
45
|
+
shoes_swt = jar.glob "gems/shoes-swt*"
|
46
|
+
expect(shoes_swt.length).to equal(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "excludes directories recursively" do
|
50
|
+
expect(jar.entries).not_to include("dir_to_ignore/file_to_ignore")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
its(:default_dir) { should eq(@output_dir) }
|
55
|
+
its(:filename) { should eq(jar_name) }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "with an invalid configuration" do
|
59
|
+
let(:config) { Shoes::Package::Configuration.create}
|
60
|
+
subject { Shoes::Package::Jar.new(config) }
|
61
|
+
|
62
|
+
it "fails to initialize" do
|
63
|
+
expect { subject }.to raise_error(Furoshiki::ConfigurationError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: Sugar Clouds
|
2
|
+
shortname: sweet-nebulae
|
3
|
+
ignore:
|
4
|
+
- pkg
|
5
|
+
- dir_to_ignore
|
6
|
+
run: bin/hello_world
|
7
|
+
gems: rspec
|
8
|
+
version: 0.0.1
|
9
|
+
release: Mindfully
|
10
|
+
icons:
|
11
|
+
osx: img/boots.icns
|
12
|
+
gtk: img/boots_512x512x32.png
|
13
|
+
win32: img/boots.ico
|
14
|
+
dmg:
|
15
|
+
ds_store: path/to/custom/.DS_Store
|
16
|
+
background: path/to/custom/background.png
|
17
|
+
custom: my custom feature
|
@@ -0,0 +1 @@
|
|
1
|
+
should be ignored
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
# Just a sibling of 'app.yaml'
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shoes-package
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 4.0.0.pre3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Team Shoes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - '='
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 4.0.0.pre3
|
19
|
+
name: shoes-core
|
20
|
+
prerelease: false
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0.pre3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.3.0
|
33
|
+
name: furoshiki
|
34
|
+
prerelease: false
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.3.0
|
41
|
+
description: Application packaging for Shoes, the best little GUI toolkit for Ruby. Shoes makes building for Mac, Windows, and Linux super simple.
|
42
|
+
email:
|
43
|
+
- shoes@librelist.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Rakefile
|
49
|
+
- lib/shoes/package/configuration.rb
|
50
|
+
- lib/shoes/package/jar.rb
|
51
|
+
- lib/shoes/package/jar_app.rb
|
52
|
+
- lib/shoes/package/version.rb
|
53
|
+
- lib/warbler/traits/shoes.rb
|
54
|
+
- shoes-package.gemspec
|
55
|
+
- spec/.furoshiki/cache/shoes-app-template.zip
|
56
|
+
- spec/configuration_spec.rb
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
- spec/support/shared_config.rb
|
59
|
+
- spec/swt_app_spec.rb
|
60
|
+
- spec/swt_jar_spec.rb
|
61
|
+
- spec/test_app/app.yaml
|
62
|
+
- spec/test_app/bin/hello_world
|
63
|
+
- spec/test_app/dir_to_ignore/file_to_ignore.txt
|
64
|
+
- spec/test_app/img/boots.icns
|
65
|
+
- spec/test_app/img/boots.ico
|
66
|
+
- spec/test_app/img/boots_512x512x32.png
|
67
|
+
- spec/test_app/sibling.rb
|
68
|
+
homepage: https://github.com/shoes/shoes4
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>'
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.3.1
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.4.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Application packaging for Shoes, the best little GUI toolkit for Ruby.
|
92
|
+
test_files:
|
93
|
+
- spec/.furoshiki/cache/shoes-app-template.zip
|
94
|
+
- spec/configuration_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
- spec/support/shared_config.rb
|
97
|
+
- spec/swt_app_spec.rb
|
98
|
+
- spec/swt_jar_spec.rb
|
99
|
+
- spec/test_app/app.yaml
|
100
|
+
- spec/test_app/bin/hello_world
|
101
|
+
- spec/test_app/dir_to_ignore/file_to_ignore.txt
|
102
|
+
- spec/test_app/img/boots.icns
|
103
|
+
- spec/test_app/img/boots.ico
|
104
|
+
- spec/test_app/img/boots_512x512x32.png
|
105
|
+
- spec/test_app/sibling.rb
|
106
|
+
has_rdoc:
|