terraspace 1.0.6 → 1.1.3

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -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 +34 -14
  7. data/lib/terraspace/app.rb +2 -1
  8. data/lib/terraspace/builder/children.rb +42 -0
  9. data/lib/terraspace/builder.rb +34 -32
  10. data/lib/terraspace/cli/all.rb +2 -0
  11. data/lib/terraspace/cli/build/placeholder.rb +3 -1
  12. data/lib/terraspace/cli/help/all/plan.md +11 -1
  13. data/lib/terraspace/cli/help/all/up.md +10 -0
  14. data/lib/terraspace/cli/help/check_setup.md +2 -2
  15. data/lib/terraspace/cli/help/plan.md +9 -1
  16. data/lib/terraspace/cli/help/up.md +9 -1
  17. data/lib/terraspace/cli/init.rb +1 -1
  18. data/lib/terraspace/cli/new/shim.rb +1 -1
  19. data/lib/terraspace/cli/setup/check.rb +7 -3
  20. data/lib/terraspace/cli.rb +3 -2
  21. data/lib/terraspace/command.rb +1 -1
  22. data/lib/terraspace/compiler/dirs_concern.rb +6 -1
  23. data/lib/terraspace/compiler/{builder → perform}/skip.rb +1 -1
  24. data/lib/terraspace/compiler/{builder.rb → perform.rb} +21 -20
  25. data/lib/terraspace/compiler/select.rb +2 -7
  26. data/lib/terraspace/compiler/strategy/mod/text_file.rb +14 -13
  27. data/lib/terraspace/compiler/strategy/tfvar/layer.rb +3 -2
  28. data/lib/terraspace/compiler/strategy/tfvar.rb +2 -1
  29. data/lib/terraspace/core.rb +1 -1
  30. data/lib/terraspace/dependency/resolver.rb +20 -0
  31. data/lib/terraspace/ext/core/module.rb +8 -1
  32. data/lib/terraspace/mod.rb +1 -0
  33. data/lib/terraspace/plugin/summary/interface.rb +1 -1
  34. data/lib/terraspace/terraform/args/expand.rb +25 -0
  35. data/lib/terraspace/terraform/args/thor.rb +14 -11
  36. data/lib/terraspace/terraform/ihooks/after/plan.rb +2 -2
  37. data/lib/terraspace/terraform/ihooks/base.rb +5 -0
  38. data/lib/terraspace/terraform/ihooks/before/plan.rb +3 -5
  39. data/lib/terraspace/terraform/remote_state/fetcher.rb +12 -5
  40. data/lib/terraspace/terraform/remote_state/marker/output.rb +1 -1
  41. data/lib/terraspace/version.rb +1 -1
  42. data/spec/terraspace/all/runner_spec.rb +1 -0
  43. data/spec/terraspace/compiler/erb/render_spec.rb +5 -1
  44. data/terraspace.gemspec +5 -1
  45. metadata +12 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57c0c4f1c1fce5c1dd8fcd7447f5bc5a82e17d66e5353f4d092d492c137021e5
4
- data.tar.gz: 81a4ba08410e898a1dd057625ca556392e746c4b48a3453874472c60424c3949
3
+ metadata.gz: '0898edc00c5c61938fca0f22e97cd6cc1786e94ddc31f089ce095a75b3543240'
4
+ data.tar.gz: 9ecef3964e431d2665bf98adacea090cf09a4db0aa91a70d8955a8301444857a
5
5
  SHA512:
