Class: ShopifyCli::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/shopify-cli/context.rb

Overview

Context captures a lot about the current running command. It captures the environment, output, system and file operations. It is useful to have the context especially in tests so that you have a single access point to these resoures.

Constant Summary collapse

GEM_LATEST_URI =
URI.parse('https://rubygems.org/api/v1/versions/shopify-cli/latest.json')
VERSION_CHECK_SECTION =
'versioncheck'
LAST_CHECKED_AT_FIELD =
'last_checked_at'
VERSION_CHECK_INTERVAL =
86400

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root: Dir.pwd, env: ($original_env || ENV).clone) ⇒ Context

:nodoc:



56
57
58
59
# File 'lib/shopify-cli/context.rb', line 56

def initialize(root: Dir.pwd, env: ($original_env || ENV).clone) # :nodoc:
  self.root = root
  self.env = env
end

Class Attribute Details

.messagesObject (readonly)

Returns the value of attribute messages.



22
23
24
# File 'lib/shopify-cli/context.rb', line 22

def messages
  @messages
end

Instance Attribute Details

#envObject

is an accessor for environment variables. These variables are also added to any command run by the context.



54
55
56
# File 'lib/shopify-cli/context.rb', line 54

def env
  @env
end

#rootObject

is the directory root that the current command is running in. If you want to simulate a cd for the file operations, you can change this variable.



51
52
53
# File 'lib/shopify-cli/context.rb', line 51

def root
  @root
end

Class Method Details

.load_messages(messages) ⇒ Object

adds a new set of messages to be used by the CLI. The messages are expected to be a hash of symbols, and multiple levels are allowed. When fetching messages a dot notation is used to separate different levels. See Context::message for more information.

#### Parameters

  • messages - Hash containing the new keys to register



30
31
32
33
34
35
# File 'lib/shopify-cli/context.rb', line 30

def load_messages(messages)
  @messages ||= {}
  @messages = @messages.merge(messages) do |key|
    Context.new.abort("Message key '#{key}' already exists and cannot be registered") if @messages.key?(key)
  end
end

.message(key, *params) ⇒ Object

returns the user-facing messages for the given key. Returns the key if no message is available.

#### Parameters

  • key - a symbol representing the message

  • params - the parameters to format the string with



42
43
44
45
46
# File 'lib/shopify-cli/context.rb', line 42

def message(key, *params)
  key_parts = key.split('.').map(&:to_sym)
  str = Context.messages.dig(*key_parts)
  str ? str % params : key
end

Instance Method Details

#abort(text) ⇒ Object

aborts the current running command and outputs an error message, prefixed by a red x

#### Parameters

  • text - a string message to output

Raises:



295
296
297
# File 'lib/shopify-cli/context.rb', line 295

def abort(text)
  raise ShopifyCli::Abort, "{{x}} #{text}"
end

#capture2(*args, **kwargs) ⇒ Object

Execute a command in the user’s environment This is meant to be largely equivalent to backticks, only with the env passed in. Captures the results of the command without output to the console

