terraspace 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/lib/templates/base/project/.gitignore +1 -0
  4. data/lib/templates/base/project/config/app.rb +1 -1
  5. data/lib/terraspace/all/grapher.rb +11 -3
  6. data/lib/terraspace/all/runner.rb +17 -10
  7. data/lib/terraspace/app/callable_option/concern.rb +12 -0
  8. data/lib/terraspace/app/callable_option.rb +58 -0
  9. data/lib/terraspace/app.rb +15 -2
  10. data/lib/terraspace/autodetect.rb +3 -7
  11. data/lib/terraspace/builder/allow/base.rb +59 -0
  12. data/lib/terraspace/builder/allow/env.rb +17 -0
  13. data/lib/terraspace/builder/allow/region.rb +31 -0
  14. data/lib/terraspace/builder/allow/stack.rb +17 -0
  15. data/lib/terraspace/builder/allow.rb +3 -33
  16. data/lib/terraspace/builder.rb +11 -33
  17. data/lib/terraspace/cli/all.rb +2 -0
  18. data/lib/terraspace/cli/build/placeholder.rb +3 -1
  19. data/lib/terraspace/cli/help/all/plan.md +11 -1
  20. data/lib/terraspace/cli/help/all/up.md +10 -0
  21. data/lib/terraspace/cli/help/plan.md +9 -1
  22. data/lib/terraspace/cli/help/up.md +9 -1
  23. data/lib/terraspace/cli/init.rb +1 -1
  24. data/lib/terraspace/cli.rb +3 -2
  25. data/lib/terraspace/compiler/dirs_concern.rb +6 -1
  26. data/lib/terraspace/compiler/expander/backend.rb +1 -1
  27. data/lib/terraspace/compiler/expander.rb +38 -14
  28. data/lib/terraspace/compiler/{builder → perform}/skip.rb +1 -1
  29. data/lib/terraspace/compiler/{builder.rb → perform.rb} +21 -20
  30. data/lib/terraspace/compiler/select.rb +61 -15
  31. data/lib/terraspace/compiler/strategy/tfvar.rb +2 -1
  32. data/lib/terraspace/dependency/resolver.rb +19 -0
  33. data/lib/terraspace/terraform/args/expand.rb +25 -0
  34. data/lib/terraspace/terraform/args/thor.rb +14 -11
  35. data/lib/terraspace/terraform/ihooks/after/plan.rb +2 -2
  36. data/lib/terraspace/terraform/ihooks/base.rb +5 -0
  37. data/lib/terraspace/terraform/ihooks/before/plan.rb +3 -5
  38. data/lib/terraspace/terraform/remote_state/fetcher.rb +10 -3
  39. data/lib/terraspace/terraform/remote_state/marker/output.rb +1 -1
  40. data/lib/terraspace/version.rb +1 -1
  41. data/spec/terraspace/all/runner_spec.rb +1 -0
  42. data/terraspace.gemspec +1 -1
  43. metadata +13 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 856fb06902999857dcda1fd2a82709f7f01487a1b94420361cc7c7474df81c3e
4
- data.tar.gz: cc60137b36d9a25928b410f2ef4b65467647674a378e8732e6c55e90d2e246ca
3
+ metadata.gz: c6c8809128dbbbf51d0f654c92784a879c88cab49d31e15e2a1d827c4b40d051
4
+ data.tar.gz: c557bd4692fddd9a1bf859a986566780cb6ccd6024dcdb3e7979138a42f1e00e
5
5
  SHA512:
