bookbindery 4.1.0 → 4.1.1
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.
- checksums.yaml +4 -4
- data/lib/bookbinder/cf_command_runner.rb +33 -30
- data/lib/bookbinder/cli.rb +15 -7
- data/lib/bookbinder/code_example_reader.rb +5 -7
- data/lib/bookbinder/commands/bind/directory_preparer.rb +1 -1
- data/lib/bookbinder/commands/build_and_push_tarball.rb +18 -6
- data/lib/bookbinder/commands/collection.rb +16 -21
- data/lib/bookbinder/commands/push_from_local.rb +27 -19
- data/lib/bookbinder/commands/push_to_prod.rb +30 -34
- data/lib/bookbinder/commands/tag.rb +5 -6
- data/lib/bookbinder/commands/update_local_doc_repos.rb +0 -1
- data/lib/bookbinder/config/cf_credentials.rb +1 -14
- data/lib/bookbinder/deploy/app_fetcher.rb +36 -0
- data/lib/bookbinder/deploy/archive.rb +102 -0
- data/lib/bookbinder/deploy/artifact.rb +26 -0
- data/lib/bookbinder/deploy/blue_green_app.rb +29 -0
- data/lib/bookbinder/deploy/cf_routes.rb +32 -0
- data/lib/bookbinder/deploy/deployment.rb +55 -0
- data/lib/bookbinder/deploy/distributor.rb +76 -0
- data/lib/bookbinder/deploy/failure.rb +15 -0
- data/lib/bookbinder/deploy/pusher.rb +34 -0
- data/lib/bookbinder/deploy/success.rb +16 -0
- data/lib/bookbinder/ingest/cloner_factory.rb +4 -4
- data/lib/bookbinder/ingest/local_filesystem_cloner.rb +5 -6
- data/lib/bookbinder/local_file_system_accessor.rb +9 -0
- data/lib/bookbinder/{post_production → postprocessing}/sitemap_writer.rb +7 -2
- data/lib/bookbinder/preprocessing/{copy_to_site_gen_dir.rb → link_to_site_gen_dir.rb} +2 -2
- data/lib/bookbinder/server_director.rb +3 -10
- data/lib/bookbinder/sheller.rb +5 -1
- data/master_middleman/bookbinder_helpers.rb +4 -12
- data/template_app/config.ru +3 -7
- data/template_app/rack_app.rb +23 -0
- metadata +60 -58
- data/lib/bookbinder/app_fetcher.rb +0 -36
- data/lib/bookbinder/archive.rb +0 -102
- data/lib/bookbinder/artifact_namer.rb +0 -22
- data/lib/bookbinder/commands/bookbinder_command.rb +0 -18
- data/lib/bookbinder/distributor.rb +0 -80
- data/lib/bookbinder/pusher.rb +0 -34
- data/lib/bookbinder/time_fetcher.rb +0 -7
- data/lib/bookbinder/values/blue_green_app.rb +0 -27
- data/lib/bookbinder/values/cf_routes.rb +0 -30
@@ -1,36 +0,0 @@
|
|
1
|
-
require_relative 'values/blue_green_app'
|
2
|
-
require_relative 'values/cf_routes'
|
3
|
-
|
4
|
-
module Bookbinder
|
5
|
-
|
6
|
-
class AppFetcher
|
7
|
-
def initialize(routes_to_search, cf_command_runner)
|
8
|
-
@routes_to_search = routes_to_search
|
9
|
-
@cf_command_runner = cf_command_runner
|
10
|
-
end
|
11
|
-
|
12
|
-
def fetch_current_app
|
13
|
-
raw_cf_routes = cf_command_runner.cf_routes_output
|
14
|
-
cf_routes = CfRoutes.new(raw_cf_routes)
|
15
|
-
|
16
|
-
existing_hosts = routes_to_search.select do |domain, host|
|
17
|
-
cf_routes.apps_by_host_and_domain.has_key?([host, domain])
|
18
|
-
end
|
19
|
-
|
20
|
-
return nil if existing_hosts.empty?
|
21
|
-
app_groups = existing_hosts.map { |domain, host| apps_for_host(cf_routes, domain, host) }
|
22
|
-
apps_for_existing_routes = app_groups.first
|
23
|
-
apps_for_existing_routes.first
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
attr_reader :routes_to_search, :cf_command_runner
|
29
|
-
|
30
|
-
def apps_for_host(routes_from_cf, domain, host)
|
31
|
-
routes_from_cf.apps_by_host_and_domain.fetch([host, domain], []).
|
32
|
-
map &BlueGreenApp.method(:new)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
data/lib/bookbinder/archive.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
require 'fog/aws'
|
2
|
-
require 'tmpdir'
|
3
|
-
require_relative 'artifact_namer'
|
4
|
-
require_relative 'deprecated_logger'
|
5
|
-
require_relative 'time_fetcher'
|
6
|
-
|
7
|
-
module Bookbinder
|
8
|
-
class Archive
|
9
|
-
class FileDoesNotExist < StandardError; end
|
10
|
-
class NoNamespaceGiven < StandardError; end
|
11
|
-
|
12
|
-
def initialize(logger: nil, time_fetcher: TimeFetcher.new, key: '', secret: '')
|
13
|
-
@logger = logger
|
14
|
-
@time_fetcher = time_fetcher
|
15
|
-
@aws_key = key
|
16
|
-
@aws_secret_key = secret
|
17
|
-
end
|
18
|
-
|
19
|
-
def create_and_upload_tarball(build_number: nil, app_dir: 'final_app', bucket: '', namespace: nil)
|
20
|
-
build_number ||= time_fetcher.current_time
|
21
|
-
raise 'You must provide a namespace to push an identifiable build.' unless namespace
|
22
|
-
|
23
|
-
tarball_filename, tarball_path = create_tarball(app_dir, build_number, namespace)
|
24
|
-
|
25
|
-
upload_file(bucket, tarball_filename, tarball_path)
|
26
|
-
@logger.log "Green build ##{build_number.to_s.green} has been uploaded to S3 for #{namespace.to_s.cyan}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def upload_file(bucket, name, source_path)
|
30
|
-
find_or_create_directory(bucket).
|
31
|
-
files.create(key: name,
|
32
|
-
body: File.read(source_path),
|
33
|
-
public: true)
|
34
|
-
end
|
35
|
-
|
36
|
-
def download(download_dir: nil, bucket: nil, build_number: nil, namespace: nil)
|
37
|
-
raise NoNamespaceGiven, 'One must specify a namespace to find files in this bucket' unless namespace
|
38
|
-
|
39
|
-
directory = connection.directories.get bucket
|
40
|
-
build_number ||= latest_build_number_for_namespace(directory, namespace)
|
41
|
-
filename = ArtifactNamer.new(namespace, build_number, 'tgz').filename
|
42
|
-
|
43
|
-
s3_file = directory.files.get(filename)
|
44
|
-
raise FileDoesNotExist, "Unable to find tarball on AWS for book '#{namespace}', build number: #{build_number}" unless s3_file
|
45
|
-
|
46
|
-
downloaded_file = File.join(Dir.mktmpdir, 'downloaded.tgz')
|
47
|
-
File.open(downloaded_file, 'wb') { |f| f.write(s3_file.body) }
|
48
|
-
Dir.chdir(download_dir) { `tar xzf #{downloaded_file}` }
|
49
|
-
|
50
|
-
@logger.log "Green build ##{build_number.to_s.green} has been downloaded from S3 and untarred into #{download_dir.to_s.cyan}"
|
51
|
-
end
|
52
|
-
|
53
|
-
def tarball_name_regex(namespace)
|
54
|
-
/^#{namespace}-(\d+_?\d*)\.tgz/
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
attr_reader :time_fetcher
|
60
|
-
|
61
|
-
def find_or_create_directory(name)
|
62
|
-
connection.directories.create(key: name)
|
63
|
-
rescue Excon::Errors::Conflict
|
64
|
-
connection.directories.get(name)
|
65
|
-
end
|
66
|
-
|
67
|
-
def create_tarball(app_dir, build_number, namespace)
|
68
|
-
tarball_filename = ArtifactNamer.new(namespace, build_number, 'tgz').filename
|
69
|
-
tarball_path = File.join(Dir.mktmpdir, tarball_filename)
|
70
|
-
|
71
|
-
Dir.chdir(app_dir) { `tar czf #{tarball_path} *` }
|
72
|
-
return tarball_filename, tarball_path
|
73
|
-
end
|
74
|
-
|
75
|
-
def latest_build_number_for_namespace(directory, namespace)
|
76
|
-
all_files = all_files_workaround_for_fog_map_limitation(directory.files)
|
77
|
-
|
78
|
-
all_files_with_namespace = all_files.map do |file|
|
79
|
-
filename = file.key
|
80
|
-
file if filename[tarball_name_regex(namespace)]
|
81
|
-
end.compact
|
82
|
-
|
83
|
-
return nil if all_files_with_namespace.empty?
|
84
|
-
most_recent_file = all_files_with_namespace.sort_by { |file| file.last_modified }.last
|
85
|
-
|
86
|
-
most_recent_filename = most_recent_file.key
|
87
|
-
most_recent_filename[tarball_name_regex(namespace), 1]
|
88
|
-
end
|
89
|
-
|
90
|
-
def connection
|
91
|
-
@connection ||= Fog::Storage.new :provider => 'AWS',
|
92
|
-
:aws_access_key_id => @aws_key,
|
93
|
-
:aws_secret_access_key => @aws_secret_key
|
94
|
-
end
|
95
|
-
|
96
|
-
def all_files_workaround_for_fog_map_limitation(files)
|
97
|
-
all_files = []
|
98
|
-
files.each { |f| all_files << f }
|
99
|
-
all_files
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Bookbinder
|
2
|
-
class ArtifactNamer
|
3
|
-
def initialize(namespace, build_number, extension, path = '.')
|
4
|
-
@namespace = namespace
|
5
|
-
@build_number = build_number
|
6
|
-
@path = path
|
7
|
-
@extension = extension
|
8
|
-
end
|
9
|
-
|
10
|
-
def full_path
|
11
|
-
File.join(path, filename)
|
12
|
-
end
|
13
|
-
|
14
|
-
def filename
|
15
|
-
"#{namespace}-#{build_number}.#{extension}"
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
attr_reader :namespace, :build_number, :path, :extension
|
21
|
-
end
|
22
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Bookbinder
|
2
|
-
module Commands
|
3
|
-
class BookbinderCommand
|
4
|
-
def initialize(logger, configuration_fetcher)
|
5
|
-
@logger = logger
|
6
|
-
@configuration_fetcher = configuration_fetcher
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def config
|
12
|
-
@config ||= configuration_fetcher.fetch_config
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :configuration_fetcher
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require_relative 'app_fetcher'
|
2
|
-
require_relative 'archive'
|
3
|
-
require_relative 'artifact_namer'
|
4
|
-
require_relative 'cf_command_runner'
|
5
|
-
require_relative 'ingest/destination_directory'
|
6
|
-
require_relative 'pusher'
|
7
|
-
require_relative 'sheller'
|
8
|
-
|
9
|
-
module Bookbinder
|
10
|
-
class Distributor
|
11
|
-
EXPIRATION_HOURS = 2
|
12
|
-
|
13
|
-
def self.build(logger, options)
|
14
|
-
namespace = Ingest::DestinationDirectory.new(options[:book_repo])
|
15
|
-
namer = ArtifactNamer.new(namespace, options[:build_number], 'log', '/tmp')
|
16
|
-
|
17
|
-
archive = Archive.new(logger: logger, key: options[:aws_credentials].access_key, secret: options[:aws_credentials].secret_key)
|
18
|
-
cf_command_runner = CfCommandRunner.new(logger, Sheller.new, options[:cf_credentials], namer.full_path)
|
19
|
-
cf_app_fetcher = AppFetcher.new(options[:cf_credentials].flat_routes, cf_command_runner)
|
20
|
-
|
21
|
-
pusher = Pusher.new(cf_command_runner, cf_app_fetcher)
|
22
|
-
new(logger, archive, pusher, namespace, namer, options)
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(logger, archive, pusher, namespace, namer, options)
|
26
|
-
@logger = logger
|
27
|
-
@archive = archive
|
28
|
-
@pusher = pusher
|
29
|
-
@namespace = namespace
|
30
|
-
@namer = namer
|
31
|
-
@options = options
|
32
|
-
end
|
33
|
-
|
34
|
-
def distribute
|
35
|
-
download if cf_credentials.download_archive_before_push?
|
36
|
-
push_app
|
37
|
-
nil
|
38
|
-
rescue => e
|
39
|
-
@logger.error(<<-ERROR.chomp)
|
40
|
-
[ERROR] #{e.message}
|
41
|
-
[DEBUG INFO]
|
42
|
-
CF organization: #{cf_credentials.organization}
|
43
|
-
CF space: #{cf_credentials.space}
|
44
|
-
CF account: #{cf_credentials.username}
|
45
|
-
routes: #{cf_credentials.routes}
|
46
|
-
ERROR
|
47
|
-
raise
|
48
|
-
ensure
|
49
|
-
upload_trace
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
attr_reader :options, :archive, :namer, :namespace, :pusher
|
55
|
-
|
56
|
-
def download
|
57
|
-
archive.download(download_dir: options[:app_dir],
|
58
|
-
bucket: options[:aws_credentials].green_builds_bucket,
|
59
|
-
build_number: options[:build_number],
|
60
|
-
namespace: namespace)
|
61
|
-
end
|
62
|
-
|
63
|
-
def push_app
|
64
|
-
@logger.warn(cf_credentials.push_warning) if cf_credentials.push_warning
|
65
|
-
pusher.push(options[:app_dir])
|
66
|
-
end
|
67
|
-
|
68
|
-
def upload_trace
|
69
|
-
uploaded_file = archive.upload_file(options[:aws_credentials].green_builds_bucket, namer.filename, namer.full_path)
|
70
|
-
@logger.log("Your cf trace file is available at: #{uploaded_file.url(Time.now.to_i + EXPIRATION_HOURS*60*60).green}")
|
71
|
-
@logger.log("This URL will expire in #{EXPIRATION_HOURS} hours, so if you need to share it, make sure to save a copy now.")
|
72
|
-
rescue Errno::ENOENT
|
73
|
-
@logger.error "Could not find CF trace file: #{namer.full_path}"
|
74
|
-
end
|
75
|
-
|
76
|
-
def cf_credentials
|
77
|
-
options[:cf_credentials]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
data/lib/bookbinder/pusher.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require_relative 'app_fetcher'
|
2
|
-
|
3
|
-
module Bookbinder
|
4
|
-
class Pusher
|
5
|
-
def initialize(cf_cli, app_fetcher)
|
6
|
-
@cf_cli = cf_cli
|
7
|
-
@app_fetcher = app_fetcher
|
8
|
-
end
|
9
|
-
|
10
|
-
def push(app_dir)
|
11
|
-
Dir.chdir(app_dir) do
|
12
|
-
cf_cli.login
|
13
|
-
|
14
|
-
old_app = app_fetcher.fetch_current_app
|
15
|
-
|
16
|
-
if old_app
|
17
|
-
new_app = old_app.with_flipped_name
|
18
|
-
cf_cli.start(new_app)
|
19
|
-
cf_cli.push(new_app)
|
20
|
-
cf_cli.map_routes(new_app)
|
21
|
-
cf_cli.takedown_old_target_app(old_app)
|
22
|
-
else
|
23
|
-
new_app = cf_cli.new_app
|
24
|
-
cf_cli.push(new_app)
|
25
|
-
cf_cli.map_routes(new_app)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
attr_reader :cf_cli, :app_fetcher
|
33
|
-
end
|
34
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module Bookbinder
|
2
|
-
class BlueGreenApp
|
3
|
-
def initialize(name)
|
4
|
-
@name = name.strip
|
5
|
-
end
|
6
|
-
|
7
|
-
def ==(other)
|
8
|
-
to_s == other.to_s
|
9
|
-
end
|
10
|
-
|
11
|
-
def to_s
|
12
|
-
name
|
13
|
-
end
|
14
|
-
|
15
|
-
def with_flipped_name
|
16
|
-
if name.match(/green$/)
|
17
|
-
BlueGreenApp.new(name.sub(/green$/, 'blue'))
|
18
|
-
else
|
19
|
-
BlueGreenApp.new(name.sub(/blue$/, 'green'))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
attr_reader :name
|
26
|
-
end
|
27
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Bookbinder
|
2
|
-
class CfRoutes
|
3
|
-
def initialize(raw_routes)
|
4
|
-
@raw_routes = raw_routes
|
5
|
-
end
|
6
|
-
|
7
|
-
def apps_by_host_and_domain
|
8
|
-
@apps_by_host_and_domain ||= data(raw_routes).reduce({}) {|acc, row|
|
9
|
-
parsed_row = Hash[headers(raw_routes).zip(row)]
|
10
|
-
acc.merge(parsed_row.values_at('host', 'domain') => parse_apps(parsed_row['apps']))
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
attr_reader :raw_routes
|
17
|
-
|
18
|
-
def parse_apps(apps)
|
19
|
-
apps.split(',').map(&:strip)
|
20
|
-
end
|
21
|
-
|
22
|
-
def headers(raw)
|
23
|
-
raw.lines[2].split(/\s+/)
|
24
|
-
end
|
25
|
-
|
26
|
-
def data(raw)
|
27
|
-
raw.lines[3..-1].map {|line| line.split(/\s+/, headers(raw).size)}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|