#### Parameters

  • ‘*args`: A splat of arguments evaluated as a command. (e.g. `’rm’, folder` is equivalent to ‘rm #folder`)

  • ‘**kwargs`: additional arguments to pass to Open3.capture2

#### Returns

  • output: output (STDOUT) of the command execution

  • status: boolean success status of the command execution

#### Usage

out, stat = @ctx.capture2('ls', 'a_folder')


358
359
360
# File 'lib/shopify-cli/context.rb', line 358

def capture2(*args, **kwargs)
  CLI::Kit::System.capture2(*args, env: @env, **kwargs)
end

#capture2e(*args, **kwargs) ⇒ Object

Execute a command in the user’s environment This is meant to be largely equivalent to backticks, only with the env passed in. Captures the results of the command without output to the console

#### Parameters

  • ‘*args`: A splat of arguments evaluated as a command. (e.g. `’rm’, folder` is equivalent to ‘rm #folder`)

  • ‘**kwargs`: additional arguments to pass to Open3.capture2e

#### Returns

  • output: output (STDOUT merged with STDERR) of the command execution

  • status: boolean success status of the command execution

#### Usage

out_and_err, stat = @ctx.capture2e('ls', 'a_folder')


378
379
380
# File 'lib/shopify-cli/context.rb', line 378

def capture2e(*args, **kwargs)
  CLI::Kit::System.capture2e(*args, env: @env, **kwargs)
end

#capture3(*args, **kwargs) ⇒ Object

Execute a command in the user’s environment This is meant to be largely equivalent to backticks, only with the env passed in. Captures the results of the command without output to the console

#### Parameters

  • ‘*args`: A splat of arguments evaluated as a command. (e.g. `’rm’, folder` is equivalent to ‘rm #folder`)

  • ‘**kwargs`: additional arguments to pass to Open3.capture3

#### Returns

  • output: STDOUT of the command execution

  • error: STDERR of the command execution

  • status: boolean success status of the command execution

#### Usage

out, err, stat = @ctx.capture3('ls', 'a_folder')


399
400
401
# File 'lib/shopify-cli/context.rb', line 399

def capture3(*args, **kwargs)
  CLI::Kit::System.capture3(*args, env: @env, **kwargs)
end

#chdir(path) ⇒ Object

will change directories and update the root, the filepath is relative to the command root unless absolute

#### Parameters

  • path - the file path to a directory, relative to the context root to remove from the FS



152
153
154
155
# File 'lib/shopify-cli/context.rb', line 152

def chdir(path)
  Dir.chdir(ctx_path(path))
  self.root = ctx_path(path)
end

#ci?Boolean

will return true if the cli is being tested on CI

Returns:

  • (Boolean)


105
106
107
# File 'lib/shopify-cli/context.rb', line 105

def ci?
  ENV['CI']
end

#cp(from, to) ⇒ Object

will copy a directory from the FS, the filepath is relative to the command root unless absolute

#### Parameters

  • from - the path of the original file

  • to - the destination path



193
194
195
# File 'lib/shopify-cli/context.rb', line 193

def cp(from, to)
  FileUtils.cp(ctx_path(from), ctx_path(to))
end

#cp_r(from, to) ⇒ Object

will recursively copy a directory from the FS, the filepath is relative to the command root unless absolute

#### Parameters

  • from - the path of the original file

  • to - the destination path



182
183
184
# File 'lib/shopify-cli/context.rb', line 182

def cp_r(from, to)
  FileUtils.cp_r(ctx_path(from), ctx_path(to))
end

#debug(text) ⇒ Object

outputs a message, prefixed by a red DEBUG tag. This will only output to the console if you have ‘DEBUG=1` set in your shell environment.

#### Parameters

  • text - a string message to output



305
306
307
# File 'lib/shopify-cli/context.rb', line 305

def debug(text)
  puts("{{red:DEBUG}} #{text}") if getenv('DEBUG')
end

#development?Boolean

will return true if the cli is running on your development instance.

Returns:

  • (Boolean)


94
95
96
# File 'lib/shopify-cli/context.rb', line 94

def development?
  !system? && !testing?
end

#dir_exist?(path) ⇒ Boolean

checks if a directory exists, the filepath is relative to the command root unless absolute

#### Parameters

  • path - the file path to a directory, relative to the context root to remove from the FS

Returns:

  • (Boolean)


162
163
164
# File 'lib/shopify-cli/context.rb', line 162

def dir_exist?(path)
  Dir.exist?(ctx_path(path))
end

#done(text) ⇒ Object

outputs a message, prefixed by a checkmark indicating that something completed

#### Parameters

  • text - a string message to output



285
286
287
# File 'lib/shopify-cli/context.rb', line 285

def done(text)
  puts("{{v}} #{text}")
end

#file_exist?(path) ⇒ Boolean

checks if a file exists, the filepath is relative to the command root unless absolute

#### Parameters

  • path - the file path to a file, relative to the context root to remove from the FS

Returns:

  • (Boolean)


171
172
173
# File 'lib/shopify-cli/context.rb', line 171

def file_exist?(path)
  File.exist?(ctx_path(path))
end

#getenv(name) ⇒ Object

get a environment variable value by name.

#### Parameters

  • name - the name of the environment variable that you want to fetch

#### Returns

  • value - will return the value, or nil if the variable does not exist



117
118
119
120
# File 'lib/shopify-cli/context.rb', line 117

def getenv(name)
  v = @env[name]
  v == '' ? nil : v
end

#linux?Boolean

will return true if the cli is running on a linux distro

Returns:

  • (Boolean)


75
76
77
# File 'lib/shopify-cli/context.rb', line 75

def linux?
  os == :linux
end

#mac?Boolean

will return true if the cli is running on an apple computer.

Returns:

  • (Boolean)


70
71
72
# File 'lib/shopify-cli/context.rb', line 70

def mac?
  os == :mac
end

#message(key, *params) ⇒ Object

proxy call to Context.message.

#### Parameters

  • key - a symbol representing the message

  • params - the parameters to format the string with



314
315
316
# File 'lib/shopify-cli/context.rb', line 314

def message(key, *params)
  Context.message(key, *params)
end

#mkdir_p(path) ⇒ Object

will create a directory, recursively if it does not exist. So if you create a directory foo/bar/dun, this will also create the directories foo and foo/bar if they do not exist. The path will be made relative to the command root unless absolute

#### Parameters

  • path - file path of the directory that you want to create



246
247
248
# File 'lib/shopify-cli/context.rb', line 246

def mkdir_p(path)
  FileUtils.mkdir_p(path)
end

#new_versionObject

Checks if there’s a newer version of the CLI available and returns version string if this should be conveyed to the user (i.e., if it’s been over 24 hours since last check)

#### Parameters

#### Returns

  • version: string of newer version available, IFF new version is available AND it’s time to inform user,

    : nil otherwise
    


465
466
467
468
469
470
471
# File 'lib/shopify-cli/context.rb', line 465

def new_version
  if (time_of_last_check + VERSION_CHECK_INTERVAL) < (now = Time.now.to_i)
    update_time_of_last_check(now)
    latest_version = retrieve_latest_gem_version
    latest_version unless latest_version == ShopifyCli::VERSION
  end
end

#on_siginfoObject

captures the info signal (ctrl-T) and provide a handler to it.

#### Example

@ctx.on_siginfo do
  @ctx.open_url!("http://google.com")
end


411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/shopify-cli/context.rb', line 411

def on_siginfo
  # Reset any previous SIGINFO handling we had so the only action we take is the given block
  trap('INFO', 'DEFAULT')

  fork do
    begin
      r, w = IO.pipe
      @signal = false
      trap('SIGINFO') do
        @signal = true
        w.write(0)
      end
      while r.read(1)
        next unless @signal
        @signal = false
        yield
      end
    rescue Interrupt
      exit(0)
    end
  end
end

#open_url!(uri) ⇒ Object

will output to the console a link for the user to either copy/paste or click on.

#### Parameters

  • uri - a http URI to open in a browser



256
257
258
259
# File 'lib/shopify-cli/context.rb', line 256

def open_url!(uri)
  help = message('core.context.open_url', uri)
  puts(help)
end

#osObject

will return which operating system that the cli is running on [:mac, :linux]



62
63
64
65
66
67
# File 'lib/shopify-cli/context.rb', line 62

def os
  host = uname
  return :mac if /darwin/.match(host)
  return :linux if /linux/.match(host)
  return :windows if /mingw32/.match(host)
end

will output a message, prefixed by a yellow star, indicating that task started.

#### Parameters

  • text - a string message to output



267
268
269
# File 'lib/shopify-cli/context.rb', line 267

def print_task(text)
  puts "{{yellow:*}} #{text}"
end

#puts(*args) ⇒ Object

a wrapper around Kernel.puts to allow for easy formatting

#### Parameters

  • text - a string message to output



276
277
278
# File 'lib/shopify-cli/context.rb', line 276

def puts(*args)
  Kernel.puts(CLI::UI.fmt(*args))
end

#rename(from, to) ⇒ Object

will rename a file from one place to another, relative to the command root unless the path is absolute.

#### Parameters

  • from - the path of the original file

  • to - the destination path



204
205
206
# File 'lib/shopify-cli/context.rb', line 204

def rename(from, to)
  File.rename(ctx_path(from), ctx_path(to))
end

#rm(fname) ⇒ Object

will remove a plain file from the FS, the filepath is relative to the command root unless absolute.

#### Parameters

  • fname - the file path relative to the context root to remove from the FS



214
215
216
# File 'lib/shopify-cli/context.rb', line 214

def rm(fname)
  FileUtils.rm(ctx_path(fname))
end

#rm_r(fname) ⇒ Object

will remove a directory from the FS, the filepath is relative to the command root unless absolute

#### Parameters

  • fname - the file path to a directory, relative to the context root to remove from the FS



224
225
226
# File 'lib/shopify-cli/context.rb', line 224

def rm_r(fname)
  FileUtils.rm_r(ctx_path(fname))
end

#rm_rf(fname) ⇒ Object

will force remove a directory from the FS, the filepath is relative to the command root unless absolute

#### Parameters

  • fname - the file path to a directory, relative to the context root to remove from the FS



234
235
236
# File 'lib/shopify-cli/context.rb', line 234

def rm_rf(fname)
  FileUtils.rm_rf(ctx_path(fname))
end

#setenv(key, value) ⇒ Object

set a environment variable value by name.

#### Parameters

  • key - the name of the environment variable that you want to set

  • value - the value of the variable



128
129
130
# File 'lib/shopify-cli/context.rb', line 128

def setenv(key, value)
  @env[key] = value
end

#system(*args, **kwargs) ⇒ Object

Execute a command in the user’s environment Outputs result of the command without capturing it

#### Parameters

  • ‘*args`: A splat of arguments evaluated as a command. (e.g. `’rm’, folder` is equivalent to ‘rm #folder`)

  • ‘**kwargs`: additional keyword arguments to pass to Process.spawn

#### Returns

  • status: boolean success status of the command execution

#### Usage

stat = @ctx.system('ls', 'a_folder')


338
339
340
# File 'lib/shopify-cli/context.rb', line 338

def system(*args, **kwargs)
  CLI::Kit::System.system(*args, env: @env, **kwargs)
end

#system?Boolean

will return true if the cli is being run from an installation, and not a development instance. The gem installation will not have a ‘test’ directory. See #development? for checking for development environment.

Returns:

  • (Boolean)


88
89
90
# File 'lib/shopify-cli/context.rb', line 88

def system?
  !Dir.exist?(File.join(ShopifyCli::ROOT, 'test'))
end

#testing?Boolean

will return true while tests are running, either locally or on CI

Returns:

  • (Boolean)


99
100
101
# File 'lib/shopify-cli/context.rb', line 99

def testing?
  ci? || ENV['TEST']
end

#unameObject

will grab the host info of the computer running the cli. This indicates the computer architecture and operating system



320
321
322
# File 'lib/shopify-cli/context.rb', line 320

def uname
  @uname ||= RbConfig::CONFIG["host"]
end

#which(cmd) ⇒ Object

TODO:

This is currently a duplicate of CLI::Kit::System.which() - we should make that method public when we make Kit changes and make this a wrapper instead.

Checks if the given command exists in the system

#### Parameters

  • cmd: The command to test

#### Returns The path of the executable if it is found



444
445
446
447
448
449
450
451
452
453
454
# File 'lib/shopify-cli/context.rb', line 444

def which(cmd)
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    exts.each do |ext|
      exe = File.join(File.expand_path(path), "#{cmd}#{ext}")
      return exe if File.executable?(exe) && !File.directory?(exe)
    end
  end

  nil
end

#windows?Boolean

will return true if the cli is running on Windows

Returns:

  • (Boolean)


80
81
82
# File 'lib/shopify-cli/context.rb', line 80

def windows?
  os == :windows
end

#write(fname, content) ⇒ Object

will write/overwrite a file with the provided contents, relative to the context root unless the file path is absolute.

#### Parameters

  • fname - filename of the file that you are writing, relative to root unless it is absolute.

  • content - the body contents of the file that you are writing

#### Example

@ctx.write('new.txt', 'hello world')


143
144
145
# File 'lib/shopify-cli/context.rb', line 143

def write(fname, content)
  File.write(ctx_path(fname), content)
end