6
- metadata.gz: e629795fd75ad23d343ec28390e76756650f11740c6b380ad3df87cb216f7a998acfea282ebb1a1399343c3db2b5abe452705bb82fd61e76a8cd1a2ca258300b
7
- data.tar.gz: 7104217cb56ee3ba2317d4d2abe0d4f760fe35de359903349a97bef4c2de3c92aaff3e0605789fd9e970f8a841a2203dedc9d740d9bddf127d2555a3f3818f30
6
+ metadata.gz: ef0e86ce8739f13f2d5dfe1aa9caa7cae4a31603e6f365c36ba3d3edab25e98b824babb2077f15a983bc8f9f65146c91cc4c89850717fefb88e9bd258e32aecb
7
+ data.tar.gz: 71313596f783a4a635bec2e3536e38bdc22d0d4983ca862e1c0f45eeeca898eaa1474da0d25db76b7b8af4c557deb85ee293fb29883f1a376343c16a081244ab
data/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
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.3] - 2022-02-17
7
+ - [#207](https://github.com/boltops-tools/terraspace/pull/207) dont fork when all.concurrency = 1
8
+
9
+ ## [1.1.2] - 2022-02-17
10
+ - [#200](https://github.com/boltops-tools/terraspace/pull/200) fix terraspace typos
11
+ - [#202](https://github.com/boltops-tools/terraspace/pull/202) Windows support: fix include_dir for windows
12
+ - [#203](https://github.com/boltops-tools/terraspace/pull/203) Fix ERB for windows
13
+ - [#204](https://github.com/boltops-tools/terraspace/pull/204) improve file check
14
+ - setup check terraform_bin windows support
15
+ - slight layering improvement strip trailing slash, helps with custom layering
16
+
17
+ ## [1.1.1] - 2022-02-02
18
+ - [#199](https://github.com/boltops-tools/terraspace/pull/199) build required dependent stacks as part of terraspace up
19
+
20
+ ## [1.1.0] - 2022-01-30
21
+ - [#196](https://github.com/boltops-tools/terraspace/pull/196) terraspace all: build modules in batches and only each specific stack
22
+ - [#197](https://github.com/boltops-tools/terraspace/pull/197) all plan --output and all up --plan options
23
+ - simplify starter config/app.rb
24
+
6
25
  ## [1.0.6] - 2022-01-24
7
26
  - [#195](https://github.com/boltops-tools/terraspace/pull/195) improve autodetection for plugin expander for backend like remote
8
27
 
@@ -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,42 +22,56 @@ 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
- pid = fork do
50
- run_terraspace(node.name)
44
+ if fork?
45
+ pid = fork do
46
+ deploy_stack(node)
47
+ end
48
+ @pids[pid] = node.name # store mod_name mapping
49
+ else
50
+ deploy_stack(node)
51
51
  end
52
- @pids[pid] = node.name # store mod_name mapping
53
52
  end
54
53
  end
54
+ return unless fork?
55
55
  wait_for_child_proccess
56
56
  summarize # also reports lower-level error info
57
57
  report_errors # reports finall errors and possibly exit
58
58
  end
59
59
 
60
+ def deploy_stack(node)
61
+ build_stack(node.name)
62
+ run_terraspace(node.name)
63
+ end
64
+
65
+ def build_modules
66
+ builder = Terraspace::Builder.new(@options.merge(mod: "placeholder", clean: true, quiet: true, include_stacks: :none))
67
+ builder.build(modules: true)
68
+ end
69
+
70
+ def build_stack(name)
71
+ builder = Terraspace::Builder.new(@options.merge(mod: name, clean: false, quiet: true, include_stacks: :root_only))
72
+ builder.build(modules: false)
73
+ end
74
+
60
75
  def wait_for_child_proccess
61
76
  @errors = [] # stores child processes pids that errored
62
77
  @pids.each do |pid, _|
@@ -70,7 +85,7 @@ module Terraspace::All
70
85
  @errors.each do |pid|
71
86
  mod_name = @pids[pid]
72
87
  terraspace_command = terraspace_command(mod_name)
73
- logger.error "Error running: #{terraspace_command}. Check logs and fix the error.".color(:red)
88
+ logger.error "Error running: #{terraspace_command}. Fix the error above or check logs for the error.".color(:red)
74
89
  end
75
90
  unless @errors.empty?
76
91
  exit 2 if exit_on_fail?
@@ -97,12 +112,13 @@ module Terraspace::All
97
112
  log_path: log_path(mod_name),
98
113
  terraspace_command: terraspace_command(mod_name),
99
114
  }
100
- Summary.new(data).run
115
+ # Its possible for log file to not get created if RemoteState::Fetcher#validate! fails
116
+ Summary.new(data).run if File.exist?(data[:log_path])
101
117
  end
102
118
  end
103
119
 
104
120
  def run_terraspace(mod_name)
105
- set_log_path!(mod_name)
121
+ set_log_path!(mod_name) if fork?
106
122
  name = command_map(@command)
107
123
  o = @options.merge(mod: mod_name, yes: true, build: false, input: false, log_to_stderr: true)
108
124
  o.merge!(quiet: false) if @command == "init" # noisy so can filter and summarize output
@@ -116,6 +132,10 @@ module Terraspace::All
116
132
  end
117
133
  end
118
134
 
135
+ def fork?
136
+ Terraspace.config.all.concurrency > 1
137
+ end
138
+
119
139
  def set_log_path!(mod_name)
120
140
  command = terraspace_command(mod_name)
121
141
  path = log_path(mod_name)
@@ -16,7 +16,8 @@ module Terraspace
16
16
  config.all = ActiveSupport::OrderedOptions.new
17
17
  config.all.concurrency = 5
18
18
  config.all.exit_on_fail = ActiveSupport::OrderedOptions.new
19
- config.all.exit_on_fail.down = true
19
+ config.all.exit_on_fail.down = false
20
+ config.all.exit_on_fail.plan = true
20
21
  config.all.exit_on_fail.up = true
21
22
 
22
23
  config.allow = ActiveSupport::OrderedOptions.new
@@ -0,0 +1,42 @@
1
+ class Terraspace::Builder
2
+ class Children
3
+ include Terraspace::Util::Logging
4
+
5
+ def initialize(mod, options={})
6
+ @mod, @options = mod, options
7
+ @queue = []
8
+ end
9
+
10
+ def build
11
+ dependencies = Terraspace::Dependency::Registry.data
12
+ # Find out if current deploy stack contains dependency
13
+ found = dependencies.find do |parent_child|
14
+ parent, _ = parent_child.split(':')
15
+ parent == @mod.name
16
+ end
17
+ return unless found
18
+
19
+ # Go down graph children, which are the dependencies to build a queue
20
+ parent, _ = found.split(':')
21
+ node = Terraspace::Dependency::Node.find_by(name: parent)
22
+ build_queue(node)
23
+
24
+ logger.debug "Terraspace::Builder::Children @queue #{@queue}"
25
+
26
+ # Process queue in reverse order to build leaf nodes first
27
+ @queue.reverse.each do |node|
28
+ mod = Terraspace::Mod.new(node.name, @options)
29
+ Terraspace::Compiler::Perform.new(mod).compile
30
+ end
31
+ end
32
+
33
+ # Use depth first traversal to build queue
34
+ def build_queue(node)
35
+ node.children.each do |child|
36
+ @queue << child
37
+ build_queue(child)
38
+ end
39
+ @queue
40
+ end
41
+ end
42
+ end
@@ -4,64 +4,66 @@ module Terraspace
4
4
  include Compiler::DirsConcern
5
5
  include Hooks::Concern
6
6
 
7
- attr_reader :graph
7
+ # @include_stacks can be 3 values: root_with_children, none, root_only
8
+ #
9
+ # none: dont build any stacks at all. used by `terraspace all up`
10
+ # root_only: only build root stack. used by `terraspace all up`
11
+ # root_with_children: build all parent stacks as well as the root stack. normal `terraspace up`
12
+ #
13
+ def initialize(options={})
14
+ super
15
+ @include_stacks = @options[:include_stacks] || :root_with_children
16
+ end
8
17
 
9
18
  def run
10
19
  return if @options[:build] == false
11
20
  Terraspace::CLI::Setup::Check.check!
21
+ check_allow!
12
22
  @mod.root_module = true
13
23
  clean
24
+ resolve_dependencies if @include_stacks == :root_with_children
25
+ build
26
+ end
27
+
28
+ def resolve_dependencies
29
+ resolver = Terraspace::Dependency::Resolver.new(@options.merge(quiet: true))
30
+ resolver.resolve # returns batches
31
+ end
32
+
33
+ def build(modules: true)
14
34
  build_dir = Util.pretty_path(@mod.cache_dir)
15
35
  placeholder_stack_message
16
36
  logger.info "Building #{build_dir}" unless @options[:quiet] # from terraspace all
17
-
18
- batches = nil
19
37
  FileUtils.mkdir_p(@mod.cache_dir) # so terraspace before build hooks work
20
38
  run_hooks("terraspace.rb", "build") do
21
- check_allow!
22
- build_unresolved
23
- batches = build_batches
24
- build_all
39
+ build_dir("modules") if modules
40
+ build_stacks
25
41
  logger.info "Built in #{build_dir}" unless @options[:quiet] # from terraspace all
26
42
  end
27
- batches
28
43
  end
29
44
 
30
45
  def check_allow!
31
46
  Allow.new(@mod).check!
32
47
  end
33
48
 
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
49
+ def build_stacks
50
+ return if @include_stacks == :none
51
+ build_children_stacks if @include_stacks == :root_with_children
52
+ Compiler::Perform.new(@mod).compile # @include_stacks :root or :root_with_children
52
53
  end
53
54
 
54
- def build_root_module
55
- @mod.resolved = @resolved
56
- Compiler::Builder.new(@mod).build
55
+ # Build stacks that are part of the dependency graph. Because .terraspace-cache folders
56
+ # need to exist so `terraform state pull` works to get the state info.
57
+ def build_children_stacks
58
+ children = Children.new(@mod, @options)
59
+ children.build
57
60
  end
58
61
 
59
62
  def build_dir(type_dir)
60
63
  with_each_mod(type_dir) do |mod|
61
- mod.resolved = @resolved
62
64
  is_root_module = mod.cache_dir == @mod.cache_dir
63
- next if is_root_module # handled by build_root_module
64
- Compiler::Builder.new(mod).build
65
+ next if is_root_module # handled by build_stacks
66
+ Compiler::Perform.new(mod).compile
65
67
  end
66
68
  end
67
69
 
@@ -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"
@@ -1,9 +1,9 @@
1
1
  ## Example
2
2
 
3
3
  $ terraspace check_setup
4
- Detected Terrspace version: 0.3.3
4
+ Detected Terraspace version: 0.3.3
5
5
  Detected Terraform bin: /home/ec2-user/.tfenv/bin/terraform
6
6
  Detected Terraform v0.13.2
7
7
  Terraspace requires Terraform v0.12.x and above
8
8
  You're all set!
9
- $
9
+ $
@@ -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?
@@ -50,7 +50,7 @@ class Terraspace::CLI::New
50
50
 
51
51
  private
52
52
  def switch_ruby_version_line
53
- rbenv_installed = system("type rbenv 2>&1 > /dev/null")
53
+ rbenv_installed = system("type rbenv > /dev/null 2>&1")
54
54
  if rbenv_installed
55
55
  'eval "$(rbenv init -)"'
56
56
  end
@@ -11,7 +11,7 @@ class Terraspace::CLI::Setup
11
11
 
12
12
  # Used for the CLI
13
13
  def run
14
- puts "Detected Terrspace version: #{Terraspace::VERSION}"
14
+ puts "Detected Terraspace version: #{Terraspace::VERSION}"
15
15
  if terraform_bin
16
16
  puts "Detected Terraform bin: #{terraform_bin}"
17
17
  puts "Detected #{terraform_version_message}"
@@ -65,8 +65,12 @@ class Terraspace::CLI::Setup
65
65
  end
66
66
 
67
67
  def terraform_bin
68
- out = `type terraform 2>&1`.strip
69
- return unless $?.success?
68
+ if Gem.win_platform?
69
+ out = "is terraform.exe".strip
70
+ else
71
+ out = `type terraform 2>&1`.strip
72
+ return unless $?.success?
73
+ end
70
74
  md = out.match(/is (.*)/)
71
75
  md[1] if md
72
76
  end
@@ -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
@@ -30,7 +30,7 @@ module Terraspace
30
30
  include Terraspace::Util::Logging
31
31
 
32
32
  def dispatch(m, args, options, config)
33
- # Terraspace.argv provides consistency when terraspace is being called by rspec-terrspace test harness
33
+ # Terraspace.argv provides consistency when terraspace is being called by rspec-terraspace test harness
34
34
  Terraspace.argv = args.clone # important to clone since Thor removes the first argv
35
35
 
36
36
  check_standalone_install!
@@ -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
@@ -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
@@ -3,9 +3,8 @@ module Terraspace::Compiler
3
3
  include Terraspace::App::CallableOption::Concern
4
4
  include Terraspace::Util::Logging
5
5
 
6
- def initialize(path)
7
- @path = path
8
- @stack_name = extract_stack_name(path)
6
+ def initialize(stack_name)
7
+ @stack_name = stack_name
9
8
  end
10
9
 
11
10
  def selected?
@@ -61,10 +60,6 @@ module Terraspace::Compiler
61
60
  Terraspace.config
62
61
  end
63
62
 
64
- def extract_stack_name(path)
65
- path.sub(%r{.*(app|vendor)/stacks/}, '')
66
- end
67
-
68
63
  @@ignore_stacks_deprecation_warning = nil
69
64
  def ignore_stacks_deprecation_warning
70
65
  return unless config.all.ignore_stacks
@@ -8,28 +8,29 @@ class Terraspace::Compiler::Strategy::Mod
8
8
  @filename = filename
9
9
  end
10
10
 
11
- @@already_reported = false
12
11
  def check
13
- unless file_installed?
14
- return true if @@already_reported
15
- logger.warn <<~EOL.color(:yellow)
16
- WARN: The command 'file' is not installed.
17
- Unable to check if files are text or binary files as a part of the Terraspace compile processing.
18
- Assuming all files are not binary file.
12
+ return true if Gem.win_platform? # assume text file if on windows
13
+ return true unless file_installed?
19
14
 
20
- Please install the file command to remove this warning message.
21
- EOL
22
- @@already_reported = true
23
- return true
24
- end
25
15
  # Thanks: https://stackoverflow.com/questions/2355866/ruby-how-to-determine-if-file-being-read-is-binary-or-text
26
16
  file_type, status = Open3.capture2e("file", @filename)
27
17
  status.success? && file_type.include?("text")
28
18
  end
29
19
 
30
20
  private
21
+ @@has_file = nil
31
22
  def file_installed?
32
- system("type file > /dev/null 2>&1")
23
+ return @@has_file unless @@has_file.nil?
24
+ @@has_file = system("type file > /dev/null 2>&1")
25
+ unless @@has_file
26
+ logger.warn <<~EOL.color(:yellow)
27
+ WARN: The command 'file' is not installed.
28
+ Unable to check if files are text or binary files as a part of the Terraspace compile processing.
29
+ Assuming all files are not binary file.
30
+ Please install the file command to remove this warning message.
31
+ EOL
32
+ end
33
+ @@has_file
33
34
  end
34
35
  end
35
36
  end
@@ -58,8 +58,9 @@ class Terraspace::Compiler::Strategy::Tfvar
58
58
  end
59
59
 
60
60
  def full_layering
61
- # layers is defined in Terraspace::Layering module
62
- layers.inject([]) do |sum, layer|
61
+ # layers defined in Terraspace::Layering module
62
+ all = layers.map { |layer| layer.sub(/\/$/,'') } # strip trailing slash
63
+ all.inject([]) do |sum, layer|
63
64
  sum += layer_levels(layer) unless layer.nil?
64
65
  sum
65
66
  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)
@@ -59,7 +59,7 @@ module Terraspace
59
59
  end
60
60
  end
61
61
 
62
- # Terraspace.argv provides consistency when terraspace is being called by rspec-terrspace test harness
62
+ # Terraspace.argv provides consistency when terraspace is being called by rspec-terraspace test harness
63
63
  # So use Terraspace.argv instead of ARGV constant
64
64
  def argv=(argv)
65
65
  @@argv = argv
@@ -0,0 +1,20 @@
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
+ mod.resolved = false
12
+ Terraspace::Compiler::Perform.new(mod).compile_tfvars(write: false)
13
+ end
14
+
15
+ dependencies = Terraspace::Dependency::Registry.data # populated dependencies resolved
16
+ @graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
17
+ @graph.build # Returns batches to run
18
+ end
19
+ end
20
+ end
@@ -5,8 +5,15 @@ class Module
5
5
  # include Provider
6
6
  # # etc
7
7
  #
8
+ # Caller lines are different for OSes:
9
+ #
10
+ # windows: "C:/Ruby31-x64/lib/ruby/gems/3.1.0/gems/terraspace-1.1.1/lib/terraspace/builder.rb:34:in `build'"
11
+ # linux: "/home/ec2-user/.rvm/gems/ruby-3.0.3/gems/terraspace-1.1.1//lib/terraspace/compiler/dsl/syntax/mod.rb:4:in `<module:Mod>'"
12
+ #
8
13
  def include_dir(dir)
9
- calling_file = caller[0].split(':').first # IE: /home/ec2-user/environment/terraspace/lib/terraspace/compiler/dsl/syntax/mod.rb
14
+ caller_line = caller[0]
15
+ parts = caller_line.split(':')
16
+ calling_file = caller_line.match(/^[a-zA-Z]:/) ? parts[1] : parts[0]
10
17
  parent_dir = File.dirname(calling_file)
11
18
 
12
19
  full_dir = "#{parent_dir}/#{dir}"
@@ -15,6 +15,7 @@ module Terraspace
15
15
  @name, @options = placeholder(name), options
16
16
  @consider_stacks = options[:consider_stacks].nil? ? true : options[:consider_stacks]
17
17
  @instance = options[:instance]
18
+ @resolved = true # more common case
18
19
  end
19
20
 
20
21
  def placeholder(name)
@@ -49,7 +49,7 @@ module Terraspace::Plugin::Summary
49
49
  def download_statefiles
50
50
  return unless download?
51
51
  FileUtils.rm_rf(@dest_folder)
52
- logger.info("Downloading statefiles to #{@dest_folder}")
52
+ logger.debug("Downloading statefiles to #{@dest_folder}")
53
53
  download # INTERFACE METHOD
54
54
  end
55
55
 
@@ -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={})
@@ -47,7 +48,7 @@ module Terraspace::Terraform::RemoteState
47
48
  def output_error(type)
48
49
  msg = case type
49
50
  when :key_not_found
50
- "Output #{@output_key} was not found for the #{@parent.name} tfvars file. Either #{@child.name} stack has not been deployed yet or it does not have this output: #{@output_key}"
51
+ "Output #{@output_key} was not found for the #{@parent.name} tfvars file. Either #{@child.name} stack has not been deployed yet or it does not have this output: #{@output_key}. Also, if local backend is being used and has been removed/cleaned, then it will also result zero-byte state.json with the 'terraform state pull' used to download the terraform state and output will not be found."
51
52
  when :state_not_found
52
53
  "Output #{@output_key} could not be looked up for the #{@parent.name} tfvars file. #{@child.name} stack needs to be deployed"
53
54
  when :bucket_not_found
@@ -62,7 +63,7 @@ module Terraspace::Terraform::RemoteState
62
63
  @@download_shown = false
63
64
  def pull
64
65
  return if @@pull_successes[cache_key]
65
- logger.info "Downloading tfstate files for dependencies defined in tfvars..." unless @@download_shown || @options[:quiet]
66
+ logger.debug "Downloading tfstate files for dependencies defined in tfvars..." unless @@download_shown || @options[:quiet]
66
67
  @@download_shown = true
67
68
  logger.debug "Downloading tfstate for stack: #{@child.name}"
68
69
 
@@ -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.6"
2
+ VERSION = "1.1.3"
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
@@ -1,6 +1,10 @@
1
1
  describe Terraspace::Compiler::Erb::Render do
2
2
  let(:render) { described_class.new(mod, src_path) }
3
- let(:mod) { Terraspace::Mod.new("a1") }
3
+ let(:mod) do
4
+ mod = Terraspace::Mod.new("a1")
5
+ mod.resolved = false
6
+ mod
7
+ end
4
8
 
5
9
  # Only testing mod unresolved as a sanity check and its worth the ROI.
6
10
  # The resolved would the Fetcher. We have unit tests to cover those other changes.
data/terraspace.gemspec CHANGED
@@ -9,9 +9,13 @@ 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
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/boltops-tools/terraspace"
17
+ spec.metadata["changelog_uri"] = "https://github.com/boltops-tools/terraspace/blob/master/CHANGELOG.md"
18
+
15
19
  spec.files = File.directory?('.git') ? `git ls-files`.split($/) : Dir.glob("**/*")
16
20
  spec.bindir = "exe"
17
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
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.6
4
+ version: 1.1.3
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-24 00:00:00.000000000 Z
11
+ date: 2022-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -490,6 +490,7 @@ files:
490
490
  - lib/terraspace/builder/allow/env.rb
491
491
  - lib/terraspace/builder/allow/region.rb
492
492
  - lib/terraspace/builder/allow/stack.rb
493
+ - lib/terraspace/builder/children.rb
493
494
  - lib/terraspace/bundle.rb
494
495
  - lib/terraspace/cli.rb
495
496
  - lib/terraspace/cli/all.rb
@@ -602,8 +603,6 @@ files:
602
603
  - lib/terraspace/cli/up.rb
603
604
  - lib/terraspace/command.rb
604
605
  - lib/terraspace/compiler/basename.rb
605
- - lib/terraspace/compiler/builder.rb
606
- - lib/terraspace/compiler/builder/skip.rb
607
606
  - lib/terraspace/compiler/cleaner.rb
608
607
  - lib/terraspace/compiler/cleaner/backend_change.rb
609
608
  - lib/terraspace/compiler/commands_concern.rb
@@ -633,6 +632,8 @@ files:
633
632
  - lib/terraspace/compiler/expander.rb
634
633
  - lib/terraspace/compiler/expander/backend.rb
635
634
  - lib/terraspace/compiler/helper_extender.rb
635
+ - lib/terraspace/compiler/perform.rb
636
+ - lib/terraspace/compiler/perform/skip.rb
636
637
  - lib/terraspace/compiler/select.rb
637
638
  - lib/terraspace/compiler/strategy/abstract_base.rb
638
639
  - lib/terraspace/compiler/strategy/mod.rb
@@ -655,6 +656,7 @@ files:
655
656
  - lib/terraspace/dependency/helper/output.rb
656
657
  - lib/terraspace/dependency/node.rb
657
658
  - lib/terraspace/dependency/registry.rb
659
+ - lib/terraspace/dependency/resolver.rb
658
660
  - lib/terraspace/dotenv.rb
659
661
  - lib/terraspace/ext.rb
660
662
  - lib/terraspace/ext/bundler.rb
@@ -703,6 +705,7 @@ files:
703
705
  - lib/terraspace/terraform/api/workspace.rb
704
706
  - lib/terraspace/terraform/args/custom.rb
705
707
  - lib/terraspace/terraform/args/dsl.rb
708
+ - lib/terraspace/terraform/args/expand.rb
706
709
  - lib/terraspace/terraform/args/pass.rb
707
710
  - lib/terraspace/terraform/args/shorthands.rb
708
711
  - lib/terraspace/terraform/args/thor.rb
@@ -864,10 +867,13 @@ files:
864
867
  - spec/terraspace/terraform/remote_state/marker/output_spec.rb
865
868
  - spec/terraspace/terraform/remote_state/output_proxy_spec.rb
866
869
  - terraspace.gemspec
867
- homepage: https://github.com/boltops-tools/terraspace
870
+ homepage: https://terraspace.cloud
868
871
  licenses:
869
872
  - Apache-2.0
870
- metadata: {}
873
+ metadata:
874
+ homepage_uri: https://terraspace.cloud
875
+ source_code_uri: https://github.com/boltops-tools/terraspace
876
+ changelog_uri: https://github.com/boltops-tools/terraspace/blob/master/CHANGELOG.md
871
877
  post_install_message:
872
878
  rdoc_options: []
873
879
  require_paths: