batali 0.3.14 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56e09e21e418f24e83792d17a8c9498c0b46a2df
4
- data.tar.gz: 0590ade5cc488326df250797dcffc72820c49245
3
+ metadata.gz: 0415b35e7fad280740b21548ef140b5c12f98c96
4
+ data.tar.gz: 4afcc45cb368ebf1c156f0b9d96aa0f33ebb0ce1
5
5
  SHA512:
6
- metadata.gz: f82d8b530f540eeed8436fef0f84f84dadbdea515ef757516a6f50f5c6ed64964ebed25293a5babd6895a2bb3e881b6e82e72e0b8d50201af202f58b27e973c1
7
- data.tar.gz: 9d322ab00a9088b89b5f78c2bf0745588f8ff294676b03a9737df32d1eeb97df560e3efb74d6d265acb6201a6a90b6a7f8a6224f3cd916b3f93751c7c566dd87
6
+ metadata.gz: 567206dc928b7c3040bafe757ac18565a932c822f0d8d4301ec611035c01bcf773f4b982c52c7b2333432c8f5bebd9e45027e406c6bc1aef99736f8323a5e0d1
7
+ data.tar.gz: 9ecde47ac49072af4a436a39b11a465f43785482980e60f74dbd51c16c6aa33127709da278f604f58c87f565222311e81e0f30493ba123a55e00614a18cfd23d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # v0.4.0
2
+ * [feature] Add `supermarket` command for static repository generation
3
+
1
4
  # v0.3.14
