moonshot 0.7.0
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 +7 -0
- data/lib/moonshot/artifact_repository/s3_bucket.rb +60 -0
- data/lib/moonshot/artifact_repository/s3_bucket_via_github_releases.rb +89 -0
- data/lib/moonshot/build_mechanism/github_release.rb +148 -0
- data/lib/moonshot/build_mechanism/script.rb +84 -0
- data/lib/moonshot/build_mechanism/travis_deploy.rb +70 -0
- data/lib/moonshot/build_mechanism/version_proxy.rb +55 -0
- data/lib/moonshot/cli.rb +146 -0
- data/lib/moonshot/controller.rb +151 -0
- data/lib/moonshot/controller_config.rb +25 -0
- data/lib/moonshot/creds_helper.rb +28 -0
- data/lib/moonshot/deployment_mechanism/code_deploy.rb +303 -0
- data/lib/moonshot/doctor_helper.rb +57 -0
- data/lib/moonshot/environment_parser.rb +32 -0
- data/lib/moonshot/interactive_logger_proxy.rb +49 -0
- data/lib/moonshot/resources.rb +13 -0
- data/lib/moonshot/resources_helper.rb +24 -0
- data/lib/moonshot/shell.rb +52 -0
- data/lib/moonshot/stack.rb +345 -0
- data/lib/moonshot/stack_asg_printer.rb +151 -0
- data/lib/moonshot/stack_config.rb +12 -0
- data/lib/moonshot/stack_events_poller.rb +56 -0
- data/lib/moonshot/stack_lister.rb +20 -0
- data/lib/moonshot/stack_output_printer.rb +16 -0
- data/lib/moonshot/stack_parameter_printer.rb +73 -0
- data/lib/moonshot/stack_template.rb +35 -0
- data/lib/moonshot/unicode_table.rb +63 -0
- data/lib/moonshot.rb +41 -0
- metadata +239 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cdc444c7d1abfc032353018913e6a8eccb90b035
|
4
|
+
data.tar.gz: 82a6a2f524bc812d9c0c7155b1437a10c378594d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 870266482ea59c020549ec66f4cd114b4cdb2dff89957a18387ff3eb6409b5f7f1b831c06c0503397ed79cc597bfdc659b8d9100bfeb9d2df3f177aad5953538
|
7
|
+
data.tar.gz: efbef2587e5b0305c608bcdd21695388145a98f981097eb5be875dcb27faa1cbeac1e354631891b3fd8ced6b3d898d7c3d59050199ea21c006decb65e8072c84
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# The S3Bucket stores builds in an S3 Bucket.
|
2
|
+
#
|
3
|
+
# For example:
|
4
|
+
#
|
5
|
+
# def MyApplication < Moonshot::CLI
|
6
|
+
# self.artifact_repository = S3Bucket.new('my-application-builds')
|
7
|
+
# end
|
8
|
+
class Moonshot::ArtifactRepository::S3Bucket
|
9
|
+
include Moonshot::ResourcesHelper
|
10
|
+
include Moonshot::CredsHelper
|
11
|
+
include Moonshot::DoctorHelper
|
12
|
+
|
13
|
+
attr_reader :bucket_name
|
14
|
+
|
15
|
+
def initialize(bucket_name)
|
16
|
+
@bucket_name = bucket_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def store_hook(build_mechanism, version_name)
|
20
|
+
unless build_mechanism.respond_to?(:output_file)
|
21
|
+
raise "S3Bucket does not know how to store artifacts from #{build_mechanism.class}, no method '#output_file'." # rubocop:disable LineLength
|
22
|
+
end
|
23
|
+
|
24
|
+
file = build_mechanism.output_file
|
25
|
+
bucket_name = @bucket_name
|
26
|
+
key = filename_for_version(version_name)
|
27
|
+
|
28
|
+
ilog.start_threaded "Uploading #{file} to s3://#{bucket_name}/#{key}" do |s|
|
29
|
+
s3_client.put_object(key: key, body: File.open(file), bucket: bucket_name)
|
30
|
+
s.success "Uploaded s3://#{bucket_name}/#{key} successfully."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def filename_for_version(version_name)
|
35
|
+
"#{version_name}.tar.gz"
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def doctor_check_bucket_exists
|
41
|
+
s3_client.get_bucket_location(bucket: @bucket_name)
|
42
|
+
success "Bucket '#{@bucket_name}' exists."
|
43
|
+
rescue => e
|
44
|
+
# This is warning because the role you use for deployment may not actually
|
45
|
+
# be able to read builds, however the instance role assigned to the nodes
|
46
|
+
# might.
|
47
|
+
str = "Could not get information about bucket '#{@bucket_name}'."
|
48
|
+
warning(str, e.message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def doctor_check_bucket_writable
|
52
|
+
s3_client.put_object(key: 'test-object', body: '', bucket: @bucket_name)
|
53
|
+
s3_client.delete_object(key: 'test-object', bucket: @bucket_name)
|
54
|
+
success 'Bucket is writable, new builds can be uploaded.'
|
55
|
+
rescue => e
|
56
|
+
# This is a warning because you may deploy to an environment where you have
|
57
|
+
# read access to builds, but could not publish a new build.
|
58
|
+
warning('Could not write to bucket, you may still be able to deploy existing builds.', e.message) # rubocop:disable LineLength
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'moonshot/artifact_repository/s3_bucket'
|
2
|
+
require 'moonshot/shell'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'semantic'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
module Moonshot::ArtifactRepository
|
8
|
+
# S3 Bucket repository backed by GitHub releases.
|
9
|
+
# If a SemVer package isn't found in S3, it is copied from GitHub releases.
|
10
|
+
class S3BucketViaGithubReleases < S3Bucket
|
11
|
+
include Moonshot::BuildMechanism
|
12
|
+
include Moonshot::Shell
|
13
|
+
|
14
|
+
# @override
|
15
|
+
# If release version, transfer from GitHub to S3.
|
16
|
+
def store_hook(build_mechanism, version)
|
17
|
+
if release?(version)
|
18
|
+
if (@output_file = build_mechanism.output_file)
|
19
|
+
attach_release_asset(version, @output_file)
|
20
|
+
# Upload to s3.
|
21
|
+
super
|
22
|
+
else
|
23
|
+
# If there is no output file, assume it's on GitHub already.
|
24
|
+
transfer_release_asset_to_s3(version)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @override
|
32
|
+
# If release version, transfer from GitHub to S3.
|
33
|
+
# @todo This is a super hacky place to handle the transfer, give
|
34
|
+
# artifact repositories a hook before deploy.
|
35
|
+
def filename_for_version(version)
|
36
|
+
s3_name = super
|
37
|
+
if !@output_file && release?(version) && !in_s3?(s3_name)
|
38
|
+
github_to_s3(version, s3_name)
|
39
|
+
end
|
40
|
+
s3_name
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def release?(version)
|
46
|
+
::Semantic::Version.new(version)
|
47
|
+
rescue ArgumentError
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def in_s3?(key)
|
52
|
+
s3_client.head_object(key: key, bucket: bucket_name)
|
53
|
+
rescue ::Aws::S3::Errors::NotFound
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def attach_release_asset(version, file)
|
58
|
+
# -m '' leaves message unchanged.
|
59
|
+
cmd = "hub release edit #{version} -m '' --attach=#{file}"
|
60
|
+
sh_step(cmd)
|
61
|
+
end
|
62
|
+
|
63
|
+
def transfer_release_asset_to_s3(version)
|
64
|
+
ilog.start_threaded "Transferring #{version} to S3" do |s|
|
65
|
+
key = filename_for_version(version)
|
66
|
+
s.success "Uploaded s3://#{bucket_name}/#{key} successfully."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def github_to_s3(version, s3_name)
|
71
|
+
Dir.mktmpdir('github_to_s3', Dir.getwd) do |tmpdir|
|
72
|
+
Dir.chdir(tmpdir) do
|
73
|
+
sh_out("hub release download #{version}")
|
74
|
+
file = File.open(Dir.glob("*#{version}*.tar.gz").fetch(0))
|
75
|
+
s3_client.put_object(key: s3_name, body: file, bucket: bucket_name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def doctor_check_hub_release_download
|
81
|
+
sh_out('hub release download --help')
|
82
|
+
rescue
|
83
|
+
critical '`hub release download` command missing, upgrade hub.' \
|
84
|
+
' See https://github.com/github/hub/pull/1103'
|
85
|
+
else
|
86
|
+
success '`hub release download` command available.'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'moonshot/shell'
|
3
|
+
require 'open3'
|
4
|
+
require 'semantic'
|
5
|
+
require 'shellwords'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'vandamme'
|
8
|
+
|
9
|
+
module Moonshot::BuildMechanism
|
10
|
+
# A build mechanism that creates a tag and GitHub release.
|
11
|
+
class GithubRelease # rubocop:disable Metrics/ClassLength
|
12
|
+
extend Forwardable
|
13
|
+
include Moonshot::ResourcesHelper
|
14
|
+
include Moonshot::DoctorHelper
|
15
|
+
include Moonshot::Shell
|
16
|
+
|
17
|
+
def_delegator :@build_mechanism, :output_file
|
18
|
+
|
19
|
+
# @param build_mechanism Delegates building after GitHub release is created.
|
20
|
+
def initialize(build_mechanism)
|
21
|
+
@build_mechanism = build_mechanism
|
22
|
+
end
|
23
|
+
|
24
|
+
def doctor_hook
|
25
|
+
super
|
26
|
+
@build_mechanism.doctor_hook
|
27
|
+
end
|
28
|
+
|
29
|
+
def resources=(r)
|
30
|
+
super
|
31
|
+
@build_mechanism.resources = r
|
32
|
+
end
|
33
|
+
|
34
|
+
def pre_build_hook(version)
|
35
|
+
@semver = ::Semantic::Version.new(version)
|
36
|
+
@target_version = [@semver.major, @semver.minor, @semver.patch].join('.')
|
37
|
+
sh_step('git fetch --tags upstream')
|
38
|
+
@sha = `git rev-parse HEAD`.chomp
|
39
|
+
validate_commit
|
40
|
+
@changes = validate_changelog(@target_version)
|
41
|
+
confirm_or_fail(@semver)
|
42
|
+
@build_mechanism.pre_build_hook(version)
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_hook(version)
|
46
|
+
assert_state(version)
|
47
|
+
git_tag(version, @sha, @changes)
|
48
|
+
git_push_tag('upstream', version)
|
49
|
+
hub_create_release(@semver, @sha, @changes)
|
50
|
+
ilog.msg("#{releases_url}/tag/#{version}")
|
51
|
+
@build_mechanism.build_hook(version)
|
52
|
+
end
|
53
|
+
|
54
|
+
def post_build_hook(version)
|
55
|
+
assert_state(version)
|
56
|
+
@build_mechanism.post_build_hook(version)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# We carry state between hooks, make sure that's still valid.
|
62
|
+
def assert_state(version)
|
63
|
+
raise "#{version} != #{@semver}" unless version == @semver.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def confirm_or_fail(version)
|
67
|
+
say("\nCommit Summary", :yellow)
|
68
|
+
say("#{@commit_detail}\n")
|
69
|
+
say('Commit CI Status', :yellow)
|
70
|
+
say("#{@ci_statuses}\n")
|
71
|
+
say("Changelog for #{version}", :yellow)
|
72
|
+
say("#{@changes}\n\n")
|
73
|
+
|
74
|
+
q = "Do you wan't to tag and release this commit as #{version}? [y/n]"
|
75
|
+
raise Thor::Error, 'Release declined.' unless yes?(q)
|
76
|
+
end
|
77
|
+
|
78
|
+
def git_tag(tag, sha, annotation)
|
79
|
+
cmd = "git tag -a #{tag} #{sha} --file=-"
|
80
|
+
sh_step(cmd, stdin: annotation)
|
81
|
+
end
|
82
|
+
|
83
|
+
def git_push_tag(remote, tag)
|
84
|
+
cmd = "git push #{remote} refs/tags/#{tag}:refs/tags/#{tag}"
|
85
|
+
sh_step(cmd) do
|
86
|
+
sleep 2 # GitHub needs a moment to register the tag.
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def hub_create_release(semver, commitish, changelog_entry)
|
91
|
+
message = "#{semver}\n\n#{changelog_entry}"
|
92
|
+
cmd = "hub release create #{semver} --commitish=#{commitish}"
|
93
|
+
cmd << ' --prerelease' if semver.pre || semver.build
|
94
|
+
cmd << " --message=#{Shellwords.escape(message)}"
|
95
|
+
sh_step(cmd)
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_commit
|
99
|
+
cmd = "git show --stat #{@sha}"
|
100
|
+
sh_step(cmd, msg: "Validate commit #{@sha}.") do |_, out|
|
101
|
+
@commit_detail = out
|
102
|
+
end
|
103
|
+
cmd = "hub ci-status --verbose #{@sha}"
|
104
|
+
sh_step(cmd, msg: "Check CI status for #{@sha}.") do |_, out|
|
105
|
+
@ci_statuses = out
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def validate_changelog(version)
|
110
|
+
changes = nil
|
111
|
+
ilog.start_threaded('Validate `CHANGELOG.md`.') do |step|
|
112
|
+
changes = fetch_changes(version)
|
113
|
+
step.success
|
114
|
+
end
|
115
|
+
changes
|
116
|
+
end
|
117
|
+
|
118
|
+
def fetch_changes(version)
|
119
|
+
parser = Vandamme::Parser.new(
|
120
|
+
changelog: File.read('CHANGELOG.md'),
|
121
|
+
format: 'markdown'
|
122
|
+
)
|
123
|
+
parser.parse.fetch(version) do
|
124
|
+
raise "#{version} not found in CHANGELOG.md"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def releases_url
|
129
|
+
`hub browse -u -- releases`.chomp
|
130
|
+
end
|
131
|
+
|
132
|
+
def doctor_check_upstream
|
133
|
+
sh_out('git remote | grep ^upstream$')
|
134
|
+
rescue => e
|
135
|
+
critical "git remote `upstream` not found.\n#{e.message}"
|
136
|
+
else
|
137
|
+
success 'git remote `upstream` exists.'
|
138
|
+
end
|
139
|
+
|
140
|
+
def doctor_check_hub_auth
|
141
|
+
sh_out('hub ci-status 0.0.0')
|
142
|
+
rescue => e
|
143
|
+
critical "`hub` failed, install hub and authorize it.\n#{e.message}"
|
144
|
+
else
|
145
|
+
success '`hub` installed and authorized.'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'open3'
|
2
|
+
include Open3
|
3
|
+
|
4
|
+
# Compile a release artifact using a shell script.
|
5
|
+
#
|
6
|
+
# The output file will be deleted before the script is run, and is expected to
|
7
|
+
# exist after the script exits. Any non-zero exit status will be consider a
|
8
|
+
# build failure, and any output will be displayed to the user.
|
9
|
+
#
|
10
|
+
# Creating a new Script BuildMechanism looks like this:
|
11
|
+
#
|
12
|
+
# class MyReleaseTool < Moonshot::CLI
|
13
|
+
# include Moonshot::BuildMechanism
|
14
|
+
# self.build_mechanism = Script.new('script/build.sh')
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
class Moonshot::BuildMechanism::Script
|
18
|
+
include Moonshot::ResourcesHelper
|
19
|
+
include Moonshot::DoctorHelper
|
20
|
+
|
21
|
+
attr_reader :output_file
|
22
|
+
|
23
|
+
def initialize(script, output_file: 'output.tar.gz')
|
24
|
+
@script = script
|
25
|
+
@output_file = output_file
|
26
|
+
end
|
27
|
+
|
28
|
+
def pre_build_hook(_version)
|
29
|
+
File.delete(@output_file) if File.exist?(@output_file)
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_hook(version)
|
33
|
+
env = {
|
34
|
+
'VERSION' => version,
|
35
|
+
'OUTPUT_FILE' => @output_file
|
36
|
+
}
|
37
|
+
ilog.start_threaded "Running Script: #{@script}" do |s|
|
38
|
+
run_script(s, env: env)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def post_build_hook(_version)
|
43
|
+
unless File.exist?(@output_file) # rubocop:disable GuardClause
|
44
|
+
raise Thor::Error, 'Build command did not produce output file!'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def run_script(step, env: {}) # rubocop:disable AbcSize
|
51
|
+
popen2e(env, @script) do |_, out, wait|
|
52
|
+
output = []
|
53
|
+
|
54
|
+
loop do
|
55
|
+
str = out.gets
|
56
|
+
unless str.nil?
|
57
|
+
output << str.chomp
|
58
|
+
ilog.debug(str.chomp)
|
59
|
+
end
|
60
|
+
break if out.eof?
|
61
|
+
end
|
62
|
+
|
63
|
+
result = wait.value
|
64
|
+
if result.exitstatus == 0
|
65
|
+
step.success "Build script #{@script} exited successfully!"
|
66
|
+
end
|
67
|
+
unless result.exitstatus == 0
|
68
|
+
ilog.error "Build script failed with exit status #{result.exitstatus}!"
|
69
|
+
ilog.error 'Last 10 lines of output follows:'
|
70
|
+
output.pop(10).each { |l| ilog.error l }
|
71
|
+
|
72
|
+
step.failure "Build script #{@script} failed with exit status #{result.exitstatus}!"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def doctor_check_script_exists
|
78
|
+
if File.exist?(@script)
|
79
|
+
success "Script '#{@script}' exists."
|
80
|
+
else
|
81
|
+
critical "Could not find build script '#{@script}'!"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'moonshot/shell'
|
2
|
+
|
3
|
+
module Moonshot::BuildMechanism
|
4
|
+
# This simply waits for Travis-CI to finish building a job matching the
|
5
|
+
# version and 'BUILD=1'.
|
6
|
+
class TravisDeploy
|
7
|
+
include Moonshot::ResourcesHelper
|
8
|
+
include Moonshot::DoctorHelper
|
9
|
+
include Moonshot::Shell
|
10
|
+
|
11
|
+
attr_reader :output_file
|
12
|
+
|
13
|
+
def initialize(slug, pro: false)
|
14
|
+
@slug = slug
|
15
|
+
@endpoint = pro ? '--pro' : '--org'
|
16
|
+
@cli_args = "-r #{@slug} #{@endpoint}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def pre_build_hook(_)
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_hook(version)
|
23
|
+
job_number = find_build_and_job(version)
|
24
|
+
wait_for_job(job_number)
|
25
|
+
check_build(version)
|
26
|
+
end
|
27
|
+
|
28
|
+
def post_build_hook(_)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def find_build_and_job(version)
|
34
|
+
job_number = nil
|
35
|
+
ilog.start_threaded('Find Travis CI build') do |step|
|
36
|
+
sleep 2
|
37
|
+
build_out = sh_out("bundle exec travis show #{@cli_args} #{version}")
|
38
|
+
unless (job_number = build_out.match(/^#(\d+\.\d+) .+BUILD=1.+/)[1])
|
39
|
+
raise "Build for #{version} not found.\n#{build_out}"
|
40
|
+
end
|
41
|
+
step.success("Travis CI ##{job_number.gsub(/\..*/, '')} running.")
|
42
|
+
end
|
43
|
+
job_number
|
44
|
+
end
|
45
|
+
|
46
|
+
def wait_for_job(job_number)
|
47
|
+
cmd = "bundle exec travis logs #{@cli_args} #{job_number}"
|
48
|
+
# This log tailing fails at the end of the file. travis bug.
|
49
|
+
sh_step(cmd, fail: false)
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_build(version)
|
53
|
+
cmd = "bundle exec travis show #{@cli_args} #{version}"
|
54
|
+
sh_step(cmd) do |step, out|
|
55
|
+
raise "Build didn't pass.\n#{build_out}" \
|
56
|
+
if out =~ /^#(\d+\.\d+) (?!passed).+BUILD=1.+/
|
57
|
+
|
58
|
+
step.success("Travis CI build for #{version} passed.")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def doctor_check_travis_auth
|
63
|
+
sh_out("bundle exec travis raw #{@endpoint} repos/#{@slug}")
|
64
|
+
rescue => e
|
65
|
+
critical "`travis` not available or not authorized.\n#{e.message}"
|
66
|
+
else
|
67
|
+
success '`travis` installed and authorized.'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'semantic'
|
3
|
+
|
4
|
+
# This proxies build request do different mechanisms. One for semver compliant
|
5
|
+
# releases and another for everything else.
|
6
|
+
class Moonshot::BuildMechanism::VersionProxy
|
7
|
+
extend Forwardable
|
8
|
+
include Moonshot::ResourcesHelper
|
9
|
+
|
10
|
+
def_delegator :@active, :output_file
|
11
|
+
|
12
|
+
def initialize(release:, dev:)
|
13
|
+
@release = release
|
14
|
+
@dev = dev
|
15
|
+
end
|
16
|
+
|
17
|
+
def doctor_hook
|
18
|
+
@release.doctor_hook
|
19
|
+
@dev.doctor_hook
|
20
|
+
end
|
21
|
+
|
22
|
+
def resources=(r)
|
23
|
+
super
|
24
|
+
@release.resources = r
|
25
|
+
@dev.resources = r
|
26
|
+
end
|
27
|
+
|
28
|
+
def pre_build_hook(version)
|
29
|
+
active(version).pre_build_hook(version)
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_hook(version)
|
33
|
+
active(version).build_hook(version)
|
34
|
+
end
|
35
|
+
|
36
|
+
def post_build_hook(version)
|
37
|
+
active(version).post_build_hook(version)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def active(version)
|
43
|
+
@active = if release?(version)
|
44
|
+
@release
|
45
|
+
else
|
46
|
+
@dev
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def release?(version)
|
51
|
+
::Semantic::Version.new(version)
|
52
|
+
rescue ArgumentError
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
data/lib/moonshot/cli.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'interactive-logger'
|
2
|
+
|
3
|
+
# Base class for Moonshot-powered project tooling.
|
4
|
+
module Moonshot
|
5
|
+
# The main entry point for Moonshot, this class should be extended by
|
6
|
+
# project tooling.
|
7
|
+
class CLI < Thor # rubocop:disable ClassLength
|
8
|
+
class_option(:name, aliases: 'n', default: nil, type: :string)
|
9
|
+
class_option(:interactive_logger, type: :boolean, default: true)
|
10
|
+
class_option(:verbose, aliases: 'v', type: :boolean)
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :application_name
|
14
|
+
attr_accessor :artifact_repository
|
15
|
+
attr_accessor :auto_prefix_stack
|
16
|
+
attr_accessor :build_mechanism
|
17
|
+
attr_accessor :deployment_mechanism
|
18
|
+
attr_accessor :default_parent_stack
|
19
|
+
attr_reader :plugins
|
20
|
+
|
21
|
+
def plugin(plugin)
|
22
|
+
@plugins ||= []
|
23
|
+
@plugins << plugin
|
24
|
+
end
|
25
|
+
|
26
|
+
def parent(value)
|
27
|
+
@default_parent_stack = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_class_configuration
|
31
|
+
raise Thor::Error, 'No application_name is set!' unless application_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def exit_on_failure?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def inherited(base)
|
39
|
+
base.include(Moonshot::ArtifactRepository)
|
40
|
+
base.include(Moonshot::BuildMechanism)
|
41
|
+
base.include(Moonshot::DeploymentMechanism)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(*args)
|
46
|
+
super
|
47
|
+
@log = Logger.new(STDOUT)
|
48
|
+
@log.formatter = proc do |s, d, _, msg|
|
49
|
+
"[#{self.class.name} #{s} #{d.strftime('%T')}] #{msg}\n"
|
50
|
+
end
|
51
|
+
@log.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
|
52
|
+
|
53
|
+
EnvironmentParser.parse(@log)
|
54
|
+
self.class.check_class_configuration
|
55
|
+
end
|
56
|
+
|
57
|
+
no_tasks do
|
58
|
+
# Build a Moonshot::Controller from the CLI options.
|
59
|
+
def controller # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity
|
60
|
+
Moonshot::Controller.new do |config|
|
61
|
+
config.app_name = self.class.application_name
|
62
|
+
config.artifact_repository = self.class.artifact_repository
|
63
|
+
config.auto_prefix_stack = self.class.auto_prefix_stack
|
64
|
+
config.build_mechanism = self.class.build_mechanism
|
65
|
+
config.deployment_mechanism = self.class.deployment_mechanism
|
66
|
+
config.environment_name = options[:name]
|
67
|
+
config.logger = @log
|
68
|
+
|
69
|
+
# Degrade to a more compatible logger if the terminal seems outdated,
|
70
|
+
# or at the users request.
|
71
|
+
if !$stdout.isatty || !options[:interactive_logger]
|
72
|
+
config.interactive_logger = InteractiveLoggerProxy.new(@log)
|
73
|
+
end
|
74
|
+
|
75
|
+
config.show_all_stack_events = true if options[:show_all_events]
|
76
|
+
config.plugins = self.class.plugins if self.class.plugins
|
77
|
+
|
78
|
+
if options[:parent]
|
79
|
+
config.parent_stacks << options[:parent]
|
80
|
+
elsif self.class.default_parent_stack
|
81
|
+
config.parent_stacks << self.class.default_parent_stack
|
82
|
+
end
|
83
|
+
end
|
84
|
+
rescue => e
|
85
|
+
raise Thor::Error, e.message
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
desc :list, 'List stacks for this application.'
|
90
|
+
def list
|
91
|
+
controller.list
|
92
|
+
end
|
93
|
+
|
94
|
+
desc :create, 'Create a new environment.'
|
95
|
+
option(
|
96
|
+
:parent,
|
97
|
+
type: :string,
|
98
|
+
aliases: '-p',
|
99
|
+
desc: "Parent stack to import parameters from. (Default: #{default_parent_stack || 'None'})")
|
100
|
+
option :deploy, default: true, type: :boolean, aliases: '-d',
|
101
|
+
desc: 'Choose if code should be deployed after stack is created'
|
102
|
+
option :show_all_events, desc: 'Show all stack events during update. (Default: errors only)'
|
103
|
+
def create
|
104
|
+
controller.create
|
105
|
+
controller.deploy_code if options[:deploy]
|
106
|
+
end
|
107
|
+
|
108
|
+
desc :update, 'Update the CloudFormation stack within an environment.'
|
109
|
+
option :show_all_events, desc: 'Show all stack events during update. (Default: errors only)'
|
110
|
+
def update
|
111
|
+
controller.update
|
112
|
+
end
|
113
|
+
|
114
|
+
desc :status, 'Get the status of an existing environment.'
|
115
|
+
def status
|
116
|
+
controller.status
|
117
|
+
end
|
118
|
+
|
119
|
+
desc 'deploy-code', 'Create a build from the working directory, and deploy it.' # rubocop:disable LineLength
|
120
|
+
def deploy_code
|
121
|
+
controller.deploy_code
|
122
|
+
end
|
123
|
+
|
124
|
+
desc 'build-version VERSION', 'Build a tarball of the software, ready for deployment.' # rubocop:disable LineLength
|
125
|
+
def build_version(version_name)
|
126
|
+
controller.build_version(version_name)
|
127
|
+
end
|
128
|
+
|
129
|
+
desc 'deploy-version VERSION_NAME', 'Deploy a versioned release to both EB environments in an environment.' # rubocop:disable LineLength
|
130
|
+
def deploy_version(version_name)
|
131
|
+
controller.deploy_version(version_name)
|
132
|
+
end
|
133
|
+
|
134
|
+
desc :delete, 'Delete an existing environment.'
|
135
|
+
option :show_all_events, desc: 'Show all stack events during update. (Default: errors only)'
|
136
|
+
def delete
|
137
|
+
controller.delete
|
138
|
+
end
|
139
|
+
|
140
|
+
desc :doctor, 'Run configuration checks against current environment.'
|
141
|
+
def doctor
|
142
|
+
success = controller.doctor
|
143
|
+
raise Thor::Error, 'One or more checks failed.' unless success
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|