Class: Moku::CLI

Inherits:
Object
  • Object
show all
Includes:
GLI::App
Defined in:
lib/moku/cli.rb

Overview

The command-line interface for moku

Instance Method Summary collapse

Constructor Details

#initializeCLI

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/MethodLength


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/moku/cli.rb', line 16

def initialize
  program_desc "A deployment tool"
  version Moku::VERSION
  synopsis_format :terminal
  subcommand_option_handling(:normal)
  arguments(:strict)

  accept(Hash) do |value|
    value.split(",").map do |pair|
      pair.split(":")
    end.to_h
  end

  desc "Show ouput from system commands"
  switch [:v, :verbose]

  desc "The user running the action"
  flag [:u, :user],
    default_value: ENV["USER"],
    arg_name: "USER",
    type: String

  pre do |global_options, _command, options, args|
    verbosity = global_options[:verbose] || options[:verbose]
    Moku.load_settings!(global_options.merge(verbose: verbosity))
    Moku.initialize!
    global_options[:instance_name] = args.shift
    @invoker = Moku.invoker
  end

  desc "Deploy a release"
  long_desc "Deploys the instance using the source described by the " \
    "default branch. If a reference is given, that will be deployed " \
    "instead. The reference can be a branch, tag, or SHA."
  arg "instance"
  arg "reference", :optional
  command :deploy do |c|
    c.switch [:v, :verbose]
    c.action do |global_options, _options, args|
      invoker.add_command(
        Command::Deploy.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          reference: args.first
        )
      )
    end
  end

  desc "Rollback to a previous release"
  long_desc "This command quickly rolls back to a previously deployed " \
    "release that is still cached on the host servers. You can view the " \
    "list of cached releases via the caches command. If a release id is " \
    "given, this will rollback to that release. Otherwise, it rolls back " \
    "to the most recent release."
  arg "instance"
  arg "release", :optional
  command :rollback do |c|
    c.switch [:v, :verbose]
    c.action do |global_options, _options, args|
      invoker.add_command(
        Command::Rollback.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          cache_id: args.first
        )
      )
    end
  end

  desc "View or set the default branch"
  arg "instance"
  arg "new_branch", :optional
  command :default_branch do |c|
    c.switch [:v, :verbose]
    c.action do |global_options, _options, args|
      command = if args.first
        Command::SetDefaultBranch.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          new_branch: args.first
        )
      else
        Command::ReadDefaultBranch.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user]
        )
      end
      invoker.add_command(command)
    end
  end

  desc "List release history"
  arg "instance"
  command :releases do |c|
    c.desc "Show full SHAs"
    c.switch [:l, :long]
    c.switch [:v, :verbose]
    c.action do |global_options, options, _args|
      invoker.add_command(
        Command::Releases.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          long: options[:long]
        )
      )
    end
  end

  desc "List cached releases"
  arg "instance"
  command :caches do |c|
    c.desc "Show full SHAs"
    c.switch [:l, :long]
    c.switch [:v, :verbose]
    c.action do |global_options, options, _args|
      invoker.add_command(
        Command::Caches.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          long: options[:long]
        )
      )
    end
  end

  desc "Setup a new instance"
  long_desc "Idempotently initialize a new instance with data from stdin"
  arg "instance"
  command :init do |c|
    c.desc "Include default steps to finish rails builds and releases"
    c.switch [:r, :rails], default_value: true, negateable: true
    c.desc "Read from the given file instead of stdin"
    c.flag [:f, :file], type: String
    c.switch [:v, :verbose]
    c.action do |global_options, options, _args|
      invoker.add_command(
        Command::Init.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          rails: options[:rails],
          json: options[:file] ? File.read(options[:file]) : STDIN.read
        )
      )
    end
  end

  desc "Determine if an instance is busy"
  arg "instance"
  command :available do |c|
    c.action do |global_options, _options, _args|
      begin
        Moku.instance_repo.lock!(global_options[:instance_name])
        Moku.logger.info("\nThe instance #{global_options[:instance_name]} is available")
      rescue InstanceBusyError
        raise GLI::CustomExit.new(
          "\nThe instance #{global_options[:instance_name]} is unavailable",
          27
        )
      end
    end
  end

  desc "Run an command on matching hosts"
  long_desc "Run an arbitrary command from the root of the deployed release, with the " \
    "release's environment. Use the flags to specify on which hosts to run. A default " \
    "host is set for each site and one site is designated as primary for the instance. " \
    "The behavior with no flags is to run on the default host at the primary site " \
    "(to simplify operations like database migrations, which will take effect instance-wide)."
  arg "instance"
  arg "cmd", [:multiple]
  command :exec do |c|
    c.example "exec myapp-mystage bundle exec rake db:migrate",
      desc: "Run database migrations on only one host:"
    c.example "exec myapp-mystage -v --host host1,host2,host3 'DEBUG=true bin/status'",
      desc: "Run bin/status on host1, host2, and host3, setting an environment variable " \
      "and printing the output:"
    c.flag [:site, :S], type: Array, desc: "Run on the default host at the specified site(s)"
    c.flag [:host, :H], type: Array, desc: "Run on each of the specified hosts"
    c.switch [:all, :A], negatable: false, desc: "Run on every host for the instance"
    c.switch [:"each-site", :Z], negatable: false, desc: "Run on the default host at every site"
    c.switch [:v, :verbose]
    c.action do |global_options, options, args|
      scope = if options[:site]
        Sites::Scope.site(*options[:site])
      elsif options[:host]
        Sites::Scope.host(*options[:host])
      elsif options[:"each-site"]
        Sites::Scope.each_site
      elsif options[:all]
        Sites::Scope.all
      else
        Sites::Scope.once
      end

      invoker.add_command(
        Command::Exec.new(
          instance_name: global_options[:instance_name],
          user: global_options[:user],
          cmd: args.join(" "),
          scope: scope
        )
      )
    end
  end
end