6
- metadata.gz: 644e41b44f03afcaab0c6e261b7021811da19dc24d8e16d1a96fbd6516d84fc714b5fe9d6a9a0042a323a0c4919561e3236c6a3979b1d0132c850309d0eb2763
7
- data.tar.gz: a74a1d2c00910e3214fc373bb21b2ccdec57f41ab628fe123ba45816ae165d875bfa543057dea518ee6457e7a1bdbc39f6dfa4e7d32e3794270d2c3153b2d17b
6
+ metadata.gz: 8b28d7d1b8565fa0f646fda57163b6d21486db7e7f704ebe657a219335e66504206769e487cec9dc3f30da06ac793e28d81b1709ebd4c32a39a70a5d8668be05
7
+ data.tar.gz: 1a09efc8d460c9267371aca9dbb130e4dbb1c9268ea870e19d6a9734aa48e0134b3cee09aedfe894acfd2c7e956a1fed4bf7b38cad9cf9b13b935802bd2fa4a7
data/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.1.0] - 2022-01-30
7
+ - [#196](https://github.com/boltops-tools/terraspace/pull/196) terraspace all: build modules in batches and only each specific stack
8
+ - [#197](https://github.com/boltops-tools/terraspace/pull/197) all plan --output and all up --plan options
9
+ - simplify starter config/app.rb
10
+
11
+ ## [1.0.6] - 2022-01-24
12
+ - [#195](https://github.com/boltops-tools/terraspace/pull/195) improve autodetection for plugin expander for backend like remote
13
+
14
+ ## [1.0.5] - 2022-01-23
15
+ - [#194](https://github.com/boltops-tools/terraspace/pull/194) ability to allow and deny envs, regions, and stacks
16
+
17
+ ## [1.0.4] - 2022-01-21
18
+ - [#193](https://github.com/boltops-tools/terraspace/pull/193) improve all include_stacks and exclude_stacks option
19
+
6
20
  ## [1.0.3] - 2022-01-20
7
21
  - [#192](https://github.com/boltops-tools/terraspace/pull/192) run super-early boot hooks before dotenv load
8
22
 
@@ -5,6 +5,7 @@ terraform.tfvars
5
5
  *.tfstate*
6
6
 
7
7
  .terraspace-cache
8
+ *.plan
8
9
 
9
10
  # OS X files
10
11
  .history
@@ -1,4 +1,4 @@
1
+ # Docs: https://terraspace.cloud/docs/config/reference/
1
2
  Terraspace.configure do |config|
2
3
  config.logger.level = :info
3
- config.test_framework = "rspec"
4
4
  end
@@ -3,14 +3,13 @@ require "tty-tree"
3
3
 
4
4
  module Terraspace::All
5
5
  class Grapher < Base
6
+ include Terraspace::Compiler::DirsConcern
6
7
  include Terraspace::Util::Logging
7
8
 
8
9
  def run
9
10
  check_graphviz!
10
11
  logger.info "Building graph..."
11
- builder = Terraspace::Builder.new(@options.merge(mod: "placeholder", quiet: true, draw_full_graph: draw_full_graph))
12
- builder.run
13
- graph = builder.graph
12
+ graph = build_graph
14
13
  if @options[:format] == "text"
15
14
  text(graph.top_nodes)
16
15
  else
@@ -18,6 +17,15 @@ module Terraspace::All
18
17
  end
19
18
  end
20
19
 
20
+ def build_graph
21
+ resolver = Terraspace::Dependency::Resolver.new(@options.merge(quiet: true, draw_full_graph: draw_full_graph))
22
+ resolver.resolve
23
+ dependencies = Terraspace::Dependency::Registry.data # populated after build_unresolved
24
+ graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
25
+ graph.build
26
+ graph
27
+ end
28
+
21
29
  def text(nodes)
22
30
  Rainbow.enabled = false unless @options[:full]
23
31
  data = build_tree_data(nodes)
@@ -1,6 +1,7 @@
1
1
  module Terraspace::All
2
2
  class Runner < Base
3
3
  include Terraspace::Util
4
+ extend Memoist
4
5
 
5
6
  def initialize(command, options={})
6
7
  @command, @options = command, options
@@ -21,32 +22,27 @@ module Terraspace::All
21
22
  end
22
23
 
23
24
  def build_batches
24
- @batches = run_builder(quiet: false)
25
+ @batches = Terraspace::Dependency::Resolver.new(@options).resolve
25
26
  @batches.reverse! if @command == "down"
26
27
  @batches
27
28
  end
28
29
 
29
30
  def deploy_batches
30
31
  truncate_logs if ENV['TS_TRUNCATE_LOGS']
32
+ build_modules
31
33
  @batches.each_with_index do |batch,i|
32
34
  logger.info "Batch Run #{i+1}:"
33
- run_builder unless i == 0 # already handled by build_batches the first time
34
35
  deploy_batch(batch)
35
36
  end
36
37
  end
37
38
 
38
- # Should run after each batch run. run_builder also calls replace_outputs.
39
- # Important: rebuild from source so placeholders are in place.
40
- def run_builder(quiet: true)
41
- Terraspace::Builder.new(@options.merge(mod: "placeholder", quiet: quiet)).run
42
- end
43
-
44
39
  def deploy_batch(batch)
45
40
  @pids = {} # stores child processes pids. map of pid to mod_name, reset this list on each batch run
46
41
  concurrency = Terraspace.config.all.concurrency
47
42
  batch.sort_by(&:name).each_slice(concurrency) do |slice|
48
43
  slice.each do |node|
49
44
  pid = fork do
45
+ build_stack(node.name)
50
46
  run_terraspace(node.name)
51
47
  end
52
48
  @pids[pid] = node.name # store mod_name mapping
@@ -57,6 +53,16 @@ module Terraspace::All
57
53
  report_errors # reports finall errors and possibly exit
58
54
  end
59
55
 
56
+ def build_modules
57
+ builder = Terraspace::Builder.new(@options.merge(mod: "placeholder", quiet: true, clean: true))
58
+ builder.build(modules: true, stack: false)
59
+ end
60
+
61
+ def build_stack(name)
62
+ builder = Terraspace::Builder.new(@options.merge(mod: name, quiet: true, clean: false))
63
+ builder.build(modules: false, stack: true)
64
+ end
65
+
60
66
  def wait_for_child_proccess
61
67
  @errors = [] # stores child processes pids that errored
62
68
  @pids.each do |pid, _|
@@ -70,7 +76,7 @@ module Terraspace::All
70
76
  @errors.each do |pid|
71
77
  mod_name = @pids[pid]
72
78
  terraspace_command = terraspace_command(mod_name)
73
- logger.error "Error running: #{terraspace_command}. Check logs and fix the error.".color(:red)
79
+ logger.error "Error running: #{terraspace_command}. Fix the error above or check logs for the error.".color(:red)
74
80
  end
75
81
  unless @errors.empty?
76
82
  exit 2 if exit_on_fail?
@@ -97,7 +103,8 @@ module Terraspace::All
97
103
  log_path: log_path(mod_name),
98
104
  terraspace_command: terraspace_command(mod_name),
99
105
  }
100
- Summary.new(data).run
106
+ # Its possible for log file to not get created if RemoteState::Fetcher#validate! fails
107
+ Summary.new(data).run if File.exist?(data[:log_path])
101
108
  end
102
109
  end
103
110
 
@@ -0,0 +1,12 @@
1
+ class Terraspace::App::CallableOption
2
+ module Concern
3
+ def callable_option(options={})
4
+ callable_option = Terraspace::App::CallableOption.new(
5
+ config_name: options[:config_name],
6
+ config_value: options[:config_value],
7
+ passed_args: options[:passed_args],
8
+ )
9
+ callable_option.object
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,58 @@
1
+ # Class represents a terraspace option that is possibly callable. Examples:
2
+ #
3
+ # config.allow.envs
4
+ # config.allow.regions
5
+ # config.deny.envs
6
+ # config.deny.regions
7
+ # config.all.include_stacks
8
+ # config.all.exclude_stacks
9
+ #
10
+ # Abstraction is definitely obtuse. Using it to get rid of duplication.
11
+ #
12
+ class Terraspace::App
13
+ class CallableOption
14
+ include Terraspace::Util::Logging
15
+
16
+ def initialize(options={})
17
+ @options = options
18
+ # Example:
19
+ # config_name: config.allow.envs
20
+ # config_value: ["dev"]
21
+ # args: [@stack_name] # passed to object.call
22
+ @config_name = options[:config_name]
23
+ @config_value = options[:config_value]
24
+ @passed_args = options[:passed_args]
25
+ end
26
+
27
+ # Returns either an Array or nil
28
+ def object
29
+ case @config_value
30
+ when nil
31
+ return nil
32
+ when Array
33
+ return @config_value
34
+ when -> (c) { c.respond_to?(:public_instance_methods) && c.public_instance_methods.include?(:call) }
35
+ object= @config_value.new
36
+ when -> (c) { c.respond_to?(:call) }
37
+ object = @config_value
38
+ else
39
+ raise "Invalid option for #{@config_name}"
40
+ end
41
+
42
+ if object
43
+ result = @passed_args.empty? ? object.call : object.call(*@passed_args)
44
+ unless result.is_a?(Array) || result.is_a?(NilClass)
45
+ message = "ERROR: The #{@config_name} needs to return an Array or nil"
46
+ logger.info message.color(:yellow)
47
+ logger.info <<~EOL
48
+ The #{@config_name} when assigned a class, object, or proc must implement
49
+ the call method and return an Array or nil.
50
+ The current return value is a #{result.class}
51
+ EOL
52
+ raise message
53
+ end
54
+ end
55
+ result
56
+ end
57
+ end
58
+ end
@@ -12,17 +12,30 @@ module Terraspace
12
12
 
13
13
  def defaults
14
14
  config = ActiveSupport::OrderedOptions.new
15
+
15
16
  config.all = ActiveSupport::OrderedOptions.new
16
17
  config.all.concurrency = 5
17
18
  config.all.exit_on_fail = ActiveSupport::OrderedOptions.new
18
19
  config.all.exit_on_fail.down = true
20
+ config.all.exit_on_fail.plan = true
19
21
  config.all.exit_on_fail.up = true
20
- config.all.ignore_stacks = nil
21
- config.all.include_stacks = nil
22
+
22
23
  config.allow = ActiveSupport::OrderedOptions.new
23
24
  config.allow.envs = nil
24
25
  config.allow.regions = nil
26
+ config.allow.stacks = nil
27
+ config.deny = ActiveSupport::OrderedOptions.new
28
+ config.deny.envs = nil
29
+ config.deny.regions = nil
30
+ config.deny.stacks = nil
31
+
32
+ config.all.exclude_stacks = nil
33
+ config.all.include_stacks = nil
34
+ config.all.consider_allow_deny_stacks = true
35
+
25
36
  config.auto_create_backend = true
37
+ config.autodetect = ActiveSupport::OrderedOptions.new
38
+ config.autodetect.expander = nil
26
39
  config.build = ActiveSupport::OrderedOptions.new
27
40
  config.build.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
28
41
  config.build.cache_root = nil # defaults to /full/path/to/.terraspace-cache
@@ -2,13 +2,9 @@ module Terraspace
2
2
  class Autodetect
3
3
  def plugin
4
4
  plugins = Terraspace::Plugin.meta.keys
5
- if plugins.size == 1
6
- plugins.first
7
- else
8
- precedence = %w[aws azurerm google]
9
- precedence.find do |p|
10
- plugins.include?(p)
11
- end
5
+ precedence = %w[aws azurerm google]
6
+ precedence.find do |p|
7
+ plugins.include?(p)
12
8
  end
13
9
  end
14
10
  end
@@ -0,0 +1,59 @@
1
+ class Terraspace::Builder::Allow
2
+ class Base
3
+ include Terraspace::App::CallableOption::Concern
4
+
5
+ def initialize(mod)
6
+ @mod = mod # Only Region subclass uses @mod but keeping interface same for Env for simplicity
7
+ @stack_name = mod.name
8
+ end
9
+
10
+ def check!
11
+ messages = []
12
+ unless allowed?
13
+ messages << message # message is interface method
14
+ end
15
+ unless messages.empty?
16
+ puts "ERROR: The configs do not allow this.".color(:red)
17
+ puts messages
18
+ exit 1
19
+ end
20
+ end
21
+
22
+ def allowed?
23
+ if allows.nil? && denys.nil?
24
+ true
25
+ elsif denys.nil?
26
+ allows.include?(check_value)
27
+ elsif allows.nil?
28
+ !denys.include?(check_value)
29
+ else
30
+ allows.include?(check_value) && !denys.include?(check_value)
31
+ end
32
+ end
33
+
34
+ def allows
35
+ callable_option(
36
+ config_name: "config.allow.#{config_name}",
37
+ config_value: config.dig(:allow, config_name),
38
+ passed_args: [@stack_name],
39
+ )
40
+ end
41
+
42
+ def denys
43
+ callable_option(
44
+ config_name: "config.deny.#{config_name}",
45
+ config_value: config.dig(:deny, config_name),
46
+ passed_args: [@stack_name],
47
+ )
48
+ end
49
+
50
+ private
51
+ def config
52
+ Terraspace.config
53
+ end
54
+
55
+ def config_name
56
+ self.class.to_s.split('::').last.underscore.pluralize.to_sym # ActiveSuport::HashWithIndifferentAccess#dig requires symbol
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,17 @@
1
+ class Terraspace::Builder::Allow
2
+ class Env < Base
3
+ # interface method
4
+ def message
5
+ messages = []
6
+ messages << "This env is not allowed to be used: TS_ENV=#{Terraspace.env}"
7
+ messages << "Allow envs: #{allows.join(', ')}" if allows
8
+ messages << "Deny envs: #{denys.join(', ')}" if denys
9
+ messages.join("\n")
10
+ end
11
+
12
+ # interface method
13
+ def check_value
14
+ Terraspace.env
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ class Terraspace::Builder::Allow
2
+ class Region < Base
3
+ # interface method
4
+ def message
5
+ messages = []
6
+ word = config_name.to_s # IE: regions or locations
7
+ messages << "This #{word.singularize} is not allowed to be used: Detected current #{word.singularize}=#{current_region}"
8
+ messages << "Allow #{word}: #{allows.join(', ')}" if allows
9
+ messages << "Deny #{word}: #{denys.join(', ')}" if denys
10
+ messages.join("\n")
11
+ end
12
+
13
+ # interface method
14
+ def check_value
15
+ current_region
16
+ end
17
+
18
+ def current_region
19
+ expander = Terraspace::Compiler::Expander.autodetect(@mod).expander
20
+ expander.region
21
+ end
22
+
23
+ def config_name
24
+ if config.allow.locations || config.deny.locations
25
+ :locations # ActiveSuport::HashWithIndifferentAccess#dig requires symbol
26
+ else
27
+ super # :regions
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ class Terraspace::Builder::Allow
2
+ class Stack < Base
3
+ # interface method
4
+ def message
5
+ messages = []
6
+ messages << "This stack is not allowed to be used for TS_ENV=#{Terraspace.env}"
7
+ messages << "Allow stacks: #{allows.join(', ')}" if allows
8
+ messages << "Deny stacks: #{denys.join(', ')}" if denys
9
+ messages.join("\n")
10
+ end
11
+
12
+ # interface method
13
+ def check_value
14
+ @mod.name
15
+ end
16
+ end
17
+ end
@@ -5,39 +5,9 @@ class Terraspace::Builder
5
5
  end
6
6
 
7
7
  def check!
8
- messages = []
9
- unless env_allowed?
10
- messages << "This env is not allowed to be used: TS_ENV=#{Terraspace.env}"
11
- messages << "Allowed envs: #{config.allow.envs.join(', ')}"
12
- end
13
- unless region_allowed?
14
- messages << "This region is not allowed to be used: Detected current region=#{current_region}"
15
- messages << "Allowed regions: #{config.allow.regions.join(', ')}"
16
- end
17
- unless messages.empty?
18
- puts "ERROR: The configs do not allow this.".color(:red)
19
- puts messages
20
- exit 1
21
- end
22
- end
23
-
24
- def env_allowed?
25
- return true unless config.allow.envs
26
- config.allow.envs.include?(Terraspace.env)
27
- end
28
-
29
- def region_allowed?
30
- return true unless config.allow.regions
31
- config.allow.regions.include?(current_region)
32
- end
33
-
34
- def current_region
35
- expander = Terraspace::Compiler::Expander.autodetect(@mod).expander
36
- expander.region
37
- end
38
-
39
- def config
40
- Terraspace.config
8
+ Env.new(@mod).check!
9
+ Stack.new(@mod).check!
10
+ Region.new(@mod).check!
41
11
  end
42
12
  end
43
13
  end
@@ -4,64 +4,42 @@ module Terraspace
4
4
  include Compiler::DirsConcern
5
5
  include Hooks::Concern
6
6
 
7
- attr_reader :graph
8
-
9
7
  def run
10
8
  return if @options[:build] == false
11
9
  Terraspace::CLI::Setup::Check.check!
10
+ check_allow!
12
11
  @mod.root_module = true
13
12
  clean
13
+ build
14
+ end
15
+
16
+ def build(modules: true, stack: true)
14
17
  build_dir = Util.pretty_path(@mod.cache_dir)
15
18
  placeholder_stack_message
16
19
  logger.info "Building #{build_dir}" unless @options[:quiet] # from terraspace all
17
-
18
- batches = nil
19
20
  FileUtils.mkdir_p(@mod.cache_dir) # so terraspace before build hooks work
20
21
  run_hooks("terraspace.rb", "build") do
21
- check_allow!
22
- build_unresolved
23
- batches = build_batches
24
- build_all
22
+ build_dir("modules") if modules
23
+ build_root_module if stack
25
24
  logger.info "Built in #{build_dir}" unless @options[:quiet] # from terraspace all
26
25
  end
27
- batches
28
26
  end
29
27
 
30
28
  def check_allow!
31
29
  Allow.new(@mod).check!
32
30
  end
33
31
 
34
- # Builds dependency graph and returns the batches to run
35
- def build_batches
36
- dependencies = Terraspace::Dependency::Registry.data # populated after build_unresolved
37
- @graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
38
- @graph.build
39
- end
40
-
41
- def build_all
42
- # At this point dependencies have been resolved.
43
- Terraspace::Terraform::RemoteState::Fetcher.flush!
44
- @resolved = true
45
- build_unresolved
46
- end
47
-
48
- def build_unresolved
49
- build_dir("modules")
50
- build_dir("stacks")
51
- build_root_module
52
- end
53
-
54
32
  def build_root_module
55
- @mod.resolved = @resolved
56
- Compiler::Builder.new(@mod).build
33
+ @mod.resolved = true
34
+ Compiler::Perform.new(@mod).compile
57
35
  end
58
36
 
59
37
  def build_dir(type_dir)
60
38
  with_each_mod(type_dir) do |mod|
61
- mod.resolved = @resolved
39
+ mod.resolved = true
62
40
  is_root_module = mod.cache_dir == @mod.cache_dir
63
41
  next if is_root_module # handled by build_root_module
64
- Compiler::Builder.new(mod).build
42
+ Compiler::Perform.new(mod).compile
65
43
  end
66
44
  end
67
45
 
@@ -38,6 +38,7 @@ class Terraspace::CLI
38
38
 
39
39
  desc "plan", "Run plan for all or multiple stacks."
40
40
  long_desc Help.text("all/plan")
41
+ option :out, aliases: :o, desc: "Output path. Can be a pattern like :MOD_NAME.plan"
41
42
  def plan(*stacks)
42
43
  Terraspace::All::Runner.new("plan", @options.merge(stacks: stacks)).run
43
44
  end
@@ -56,6 +57,7 @@ class Terraspace::CLI
56
57
 
57
58
  desc "up", "Deploy all or multiple stacks."
58
59
  long_desc Help.text("all/up")
60
+ option :plan, desc: "Plan path. Can be a pattern like :MOD_NAME.plan"
59
61
  def up(*stacks)
60
62
  Terraspace::All::Runner.new("up", @options.merge(stacks: stacks)).run
61
63
  end
@@ -3,6 +3,7 @@
3
3
  # It's useful for the summary command.
4
4
  module Terraspace::CLI::Build
5
5
  class Placeholder
6
+ include Terraspace::Compiler::DirsConcern
6
7
  include Terraspace::Util::Logging
7
8
 
8
9
  def initialize(options={})
@@ -28,7 +29,8 @@ module Terraspace::CLI::Build
28
29
  def find_stack
29
30
  stack_paths = Dir.glob("{app,vendor}/stacks/*")
30
31
  stack_paths.select! do |path|
31
- select = Terraspace::Compiler::Select.new(path)
32
+ stack_name = extract_stack_name(path)
33
+ select = Terraspace::Compiler::Select.new(stack_name)
32
34
  select.selected?
33
35
  end
34
36
  mod_path = stack_paths.last
@@ -22,4 +22,14 @@
22
22
  terraspace plan a1: Plan: 2 to add, 0 to change, 0 to destroy.
23
23
  terraspace plan a1: Changes to Outputs:
24
24
  Time took: 11s
25
- $
25
+ $
26
+
27
+ ## Using Plan Outputs
28
+
29
+ Using plan output path. You can specify an output path for the plan that contains pattern for expansion. Example:
30
+
31
+ $ terraspace all plan --out ":MOD_NAME.plan"
32
+
33
+ You can then use this later in terraspace up:
34
+
35
+ $ terraspace all up --plan ":MOD_NAME.plan"
@@ -25,3 +25,13 @@ Once you confirm, Terraspace deploys the batches in parallel. Essentially, Terra
25
25
  Time took: 25s
26
26
 
27
27
  Terraspace provides a reduced-noise summary of the runs. The full logs are also written for further inspection and debugging. The [terraspace log](https://terraspace.cloud/reference/terraspace-log/) command is useful for viewing the logs.
28
+
29
+ ## Using Plans
30
+
31
+ Using plan output path. You can specify an output path for the plan that contains pattern for expansion. Example:
32
+
33
+ $ terraspace all plan --out ":MOD_NAME.plan"
34
+
35
+ You can then use this later in terraspace up:
36
+
37
+ $ terraspace all up --plan ":MOD_NAME.plan"
@@ -26,4 +26,12 @@
26
26
  can't guarantee that exactly these actions will be performed if
27
27
  "terraform apply" is subsequently run.
28
28
 
29
- $
29
+ $
30
+
31
+ Using plan output path. You can specify an output path for the plan. Example:
32
+
33
+ $ terraspace plan demo --out "my.plan"
34
+
35
+ You can then use this later in terraspace up:
36
+
37
+ $ terraspace up demo --plan "my.plan"
@@ -27,4 +27,12 @@
27
27
 
28
28
  bucket_name = bucket-trusty-marmoset
29
29
  Time took: 39s
30
- $
30
+ $
31
+
32
+ Using plan output path. You can specify an output path for the plan. Example:
33
+
34
+ $ terraspace plan demo --out "my.plan"
35
+
36
+ You can then use this later in terraspace up:
37
+
38
+ $ terraspace up demo --plan "my.plan"
@@ -52,7 +52,7 @@ class Terraspace::CLI
52
52
  return if meta['Dir'] == '.' # root is already built
53
53
 
54
54
  remote_mod = Terraspace::Mod::Remote.new(meta, @mod)
55
- Terraspace::Compiler::Builder.new(remote_mod).build
55
+ Terraspace::Compiler::Perform.new(remote_mod).build
56
56
  end
57
57
 
58
58
  def auto?
@@ -9,7 +9,7 @@ module Terraspace
9
9
  option :yes, aliases: :y, type: :boolean, desc: "-auto-approve the terraform apply"
10
10
  }
11
11
  out_option = Proc.new {
12
- option :out, aliases: :o, desc: "Write the output to path"
12
+ option :out, aliases: :o, desc: "Output path. Can be a pattern like :MOD_NAME.plan"
13
13
  }
14
14
  input_option = Proc.new {
15
15
  option :input, type: :boolean, desc: "Ask for input for variables if not directly set."
@@ -55,6 +55,7 @@ module Terraspace
55
55
  option :quiet, type: :boolean, desc: "quiet output"
56
56
  instance_option.call
57
57
  yes_option.call
58
+ option :clean, type: :boolean, default: nil, desc: "Whether or not clean out .terraspace-cache folder first", hide: true
58
59
  def build(mod="placeholder")
59
60
  Terraspace::Builder.new(options.merge(mod: mod)).run # building any stack builds them all
60
61
  end
@@ -190,7 +191,7 @@ module Terraspace
190
191
  desc "show STACK", "Run show."
191
192
  long_desc Help.text(:show)
192
193
  instance_option.call
193
- option :plan, desc: "path to created.plan"
194
+ option :plan, desc: "Plan path. Can be a pattern like :MOD_NAME.plan"
194
195
  def show(mod, *args)
195
196
  Commander.new("show", options.merge(mod: mod, args: args)).run
196
197
  end
@@ -37,10 +37,15 @@ module Terraspace::Compiler
37
37
  # path /home/ec2-user/environment/downloads/infra/app/stacks/demo
38
38
  def select_stack?(type_dir, path)
39
39
  return true unless type_dir == "stacks"
40
- select = Terraspace::Compiler::Select.new(path)
40
+ stack_name = extract_stack_name(path)
41
+ select = Terraspace::Compiler::Select.new(stack_name)
41
42
  select.selected?
42
43
  end
43
44
 
45
+ def extract_stack_name(path)
46
+ path.sub(%r{.*(app|vendor)/stacks/}, '')
47
+ end
48
+
44
49
  def local_paths(type_dir)
45
50
  dirs("app/#{type_dir}/*") + dirs("vendor/#{type_dir}/*")
46
51
  end
@@ -12,7 +12,7 @@ class Terraspace::Compiler::Expander
12
12
  @mod = mod
13
13
  end
14
14
 
15
- COMMENT = /^\s+#/
15
+ COMMENT = /^\s*#/
16
16
  # Works for both backend.rb DSL and backend.tf ERB
17
17
  def detect
18
18
  return nil unless src_path # no backend file. returning nil means a local backend
@@ -3,8 +3,8 @@ module Terraspace::Compiler
3
3
  extend Memoist
4
4
  delegate :expand, :expansion, to: :expander
5
5
 
6
- def initialize(mod, name)
7
- @mod, @name = mod, name
6
+ def initialize(mod, backend=nil)
7
+ @mod, @backend = mod, backend
8
8
  end
9
9
 
10
10
  def expander
@@ -12,27 +12,51 @@ module Terraspace::Compiler
12
12
  end
13
13
  memoize :expander
14
14
 
15
+ # IE: TerraspacePluginAws::Interfaces::Expander
15
16
  def expander_class
16
- # IE: TerraspacePluginAws::Interfaces::Expander
17
- klass_name = Terraspace::Plugin.klass("Expander", backend: @name)
18
- klass_name ? klass_name.constantize : Terraspace::Plugin::Expander::Generic
19
- rescue NameError
17
+ class_name = expander_class_name
18
+ class_name ? class_name.constantize : Terraspace::Plugin::Expander::Generic
19
+ rescue NameError => e
20
+ logger.debug "#{e.class}: #{e.message}"
20
21
  Terraspace::Plugin::Expander::Generic
21
22
  end
22
23
 
24
+ def expander_class_name
25
+ plugin = Terraspace.config.autodetect.expander # contains plugin name. IE: aws, azurerm, google
26
+ if plugin
27
+ # early return for user override of autodetection
28
+ return Terraspace::Plugin.klass("Expander", plugin: plugin) # can return nil
29
+ end
30
+
31
+ backend = @backend || parse_backend
32
+ class_name = Terraspace::Plugin.klass("Expander", backend: backend) # can return nil
33
+ unless class_name
34
+ backend = plugin_backend
35
+ class_name = Terraspace::Plugin.klass("Expander", backend: backend) # can return nil
36
+ end
37
+ class_name
38
+ end
39
+
40
+ # autodetect by parsing backend.tf or backend.rb
41
+ def parse_backend
42
+ Backend.new(@mod).detect
43
+ end
44
+
45
+ # autodetect by looking up loaded plugins
46
+ def plugin_backend
47
+ plugin = Terraspace::Autodetect.new.plugin # IE: aws, azurerm, google
48
+ if plugin
49
+ data = Terraspace::Plugin.meta[plugin]
50
+ data[:backend] # IE: s3, azurerm, gcs
51
+ end
52
+ end
53
+
23
54
  class << self
24
55
  extend Memoist
25
-
26
56
  def autodetect(mod, opts={})
27
- backend = opts[:backend] || find_backend(mod)
28
- new(mod, backend)
57
+ new(mod, opts)
29
58
  end
30
59
  memoize :autodetect
31
-
32
- def find_backend(mod)
33
- Backend.new(mod).detect
34
- end
35
- memoize :find_backend
36
60
  end
37
61
  end
38
62
  end
@@ -1,4 +1,4 @@
1
- class Terraspace::Compiler::Builder
1
+ class Terraspace::Compiler::Perform
2
2
  class Skip
3
3
  def initialize(mod, src_path)
4
4
  @mod, @src_path = mod, src_path
@@ -1,5 +1,5 @@
1
1
  module Terraspace::Compiler
2
- class Builder
2
+ class Perform
3
3
  include CommandsConcern
4
4
  include Basename
5
5
 
@@ -7,44 +7,45 @@ module Terraspace::Compiler
7
7
  @mod = mod
8
8
  end
9
9
 
10
- def build
11
- build_config
12
- build_module if @mod.resolved
13
- build_tfvars unless command_is?(:seed) # avoid dependencies being built and erroring when backend bucket doesnt exist
10
+ def compile
11
+ compile_config
12
+ compile_module if @mod.resolved
13
+ compile_tfvars
14
14
  end
15
15
 
16
- # build common config files: provider and backend for the root module
17
- def build_config
18
- return unless build?
19
- build_config_terraform
16
+ # compile common config files: provider and backend for the root module
17
+ def compile_config
18
+ return unless compile?
19
+ compile_config_terraform
20
20
  end
21
21
 
22
- def build_module
22
+ def compile_module
23
23
  with_mod_file do |src_path|
24
- build_mod_file(src_path)
24
+ compile_mod_file(src_path)
25
25
  end
26
26
  end
27
27
 
28
- def build_tfvars
29
- return unless build?
30
- Strategy::Tfvar.new(@mod).run # writer within Strategy to control file ordering
28
+ def compile_tfvars(write: true)
29
+ return unless compile?
30
+ return if command_is?(:seed) # avoid dependencies being built and erroring when backend bucket doesnt exist
31
+ Strategy::Tfvar.new(@mod).run(write: write) # writer within Strategy to control file ordering
31
32
  end
32
33
 
33
34
  private
34
- def build?
35
+ def compile?
35
36
  @mod.type == "stack" || @mod.root_module?
36
37
  end
37
38
 
38
- def build_config_terraform
39
+ def compile_config_terraform
39
40
  expr = "#{Terraspace.root}/config/terraform/**/*"
40
41
  search(expr).each do |path|
41
42
  next unless File.file?(path)
42
43
  next if path.include?('config/terraform/tfvars')
43
- build_config_file(basename(path))
44
+ compile_config_file(basename(path))
44
45
  end
45
46
  end
46
47
 
47
- def build_config_file(file)
48
+ def compile_config_file(file)
48
49
  existing = search("#{@mod.root}/#{file}").first
49
50
  return if existing && existing.ends_with?(".tf") # do not overwrite existing backend.tf, provider.tf, etc
50
51
 
@@ -52,10 +53,10 @@ module Terraspace::Compiler
52
53
  src_path = search("#{@mod.root}/#{basename(file)}").first # existing source. IE: backend.rb in module folder
53
54
  end
54
55
  src_path ||= search("#{Terraspace.root}/config/terraform/#{file}").first
55
- build_mod_file(src_path) if src_path
56
+ compile_mod_file(src_path) if src_path
56
57
  end
57
58
 
58
- def build_mod_file(src_path)
59
+ def compile_mod_file(src_path)
59
60
  content = Strategy::Mod.new(@mod, src_path).run
60
61
  Writer.new(@mod, src_path: src_path).write(content)
61
62
  end
@@ -1,28 +1,74 @@
1
1
  module Terraspace::Compiler
2
2
  class Select
3
- def initialize(path)
4
- @path = path
5
- @stack_name = extract_stack_name(path)
3
+ include Terraspace::App::CallableOption::Concern
4
+ include Terraspace::Util::Logging
5
+
6
+ def initialize(stack_name)
7
+ @stack_name = stack_name
6
8
  end
7
9
 
8
10
  def selected?
9
- all = Terraspace.config.all
10
- # Key difference between include_stacks vs all.include_stacks option is that
11
- # the option can be nil. The local variable is guaranteed to be an Array.
12
- # This simplifies the logic.
13
- include_stacks = all.include_stacks || []
14
- ignore_stacks = all.ignore_stacks || []
15
-
16
- if all.include_stacks.nil?
17
- !ignore_stacks.include?(@stack_name)
11
+ ignore_stacks_deprecation_warning
12
+ if include_stacks.nil? && exclude_stacks.nil?
13
+ true
14
+ elsif include_stacks.nil?
15
+ !exclude_stacks.include?(@stack_name)
16
+ elsif exclude_stacks.nil?
17
+ include_stacks.include?(@stack_name)
18
18
  else
19
- stacks = include_stacks - ignore_stacks
19
+ stacks = include_stacks - exclude_stacks
20
20
  stacks.include?(@stack_name)
21
21
  end
22
22
  end
23
23
 
24
- def extract_stack_name(path)
25
- path.sub(%r{.*(app|vendor)/stacks/}, '')
24
+ def include_stacks
25
+ if config.all.include_stacks
26
+ config_name = "config.all.include_stacks"
27
+ config_value = config.dig(:all, :include_stacks)
28
+ elsif config.all.consider_allow_deny_stacks
29
+ config_name = "config.allow.stacks"
30
+ config_value = config.dig(:allow, :stacks)
31
+ else
32
+ return
33
+ end
34
+ callable_option(
35
+ config_name: config_name,
36
+ config_value: config_value,
37
+ passed_args: [@stack_name],
38
+ )
39
+ end
40
+
41
+ def exclude_stacks
42
+ if config.all.exclude_stacks
43
+ config_name = "config.all.exclude_stacks"
44
+ config_value = config.dig(:all, :exclude_stacks)
45
+ elsif config.all.consider_allow_deny_stacks
46
+ config_name = "config.deny.stacks"
47
+ config_value = config.dig(:deny, :stacks)
48
+ else
49
+ return
50
+ end
51
+ callable_option(
52
+ config_name: config_name,
53
+ config_value: config_value,
54
+ passed_args: [@stack_name],
55
+ )
56
+ end
57
+
58
+ private
59
+ def config
60
+ Terraspace.config
61
+ end
62
+
63
+ @@ignore_stacks_deprecation_warning = nil
64
+ def ignore_stacks_deprecation_warning
65
+ return unless config.all.ignore_stacks
66
+ return if @@ignore_stacks_deprecation_warning
67
+ puts <<~EOL.color(:yellow)
68
+ DEPRECATED: config.all.ignore_stacks
69
+ Instead use: config.all.exclude_stacks
70
+ EOL
71
+ @@ignore_stacks_deprecation_warning = true
26
72
  end
27
73
  end
28
74
  end
@@ -5,7 +5,7 @@ module Terraspace::Compiler::Strategy
5
5
  @order = 0
6
6
  end
7
7
 
8
- def run
8
+ def run(write: true)
9
9
  layer_paths.each do |layer_path|
10
10
  ext = File.extname(layer_path).sub('.','')
11
11
  klass = strategy_class(ext)
@@ -14,6 +14,7 @@ module Terraspace::Compiler::Strategy
14
14
  strategy = klass.new(@mod, layer_path)
15
15
  content = strategy.run
16
16
 
17
+ next unless write
17
18
  dest_name = ordered_name(layer_path)
18
19
  writer = Terraspace::Compiler::Writer.new(@mod, dest_name: dest_name)
19
20
  writer.write(content)
@@ -0,0 +1,19 @@
1
+ module Terraspace::Dependency
2
+ class Resolver
3
+ include Terraspace::Compiler::DirsConcern
4
+
5
+ def initialize(options={})
6
+ @options = options
7
+ end
8
+
9
+ def resolve
10
+ with_each_mod("stacks") do |mod|
11
+ Terraspace::Compiler::Perform.new(mod).compile_tfvars(write: false)
12
+ end
13
+
14
+ dependencies = Terraspace::Dependency::Registry.data # populated dependencies resolved
15
+ @graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
16
+ @graph.build # Returns batches to run
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module Terraspace::Terraform::Args
2
+ class Expand
3
+ class << self
4
+ def option_method(*names)
5
+ names.compact.each do |name|
6
+ option_method_each(name)
7
+ end
8
+ end
9
+
10
+ def option_method_each(name)
11
+ define_method name do
12
+ return unless @options[name]
13
+ expander = Terraspace::Compiler::Expander.autodetect(@mod)
14
+ expander.expansion(@options[name]) # pattern is a String that contains placeholders for substitutions
15
+ end
16
+ end
17
+ end
18
+
19
+ def initialize(mod, options={})
20
+ @mod, @options = mod, options
21
+ end
22
+
23
+ option_method :plan, :out
24
+ end
25
+ end
@@ -2,6 +2,8 @@ require "tempfile"
2
2
 
3
3
  module Terraspace::Terraform::Args
4
4
  class Thor
5
+ extend Memoist
6
+
5
7
  def initialize(mod, name, options={})
6
8
  @mod, @name, @options = mod, name.underscore, options
7
9
  @quiet = @options[:quiet].nil? ? true : @options[:quiet]
@@ -48,7 +50,7 @@ module Terraspace::Terraform::Args
48
50
  args << input_option
49
51
 
50
52
  # must be at the end
51
- plan = @options[:plan]
53
+ plan = expand.plan
52
54
  if plan
53
55
  copy_to_cache(plan)
54
56
  args << " #{plan}"
@@ -81,7 +83,7 @@ module Terraspace::Terraform::Args
81
83
 
82
84
  def output_args
83
85
  args = []
84
- args << "> #{expanded_out}" if @options[:out]
86
+ args << "> #{expand.out}" if expand.out
85
87
  args
86
88
  end
87
89
 
@@ -89,26 +91,22 @@ module Terraspace::Terraform::Args
89
91
  args = []
90
92
  args << input_option
91
93
  args << "-destroy" if @options[:destroy]
92
- args << "-out #{expanded_out}" if @options[:out]
93
- # Note: based on the @options[:out] will run an internal hook to copy plan
94
+ args << "-out #{expand.out}" if expand.out
95
+ # Note: based on the expand.out will run an internal hook to copy plan
94
96
  # file back up to the root project folder for use. Think this is convenient and expected behavior.
95
97
  args
96
98
  end
97
99
 
98
100
  def show_args
99
101
  args = []
100
- plan = @options[:plan]
102
+ plan = expand.plan
101
103
  if plan
102
- copy_to_cache(@options[:plan])
103
- args << " #{@options[:plan]}" # terraform show /path/to/plan
104
+ copy_to_cache(expand.plan)
105
+ args << " #{expand.plan}" # terraform show /path/to/plan
104
106
  end
105
107
  args
106
108
  end
107
109
 
108
- def expanded_out
109
- @options[:out]
110
- end
111
-
112
110
  def destroy_args
113
111
  auto_approve_arg
114
112
  end
@@ -147,5 +145,10 @@ module Terraspace::Terraform::Args
147
145
  FileUtils.mkdir_p(File.dirname(dest))
148
146
  FileUtils.cp(src, dest) unless same_file?(src, dest)
149
147
  end
148
+
149
+ def expand
150
+ Terraspace::Terraform::Args::Expand.new(@mod, @options)
151
+ end
152
+ memoize :expand
150
153
  end
151
154
  end
@@ -1,8 +1,8 @@
1
1
  module Terraspace::Terraform::Ihooks::After
2
2
  class Plan < Terraspace::Terraform::Ihooks::Base
3
3
  def run
4
- return if !@options[:out] || @options[:copy_to_root] == false
5
- copy_to_root(@options[:out])
4
+ return if !out_option || @options[:copy_to_root] == false
5
+ copy_to_root(out_option)
6
6
  end
7
7
 
8
8
  def copy_to_root(file)
@@ -4,5 +4,10 @@ module Terraspace::Terraform::Ihooks
4
4
  @name = name
5
5
  super(options)
6
6
  end
7
+
8
+ def out_option
9
+ expand = Terraspace::Terraform::Args::Expand.new(@mod, @options)
10
+ expand.out
11
+ end
7
12
  end
8
13
  end
@@ -1,12 +1,10 @@
1
1
  module Terraspace::Terraform::Ihooks::Before
2
2
  class Plan < Terraspace::Terraform::Ihooks::Base
3
3
  def run
4
- out = @options[:out]
5
- return unless out
6
- return if out =~ %r{^/} # not need to create parent dir for copy with absolute path
4
+ return unless out_option
5
+ return if out_option =~ %r{^/} # not need to create parent dir for copy with absolute path
7
6
 
8
- out = @options[:out]
9
- name = out.sub("#{Terraspace.root}/",'')
7
+ name = out_option.sub("#{Terraspace.root}/",'')
10
8
  dest = "#{@mod.cache_dir}/#{name}"
11
9
  FileUtils.mkdir_p(File.dirname(dest))
12
10
  end
@@ -2,6 +2,7 @@ module Terraspace::Terraform::RemoteState
2
2
  class Fetcher
3
3
  extend Memoist
4
4
  include Terraspace::Compiler::CommandsConcern
5
+ include Terraspace::Compiler::DirsConcern
5
6
  include Terraspace::Util::Logging
6
7
 
7
8
  def initialize(parent, identifier, options={})
@@ -130,9 +131,15 @@ module Terraspace::Terraform::RemoteState
130
131
 
131
132
  # Note we already validate mod exist at the terraform_output helper. This is just in case that logic changes.
132
133
  def validate!
133
- return if @child.exist?
134
- logger.error "ERROR: stack #{@child.name} not found".color(:red)
135
- exit 1
134
+ unless @child.exist?
135
+ logger.error "ERROR: stack #{@child.name} not found".color(:red)
136
+ exit 1
137
+ end
138
+ select = Terraspace::Compiler::Select.new(@child.name)
139
+ unless select.selected?
140
+ logger.error "ERROR: stack #{@child.name} is configured to not be included. IE: config.all.include_stacks or config.all.exclude_stacks".color(:red)
141
+ exit 1
142
+ end
136
143
  end
137
144
 
138
145
  # Using debug level because all the tfvar files always get evaluated.
@@ -25,7 +25,7 @@ module Terraspace::Terraform::RemoteState::Marker
25
25
  end
26
26
 
27
27
  def warning
28
- logger.warn "WARN: The #{@child_name} stack does not exist".color(:yellow)
28
+ logger.warn "WARN: The #{@child_name} stack does not exist or is configured to be not included. IE: config.all.include_stacks or config.all.exclude_stacks".color(:yellow)
29
29
  caller_line = caller.find { |l| l.include?('.tfvars') }
30
30
  return unless caller_line # specs dont have a tfvars file
31
31
  source_code = PrettyTracer.new(caller_line).source_code
@@ -1,3 +1,3 @@
1
1
  module Terraspace
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,6 +1,7 @@
1
1
  describe Terraspace::All::Runner do
2
2
  let(:runner) do
3
3
  runner = described_class.new(command: "up", yes: true)
4
+ allow(runner).to receive(:build_modules)
4
5
  allow(runner).to receive(:build_batches).and_return(batches)
5
6
  allow(runner).to receive(:preview)
6
7
  # Just test to the point of the run_builder and deploy_batch
data/terraspace.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Tung Nguyen"]
10
10
  spec.email = ["tung@boltops.com"]
11
11
  spec.summary = "Terraspace: The Terraspace Framework"
12
- spec.homepage = "https://github.com/boltops-tools/terraspace"
12
+ spec.homepage = "https://terraspace.cloud"
13
13
  spec.license = "Apache-2.0"
14
14
 
15
15
  spec.files = File.directory?('.git') ? `git ls-files`.split($/) : Dir.glob("**/*")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terraspace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-20 00:00:00.000000000 Z
11
+ date: 2022-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -478,12 +478,18 @@ files:
478
478
  - lib/terraspace/all/runner.rb
479
479
  - lib/terraspace/all/summary.rb
480
480
  - lib/terraspace/app.rb
481
+ - lib/terraspace/app/callable_option.rb
482
+ - lib/terraspace/app/callable_option/concern.rb
481
483
  - lib/terraspace/app/inits.rb
482
484
  - lib/terraspace/autodetect.rb
483
485
  - lib/terraspace/autoloader.rb
484
486
  - lib/terraspace/booter.rb
485
487
  - lib/terraspace/builder.rb
486
488
  - lib/terraspace/builder/allow.rb
489
+ - lib/terraspace/builder/allow/base.rb
490
+ - lib/terraspace/builder/allow/env.rb
491
+ - lib/terraspace/builder/allow/region.rb
492
+ - lib/terraspace/builder/allow/stack.rb
487
493
  - lib/terraspace/bundle.rb
488
494
  - lib/terraspace/cli.rb
489
495
  - lib/terraspace/cli/all.rb
@@ -596,8 +602,6 @@ files:
596
602
  - lib/terraspace/cli/up.rb
597
603
  - lib/terraspace/command.rb
598
604
  - lib/terraspace/compiler/basename.rb
599
- - lib/terraspace/compiler/builder.rb
600
- - lib/terraspace/compiler/builder/skip.rb
601
605
  - lib/terraspace/compiler/cleaner.rb
602
606
  - lib/terraspace/compiler/cleaner/backend_change.rb
603
607
  - lib/terraspace/compiler/commands_concern.rb
@@ -627,6 +631,8 @@ files:
627
631
  - lib/terraspace/compiler/expander.rb
628
632
  - lib/terraspace/compiler/expander/backend.rb
629
633
  - lib/terraspace/compiler/helper_extender.rb
634
+ - lib/terraspace/compiler/perform.rb
635
+ - lib/terraspace/compiler/perform/skip.rb
630
636
  - lib/terraspace/compiler/select.rb
631
637
  - lib/terraspace/compiler/strategy/abstract_base.rb
632
638
  - lib/terraspace/compiler/strategy/mod.rb
@@ -649,6 +655,7 @@ files:
649
655
  - lib/terraspace/dependency/helper/output.rb
650
656
  - lib/terraspace/dependency/node.rb
651
657
  - lib/terraspace/dependency/registry.rb
658
+ - lib/terraspace/dependency/resolver.rb
652
659
  - lib/terraspace/dotenv.rb
653
660
  - lib/terraspace/ext.rb
654
661
  - lib/terraspace/ext/bundler.rb
@@ -697,6 +704,7 @@ files:
697
704
  - lib/terraspace/terraform/api/workspace.rb
698
705
  - lib/terraspace/terraform/args/custom.rb
699
706
  - lib/terraspace/terraform/args/dsl.rb
707
+ - lib/terraspace/terraform/args/expand.rb
700
708
  - lib/terraspace/terraform/args/pass.rb
701
709
  - lib/terraspace/terraform/args/shorthands.rb
702
710
  - lib/terraspace/terraform/args/thor.rb
@@ -858,7 +866,7 @@ files:
858
866
  - spec/terraspace/terraform/remote_state/marker/output_spec.rb
859
867
  - spec/terraspace/terraform/remote_state/output_proxy_spec.rb
860
868
  - terraspace.gemspec
861
- homepage: https://github.com/boltops-tools/terraspace
869
+ homepage: https://terraspace.cloud
862
870
  licenses:
863
871
  - Apache-2.0
864
872
  metadata: {}