2
5
  * [enhancement] Use threads when installing cookbooks (#72 thanks @sawanoboly!)
3
6
 
data/README.md CHANGED
@@ -425,6 +425,31 @@ within the `batali.manifest` file. If cookbooks are defined within the
425
425
  `batali.manifest` file that have not been uploaded to the Chef server, those
426
426
  cookbooks will be uploaded.
427
427
 
428
+ ## Supermarket
429
+
430
+ Batali can generate a static supermarket repository from a `batali.manifest`. The resultant
431
+ directory can then be hosted with an httpd of choice. To generate a supermarket repository,
432
+ first run `resolve`:
433
+
434
+ ```
435
+ $ batali resolve
436
+ ```
437
+
438
+ Next, run the `supermarket` command to generate the repository:
439
+
440
+ ```
441
+ $ batali supermarket
442
+ ```
443
+
444
+ A new directory will be created (`./supermarket`) which contains the newly generated
445
+ supermarket respository. The generated `universe` file will contain URLs pointing
446
+ to `localhost`, which is the default behavior. In practice, it will be desirable to
447
+ update the URL to provide the customized location:
448
+
449
+ ```
450
+ $ batali supermarket --remote-supermarket-url="http://supermarket.example.com"
451
+ ```
452
+
428
453
  # Info
429
454
 
430
455
  * Repository: https://github.com/hw-labs/batali
data/bin/batali CHANGED
@@ -80,4 +80,24 @@ Bogo::Cli::Setup.define do
80
80
  end
81
81
  end
82
82
 
83
+ command 'supermarket' do
84
+ description 'Generate a supermarket'
85
+ self.instance_exec(&global_opts)
86
+ on :p, 'path=', 'Cookbook install path'
87
+ on :I, 'infrastructure', 'Resolve infrastructure cookbooks'
88
+ on :s, 'skip-install', 'Skip cookbook installation'
89
+ on :S, 'supermarket-path=', 'Supermarket output directory', :default => 'supermarket'
90
+ on :A, 'assets-path=', 'Supermarket assets storage path', :default => File.join('supermarket', 'assets')
91
+ on :R, 'remote-supermarket-url=', 'Custom remote supermarket URL (https://myhost.com:443/supermarket)', :default => 'http://localhost'
92
+ on :D, 'download-prefix=', 'Remote location prefixed to asset name', :default => '/assets'
93
+ on :T, 'location-type=', 'Name of location type', :default => 'batali'
94
+ on :P, 'pretty-universe', 'Output formatted universe JSON'
95
+ on :U, 'universe-only', 'Only generate the supermarket universe.json file'
96
+ on :C, 'clean-assets', 'Replace any existing compressed assets'
97
+
98
+ run do |opts, args|
99
+ Batali::Command::Supermarket.new(opts, args).execute!
100
+ end
101
+ end
102
+
83
103
  end
@@ -0,0 +1,141 @@
1
+ require 'batali'
2
+ require 'rubygems/package'
3
+ require 'zlib'
4
+
5
+ module Batali
6
+ class Command
7
+ # Generate a supermarket
8
+ class Supermarket < Batali::Command
9
+
10
+ # Generate supermarket
11
+ def execute!
12
+ ui.info "Batali supermarket generator #{ui.color('started', :bold)}"
13
+ if(config[:skip_install])
14
+ ui.warn 'Skipping cookbook installation.'
15
+ else
16
+ Install.new(config.merge(:ui => ui, :install => {}), arguments).execute!
17
+ end
18
+ run_action 'Prepare supermarket destination directory' do
19
+ FileUtils.mkdir_p(File.join(config[:supermarket_path], 'api', 'v1', 'cookbooks'))
20
+ FileUtils.mkdir_p(config[:assets_path])
21
+ nil
22
+ end
23
+ new_universe = new_universe_file = universe_diff = nil
24
+ run_action 'Generate supermarket universe.json file' do
25
+ new_universe, new_universe_file = generate_universe
26
+ nil
27
+ end
28
+ unless(config[:universe_only])
29
+ if(config[:clean_assets])
30
+ Dir.glob(File.join(config[:assets_path], '*')).each do |old_asset|
31
+ FileUtils.rm(old_asset)
32
+ end
33
+ end
34
+ new_assets = generate_cookbook_assets
35
+ valid_items = new_universe.values.map(&:values).flatten.map do |info|
36
+ File.basename(info[:download_url])
37
+ end
38
+ prune_universe(valid_items)
39
+ populate_universe(valid_items)
40
+ end
41
+ run_action 'Write supermarket universe file' do
42
+ FileUtils.cp(
43
+ new_universe_file.path,
44
+ File.join(config[:supermarket_path], 'universe')
45
+ )
46
+ FileUtils.chmod(0644, File.join(config[:supermarket_path], 'universe'))
47
+ new_universe_file.delete
48
+ nil
49
+ end
50
+ ui.info "Batali supermarket generator #{ui.color('complete!', :bold, :green)}"
51
+ ui.puts " Supermarket content written to: #{config[:supermarket_path]}"
52
+ end
53
+
54
+ # Generate compressed cookbook assets
55
+ def generate_cookbook_assets
56
+ manifest.cookbook.map do |ckbk|
57
+ base_name = "#{ckbk.name}-#{ckbk.version}.tgz"
58
+ ckbk_name = infrastructure? ? "#{ckbk.name}-#{ckbk.version}" : ckbk.name
59
+ tar_ckbk_name = "#{ckbk.name}-#{ckbk.version}"
60
+ ckbk_content_path = File.join('cookbooks', ckbk_name)
61
+ ckbk_path = File.join(config[:assets_path], base_name)
62
+ unless(File.exist?(ckbk_path))
63
+ ckbk_io = File.open(ckbk_path, 'wb')
64
+ gz_io = Zlib::GzipWriter.new(ckbk_io, Zlib::BEST_COMPRESSION)
65
+ begin
66
+ gz_io.mtime = Time.now
67
+ Gem::Package::TarWriter.new(gz_io) do |tar|
68
+ unless(File.directory?(ckbk_content_path))
69
+ raise "Cookbook path not found! Run `install`. (#{ckbk_content_path})"
70
+ end
71
+ Dir.glob(File.join(ckbk_content_path, '**', '**', '*')).each do |c_file|
72
+ next unless File.file?(c_file)
73
+ stat = File.stat(c_file)
74
+ c_path = c_file.sub(File.join(ckbk_content_path, ''), '')
75
+ tar.add_file_simple(File.join(tar_ckbk_name, c_path), stat.mode, stat.size) do |dst|
76
+ File.open(c_file, 'rb') do |src|
77
+ until(src.eof?)
78
+ dst.write src.read(4096)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ ensure
85
+ gz_io.close
86
+ end
87
+ base_name
88
+ end
89
+ end.compact
90
+ end
91
+
92
+ # Prune assets from universe
93
+ #
94
+ # @param items [Array<String>] names of assets
95
+ # TODO: This is a stub for custom action
96
+ def prune_universe(items)
97
+ end
98
+
99
+ # Add assets to universe
100
+ #
101
+ # @param items [Array<String>] names of assets
102
+ # TODO: This is a stub for custom action
103
+ def populate_universe(items)
104
+ end
105
+
106
+ # Generate the supermarket universe.json file
107
+ #
108
+ # @return [Smash, File] universe content hash, universe file
109
+ def generate_universe
110
+ universe = Smash.new.tap do |uni|
111
+ manifest.cookbook.each do |ckbk|
112
+ uni.set(ckbk.name, ckbk.version.to_s,
113
+ Smash.new(
114
+ :location_type => config[:location_type],
115
+ :location_path => File.join(config[:remote_supermarket_url], 'api', 'v1'),
116
+ :download_url => File.join(
117
+ config[:remote_supermarket_url],
118
+ config[:download_prefix],
119
+ "#{ckbk.name}-#{ckbk.version}.tgz"
120
+ ),
121
+ :dependencies => Smash[
122
+ ckbk.dependencies.map do |dep|
123
+ [dep.name, dep.requirement]
124
+ end
125
+ ]
126
+ )
127
+ )
128
+ end
129
+ end
130
+
131
+ new_universe_file = Tempfile.new('batali-universe')
132
+ new_universe_file.puts MultiJson.dump(universe, :pretty => !!config[:pretty_universe])
133
+ new_universe_file.flush
134
+ new_universe_file.rewind
135
+ [universe, new_universe_file]
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+ end
@@ -14,6 +14,7 @@ module Batali
14
14
  autoload :Display, 'batali/command/display'
15
15
  autoload :Install, 'batali/command/install'
16
16
  autoload :Resolve, 'batali/command/resolve'
17
+ autoload :Supermarket, 'batali/command/supermarket'
17
18
  autoload :Update, 'batali/command/update'
18
19
 
19
20
  # Set UI when loading via command
@@ -10,7 +10,7 @@ module Batali
10
10
  class RemoteSite < Origin
11
11
 
12
12
  # Site suffix for API endpoint
13
- COOKBOOK_API_SUFFIX = 'api/v1/cookbooks'
13
+ API_SUFFIX = 'api/v1/'
14
14
 
15
15
  include Bogo::Memoization
16
16
 
@@ -22,7 +22,8 @@ module Batali
22
22
 
23
23
  def initialize(*_)
24
24
  super
25
- endpoint = URI.join(self.endpoint, COOKBOOK_API_SUFFIX).to_s
25
+ # NOTE: We currently don't require API_SUFFIX information
26
+ # self.endpoint = URI.join(endpoint, API_SUFFIX).to_s
26
27
  self.identifier = Digest::SHA256.hexdigest(endpoint)
27
28
  unless(name?)
28
29
  self.name = identifier
@@ -1,5 +1,5 @@
1
1
  # Batali namespace
2
2
  module Batali
3
3
  # Current version
4
- VERSION = Gem::Version.new('0.3.14')
4
+ VERSION = Gem::Version.new('0.4.0')
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: batali
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.14
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-04 00:00:00.000000000 Z
11
+ date: 2016-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: attribute_struct
@@ -220,6 +220,7 @@ files:
220
220
  - lib/batali/command/display.rb
221
221
  - lib/batali/command/install.rb
222
222
  - lib/batali/command/resolve.rb
223
+ - lib/batali/command/supermarket.rb
223
224
  - lib/batali/command/update.rb
224
225
  - lib/batali/config.rb
225
226
  - lib/batali/git.rb