terraspace 1.0.3 → 1.1.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.
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: {}