Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add completely install command #45

Merged
merged 5 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ AllCops:
Naming/AccessorMethodName:
Exclude:
- 'lib/completely/tester.rb'

# The `bounce` method here is more readable without this cop
Style/GuardClause:
Exclude:
- 'lib/completely/commands/install.rb'
6 changes: 4 additions & 2 deletions lib/completely/cli.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
require 'mister_bin'
require 'completely/version'
require 'completely/commands/generate'
require 'completely/commands/init'
require 'completely/commands/install'
require 'completely/commands/preview'
require 'completely/commands/generate'
require 'completely/commands/test'
require 'completely/version'

module Completely
class CLI
Expand All @@ -16,6 +17,7 @@ def self.runner
runner.route 'preview', to: Commands::Preview
runner.route 'generate', to: Commands::Generate
runner.route 'test', to: Commands::Test
runner.route 'install', to: Commands::Install

runner
end
Expand Down
96 changes: 96 additions & 0 deletions lib/completely/commands/install.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
require 'completely/commands/base'

module Completely
module Commands
class Install < Base
TARGETS = %W[
/usr/share/bash-completion/completions
/usr/local/etc/bash_completion.d
#{Dir.home}/.bash_completion.d
]

summary 'Install a bash completion script'

help <<~HELP
This command will copy the specified file to one of the following directories:

#{TARGETS.map { |c| " - #{c}" }.join "\n"}

The target filename will be the program name, and sudo will be used if necessary.
HELP

usage 'completely install PROGRAM [SCRIPT_PATH --force --dry]'
usage 'completely install (-h|--help)'

option '-f --force', 'Overwrite target file if it exists'
option '-d --dry', 'Show the installation command but do not run it'

param 'PROGRAM', 'Name of the program the completions are for.'
param 'SCRIPT_PATH', 'Path to the source bash script [default: completely.bash].'

def run
bounce

if args['--dry']
puts command.join ' '
return
end

success = system(*command)
raise "Failed running command:\nnb`#{command.join ' '}`" unless success

say "Saved m`#{target_path}`"
say 'You may need to restart your session to test it'
end

private

def bounce
unless completions_path
raise 'Cannot determine system completions directory'
end

unless File.exist? script_path
raise "Cannot find script: m`#{script_path}`"
end

if target_exist? && !args['--force']
raise "File exists: m`#{target_path}`\nUse nb`--force` to overwrite"
end
end

def target_exist?
File.exist? target_path
end

def command
result = root? ? [] : %w[sudo]
result + %W[cp #{script_path} #{target_path}]
end

def script_path
args['SCRIPT_PATH'] || 'completely.bash'
end

def target_path
"#{completions_path}/#{args['PROGRAM']}"
end

def root?
Process.uid.zero?
end

def completions_path
@completions_path ||= completions_path!
end

def completions_path!
TARGETS.each do |tarnet|
return tarnet if Dir.exist? tarnet
end

nil
end
end
end
end
1 change: 1 addition & 0 deletions spec/approvals/cli/commands
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ Commands:
preview Generate the bash completion script to STDOUT
generate Generate the bash completion script to a file
test Test completions
install Install a bash completion script

Run completely COMMAND --help for more information
1 change: 1 addition & 0 deletions spec/approvals/cli/install/dry
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sudo cp README.md /usr/share/bash-completion/completions/completely-test
31 changes: 31 additions & 0 deletions spec/approvals/cli/install/help
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Install a bash completion script

This command will copy the specified file to one of the following directories:

- /usr/share/bash-completion/completions
- /usr/local/etc/bash_completion.d
- /home/vagrant/.bash_completion.d

The target filename will be the program name, and sudo will be used if
necessary.

Usage:
completely install PROGRAM [SCRIPT_PATH --force --dry]
completely install (-h|--help)

Options:
-f --force
Overwrite target file if it exists

-d --dry
Show the installation command but do not run it

-h --help
Show this help

Parameters:
PROGRAM
Name of the program the completions are for.

SCRIPT_PATH
Path to the source bash script [default: completely.bash].
2 changes: 2 additions & 0 deletions spec/approvals/cli/install/install-default
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Saved /usr/share/bash-completion/completions/completely-test
You may need to restart your session to test it
2 changes: 2 additions & 0 deletions spec/approvals/cli/install/install-specified
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Saved /usr/share/bash-completion/completions/completely-test
You may need to restart your session to test it
1 change: 1 addition & 0 deletions spec/approvals/cli/install/missing-script
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#<RuntimeError: Cannot find script: m`completely.bash`>
3 changes: 3 additions & 0 deletions spec/approvals/cli/install/no-args
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Usage:
completely install PROGRAM [SCRIPT_PATH --force --dry]
completely install (-h|--help)
1 change: 1 addition & 0 deletions spec/approvals/cli/install/no-completion-targets
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#<RuntimeError: Cannot determine system completions directory>
2 changes: 2 additions & 0 deletions spec/approvals/cli/install/target-exists
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#<RuntimeError: File exists: m`/usr/share/bash-completion/completions/completely-test`
Use nb`--force` to overwrite>
92 changes: 92 additions & 0 deletions spec/completely/commands/install_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
require 'spec_helper'

describe Commands::Install do
subject { described_class.new }

context 'with --help' do
it 'shows long usage' do
expect { subject.execute %w[install --help] }
.to output_approval('cli/install/help').diff(10)
end
end

context 'without arguments' do
it 'shows short usage' do
expect { subject.execute %w[install] }
.to output_approval('cli/install/no-args')
end
end

context 'with only the program name argument' do
context 'when the default script is not found' do
it 'raises an error' do
expect { subject.execute %w[install completely-test] }
.to raise_approval('cli/install/missing-script').diff(8)
end
end

context 'when the default script is found' do
let(:expected_args) do
%w[
sudo
cp
completely.bash
/usr/share/bash-completion/completions/completely-test
]
end

before do
reset_tmp_dir
File.write 'spec/tmp/completely.bash', 'not-important'
end

it 'copies the script' do
Dir.chdir 'spec/tmp' do
allow(subject).to receive(:system).with(*expected_args).and_return true
expect { subject.execute %w[install completely-test] }
.to output_approval('cli/install/install-default')
end
end
end
end

context 'with the program name argument and a script argument' do
let(:expected_args) do
%w[
sudo
cp
README.md
/usr/share/bash-completion/completions/completely-test
]
end

it 'copies the script' do
allow(subject).to receive(:system).with(*expected_args).and_return true
expect { subject.execute %w[install completely-test README.md] }
.to output_approval('cli/install/install-specified')
end
end

context 'with --dry' do
it 'shows the command' do
expect { subject.execute %w[install completely-test README.md --dry] }
.to output_approval('cli/install/dry')
end
end

context 'when none of the target directories is found' do
it 'raises an error' do
allow(subject).to receive(:completions_path).and_return nil
expect { subject.execute %w[install completely-test README.md] }
.to raise_approval('cli/install/no-completion-targets').diff(8)
end
end

context 'when the target file exists' do
it 'raises an error' do
allow(subject).to receive(:target_exist?).and_return true
expect { subject.execute %w[install completely-test README.md] }
.to raise_approval('cli/install/target-exists').diff(8)
end
end
end