Class: RuboCop::Cop::Chef::Modernize::ExecuteArchiveExtract

Inherits:
Base
  • Object
show all
Extended by:
TargetChefVersion
Includes:
RuboCop::Chef::CookbookHelpers
Defined in:
lib/rubocop/cop/chef/modernize/execute_archive_extract.rb

Overview

Use the archive_file resource built into Chef Infra Client 15+ instead of shelling out to tar or unzip. The resource extracts tar, tar.gz, tar.bz2, tar.xz, and zip archives, and it only runs when the archive is newer than what was already extracted, where the shelled out command re-extracts on every converge.

Examples:


# bad
execute 'extract nginx' do
  command "tar xzf #{Chef::Config[:file_cache_path]}/nginx.tar.gz -C /opt/nginx"
end

execute 'unzip -o /tmp/app.zip -d /opt/app'

# good
archive_file 'extract nginx' do
  path "#{Chef::Config[:file_cache_path]}/nginx.tar.gz"
  destination '/opt/nginx'
end

Constant Summary collapse

MSG =
'Use the archive_file resource built into Chef Infra Client 15+ instead of shelling out to tar or unzip'
RESTRICT_ON_SEND =
[:execute].freeze
EXTRACT_COMMAND =

tar extracts when its short flags include an x, or with the --extract long option. Anchoring the flag cluster directly after tar keeps tar czf (create), tar tzf (list), and tar --exclude=.git -czf from matching, since none of those put an x in the first cluster.

%r{
  \A\s*(?:\S*/)?(?:
    tar\s+(?:-)?[a-z]*x[a-z]*(?:\s|\z) |  # tar xzf / tar -xzf / tar zxvf
    tar\s+--extract\b                  |  # tar --extract --file
    unzip(?:\s|\z)                        # unzip -o foo.zip -d /opt
  )
}xi.freeze
SCRIPT_RESOURCES =

the shell resources whose script lives in a code property

%i(bash sh csh ksh zsh script).freeze
SHELL_OPERATORS =

A command that chains, pipes, or substitutes is doing more than extracting, so archive_file cannot replace it wholesale and the suggestion would be wrong.

['&&', '||', ';', '|', '`', '$(', "\n"].freeze

Instance Method Summary collapse

Methods included from TargetChefVersion

minimum_target_chef_version, required_minimum_chef_version, support_target_chef_version?

Methods included from RuboCop::Chef::CookbookHelpers

#each_action_symbol, #match_property_in_resource?, #match_resource_type?, #method_arg_ast_to_string, #resource_block_name_if_string

Methods inherited from Base

#target_chef_version

Instance Method Details

#on_block(node) ⇒ Object



82
83
84
85
86
87
88
89
90
# File 'lib/rubocop/cop/chef/modernize/execute_archive_extract.rb', line 82

def on_block(node)
  match_property_in_resource?(:execute, 'command', node) do |property|
    report_property(node, property)
  end

  match_property_in_resource?(SCRIPT_RESOURCES, 'code', node) do |property|
    report_property(node, property)
  end
end

#on_send(node) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rubocop/cop/chef/modernize/execute_archive_extract.rb', line 70

def on_send(node)
  command = node.arguments.first
  return unless command

  # Match on source rather than .value so an interpolated name such as
  # execute "tar xzf #{node['foo']['tarball']}" is caught too -- that is a
  # dstr node whose .value does not exist.
  return unless %i(str dstr).include?(command.type)

  add_offense(node, severity: :refactor) if extracts_archive?(unquote(command.source))
end