scripto 0.0.4 → 1.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +22 -0
- data/.gitignore +0 -18
- data/.rubocop.yml +15 -0
- data/.vscode/extensions.json +3 -0
- data/.vscode/settings.json +3 -0
- data/Gemfile +10 -2
- data/Gemfile.lock +97 -0
- data/README.md +22 -14
- data/Rakefile +3 -19
- data/justfile +54 -0
- data/lib/scripto/csv_commands.rb +18 -24
- data/lib/scripto/file_commands.rb +5 -5
- data/lib/scripto/log_commands.rb +112 -0
- data/lib/scripto/main.rb +1 -4
- data/lib/scripto/misc_commands.rb +5 -9
- data/lib/scripto/run_commands.rb +13 -15
- data/lib/scripto/version.rb +1 -1
- data/lib/scripto.rb +8 -8
- data/logo.svg +12 -0
- data/mise.toml +2 -0
- data/scripto.gemspec +18 -20
- metadata +22 -60
- data/.travis.yml +0 -9
- data/Vagrantfile +0 -16
- data/lib/scripto/print_commands.rb +0 -54
- data/test/helper.rb +0 -26
- data/test/test_csv.rb +0 -70
- data/test/test_file.rb +0 -220
- data/test/test_misc.rb +0 -53
- data/test/test_print.rb +0 -28
- data/test/test_run.rb +0 -103
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7cdd03bc1a558ff5c9f151d972205bde0816ad6feb89de992ae141bd637ebc54
|
|
4
|
+
data.tar.gz: b5c45a7c274391597e1c6dcd2e91579cdb09cc028bda6179a1762f010d005a73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a2b6883573f231fe24d7c6566d62510e95d4672842f0c580fb29e2634877e3020db88fe71b9d683e571ca23a2ef4e1479a0f0cf2de9f52d3d1faab0137d88001
|
|
7
|
+
data.tar.gz: 5934be60e97a589d0099a136027ee3888a6d7c3d7a7f3589df70fe76a525f5286ee3aad2812a0e373f6b7248371aabece0d7c68c2256329ca3eb7e092709b934
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
paths-ignore:
|
|
6
|
+
- "**.md"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
ruby-version: [3.4]
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
- uses: taiki-e/install-action@just
|
|
18
|
+
- uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
22
|
+
- run: just check
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require:
|
|
2
|
+
- standard
|
|
3
|
+
|
|
4
|
+
inherit_gem:
|
|
5
|
+
standard: config/base.yml
|
|
6
|
+
|
|
7
|
+
AllCops:
|
|
8
|
+
Exclude:
|
|
9
|
+
- vendor/**/* # for ci - https://github.com/rubocop/rubocop/issues/9832
|
|
10
|
+
NewCops: enable
|
|
11
|
+
SuggestExtensions: false
|
|
12
|
+
TargetRubyVersion: 3.1
|
|
13
|
+
|
|
14
|
+
# tweaks
|
|
15
|
+
Style/HashSyntax: { EnforcedShorthandSyntax: always }
|
data/Gemfile
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
source
|
|
1
|
+
source "https://rubygems.org"
|
|
2
2
|
|
|
3
|
-
# Specify your gem's dependencies in scripto.gemspec
|
|
4
3
|
gemspec
|
|
4
|
+
|
|
5
|
+
group :development, :test do
|
|
6
|
+
gem "minitest", "~> 5.0"
|
|
7
|
+
gem "mocha"
|
|
8
|
+
gem "pry", require: false
|
|
9
|
+
gem "rake", require: false
|
|
10
|
+
gem "ruby-lsp", require: false
|
|
11
|
+
gem "standard", require: false
|
|
12
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
scripto (1.0.0)
|
|
5
|
+
csv (~> 3.3)
|
|
6
|
+
ostruct (~> 0.0)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
ast (2.4.3)
|
|
12
|
+
coderay (1.1.3)
|
|
13
|
+
csv (3.3.5)
|
|
14
|
+
io-console (0.8.2)
|
|
15
|
+
json (2.18.0)
|
|
16
|
+
language_server-protocol (3.17.0.5)
|
|
17
|
+
lint_roller (1.1.0)
|
|
18
|
+
logger (1.7.0)
|
|
19
|
+
method_source (1.1.0)
|
|
20
|
+
minitest (5.27.0)
|
|
21
|
+
mocha (3.0.1)
|
|
22
|
+
ruby2_keywords (>= 0.0.5)
|
|
23
|
+
ostruct (0.6.3)
|
|
24
|
+
parallel (1.27.0)
|
|
25
|
+
parser (3.3.10.1)
|
|
26
|
+
ast (~> 2.4.1)
|
|
27
|
+
racc
|
|
28
|
+
prism (1.9.0)
|
|
29
|
+
pry (0.16.0)
|
|
30
|
+
coderay (~> 1.1)
|
|
31
|
+
method_source (~> 1.0)
|
|
32
|
+
reline (>= 0.6.0)
|
|
33
|
+
racc (1.8.1)
|
|
34
|
+
rainbow (3.1.1)
|
|
35
|
+
rake (13.3.1)
|
|
36
|
+
rbs (3.10.2)
|
|
37
|
+
logger
|
|
38
|
+
regexp_parser (2.11.3)
|
|
39
|
+
reline (0.6.3)
|
|
40
|
+
io-console (~> 0.5)
|
|
41
|
+
rubocop (1.82.1)
|
|
42
|
+
json (~> 2.3)
|
|
43
|
+
language_server-protocol (~> 3.17.0.2)
|
|
44
|
+
lint_roller (~> 1.1.0)
|
|
45
|
+
parallel (~> 1.10)
|
|
46
|
+
parser (>= 3.3.0.2)
|
|
47
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
48
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
49
|
+
rubocop-ast (>= 1.48.0, < 2.0)
|
|
50
|
+
ruby-progressbar (~> 1.7)
|
|
51
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
52
|
+
rubocop-ast (1.49.0)
|
|
53
|
+
parser (>= 3.3.7.2)
|
|
54
|
+
prism (~> 1.7)
|
|
55
|
+
rubocop-performance (1.26.1)
|
|
56
|
+
lint_roller (~> 1.1)
|
|
57
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
58
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
59
|
+
ruby-lsp (0.26.5)
|
|
60
|
+
language_server-protocol (~> 3.17.0)
|
|
61
|
+
prism (>= 1.2, < 2.0)
|
|
62
|
+
rbs (>= 3, < 5)
|
|
63
|
+
ruby-progressbar (1.13.0)
|
|
64
|
+
ruby2_keywords (0.0.5)
|
|
65
|
+
standard (1.53.0)
|
|
66
|
+
language_server-protocol (~> 3.17.0.2)
|
|
67
|
+
lint_roller (~> 1.0)
|
|
68
|
+
rubocop (~> 1.82.0)
|
|
69
|
+
standard-custom (~> 1.0.0)
|
|
70
|
+
standard-performance (~> 1.8)
|
|
71
|
+
standard-custom (1.0.2)
|
|
72
|
+
lint_roller (~> 1.0)
|
|
73
|
+
rubocop (~> 1.50)
|
|
74
|
+
standard-performance (1.9.0)
|
|
75
|
+
lint_roller (~> 1.1)
|
|
76
|
+
rubocop-performance (~> 1.26.0)
|
|
77
|
+
unicode-display_width (3.2.0)
|
|
78
|
+
unicode-emoji (~> 4.1)
|
|
79
|
+
unicode-emoji (4.2.0)
|
|
80
|
+
|
|
81
|
+
PLATFORMS
|
|
82
|
+
arm64-darwin-20
|
|
83
|
+
arm64-darwin-21
|
|
84
|
+
arm64-darwin-24
|
|
85
|
+
x86_64-linux
|
|
86
|
+
|
|
87
|
+
DEPENDENCIES
|
|
88
|
+
minitest (~> 5.0)
|
|
89
|
+
mocha
|
|
90
|
+
pry
|
|
91
|
+
rake
|
|
92
|
+
ruby-lsp
|
|
93
|
+
scripto!
|
|
94
|
+
standard
|
|
95
|
+
|
|
96
|
+
BUNDLED WITH
|
|
97
|
+
2.5.21
|
data/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://github.com/gurgeous/scripto/actions/workflows/ci.yml)
|
|
2
|
+
|
|
3
|
+

|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# Scripto
|
|
4
6
|
|
|
5
|
-
Scripto is a framework for writing command line applications. It fills in many of the blanks that Ruby's standard library is missing:
|
|
7
|
+
Scripto is a framework for writing Ruby command line applications. It fills in many of the blanks that Ruby's standard library is missing:
|
|
6
8
|
|
|
7
|
-
- **
|
|
9
|
+
- **printing** - Colored banners and a verbose mode to make your scripts louder.
|
|
8
10
|
- **file operations** - Mkdir, cp, mv, ln. These operations can take care of common edge cases, like creating the target directory before copying a file.
|
|
9
11
|
- **run commands** - Run external commands and raise errors on failure.
|
|
10
12
|
- **csv** - Read and write CSV files from hashes, Structs, or OpenStructs.
|
|
11
13
|
|
|
12
|
-
Rdoc at [rdoc.info](http://rdoc.info/github/gurgeous/scripto/). Thanks!
|
|
13
|
-
|
|
14
14
|
## Getting Started
|
|
15
15
|
|
|
16
16
|
You can call Scripto directly:
|
|
@@ -43,7 +43,7 @@ require "scripto"
|
|
|
43
43
|
|
|
44
44
|
class Install < Scripto::Main
|
|
45
45
|
def initialize(options = {})
|
|
46
|
-
|
|
46
|
+
super
|
|
47
47
|
|
|
48
48
|
banner("Starting installation...")
|
|
49
49
|
cp("here.txt", "there.txt")
|
|
@@ -67,17 +67,18 @@ Install.new(verbose: true)
|
|
|
67
67
|
|
|
68
68
|
## Methods
|
|
69
69
|
|
|
70
|
-
###
|
|
70
|
+
### Printing
|
|
71
|
+
|
|
72
|
+
Scripto has a built-in logger that wraps $stdout. The logger runs in INFO mode
|
|
73
|
+
by default, but will switch to DEBUG if options[:verbose] is true. Or you can
|
|
74
|
+
quiet it down to ERROR if options[:quiet] is true. `banner`, `warning` and
|
|
75
|
+
`fatal` use Scripto's logger.
|
|
71
76
|
|
|
72
77
|
```
|
|
73
78
|
banner(str) - print a banner in green
|
|
74
79
|
warning(str) - print a warning in yellow
|
|
75
80
|
fatal(str) - print a fatal error in red, then exit(1)
|
|
76
|
-
|
|
77
|
-
verbose! - turn on verbose mode
|
|
78
|
-
vbanner(str) - print a colored banner in green if verbose is on
|
|
79
|
-
vprintf(str) - printf if verbose is on
|
|
80
|
-
vputs(str) - puts if verbose is on
|
|
81
|
+
verbose! - force verbose
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
### File operations
|
|
@@ -156,7 +157,14 @@ random_string(len) - calculate a random alphanumeric string
|
|
|
156
157
|
|
|
157
158
|
# Changelog
|
|
158
159
|
|
|
159
|
-
0.0
|
|
160
|
+
### 1.0.0 (early 2026)
|
|
161
|
+
|
|
162
|
+
- banner and friends use Logger (breaking)
|
|
163
|
+
- only support Ruby 3.4+ (breaking)
|
|
164
|
+
- [standardrb](https://github.com/testdouble/standard) and a [justfile](https://github.com/casey/just)
|
|
165
|
+
- update deps, switch to Github actions, etc.
|
|
166
|
+
|
|
167
|
+
### 0.0.4 (late 2020)
|
|
160
168
|
|
|
161
169
|
- Added support for reading CSV with BOM.
|
|
162
170
|
- Only support Ruby 2.3+, since we moved to modern Bundler
|
data/Rakefile
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require 'rdoc/task'
|
|
4
|
-
require 'rubocop/rake_task'
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
require "rake/testtask"
|
|
5
3
|
|
|
6
|
-
Rake::TestTask.new(:test)
|
|
7
|
-
test.libs << 'test'
|
|
8
|
-
end
|
|
4
|
+
Rake::TestTask.new(:test)
|
|
9
5
|
task default: :test
|
|
10
|
-
|
|
11
|
-
RDoc::Task.new do |rdoc|
|
|
12
|
-
rdoc.rdoc_dir = 'rdoc'
|
|
13
|
-
rdoc.title = "scripto #{Scripto::VERSION}"
|
|
14
|
-
rdoc.main = 'README.md'
|
|
15
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
16
|
-
rdoc.rdoc_files.include('README.md')
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
RuboCop::RakeTask.new do |task|
|
|
20
|
-
task.options << '--display-cop-names'
|
|
21
|
-
end
|
data/justfile
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
# read gem version
|
|
3
|
+
gemver := `cat lib/scripto/version.rb | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+"`
|
|
4
|
+
|
|
5
|
+
set quiet := true
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# dev
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
default: test
|
|
12
|
+
|
|
13
|
+
check: lint test
|
|
14
|
+
|
|
15
|
+
fmt:
|
|
16
|
+
bundle exec rubocop -a
|
|
17
|
+
|
|
18
|
+
lint:
|
|
19
|
+
just banner lint...
|
|
20
|
+
bundle exec rubocop
|
|
21
|
+
|
|
22
|
+
pry:
|
|
23
|
+
bundle exec pry -I lib -r scripto.rb
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
just banner test...
|
|
27
|
+
bundle exec rake test
|
|
28
|
+
|
|
29
|
+
watch:
|
|
30
|
+
watchexec --watch lib --watch test --clear bundle exec rake test
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# gem tasks
|
|
34
|
+
#
|
|
35
|
+
|
|
36
|
+
gem-push: # check-git-status
|
|
37
|
+
just banner gem build...
|
|
38
|
+
gem build scripto.gemspec
|
|
39
|
+
just banner tag...
|
|
40
|
+
# git tag -a "v{{gemver}}" -m "Tagging {{gemver}}"
|
|
41
|
+
# git push --tags
|
|
42
|
+
just banner gem push...
|
|
43
|
+
gem push "scripto-{{gemver}}.gem"
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# util
|
|
47
|
+
#
|
|
48
|
+
|
|
49
|
+
GREEN := '\e[1;38;2;255;255;255;48;2;64;160;43m'
|
|
50
|
+
banner *ARGS:
|
|
51
|
+
printf '{{GREEN}}[%s] %-72s \e[m\n' "$(date +%H:%M:%S)" "{{ARGS}}"
|
|
52
|
+
|
|
53
|
+
check-git-status:
|
|
54
|
+
if [ ! -z "$(git status --porcelain)" ]; then echo "git status is dirty, bailing."; exit 1; fi
|
data/lib/scripto/csv_commands.rb
CHANGED
|
@@ -1,29 +1,23 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "csv"
|
|
2
|
+
require "tempfile"
|
|
3
|
+
require "zlib"
|
|
4
4
|
|
|
5
5
|
module Scripto
|
|
6
6
|
module CsvCommands
|
|
7
7
|
# Read a csv from +path+. Returns an array of Structs, using the keys from
|
|
8
8
|
# the csv header row.
|
|
9
9
|
def csv_read(path)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
CSV.new(f).read
|
|
14
|
-
end
|
|
15
|
-
else
|
|
16
|
-
encoding = 'bom|utf-8'
|
|
17
|
-
if RUBY_VERSION >= "2.6.0"
|
|
18
|
-
CSV.read(path, encoding: encoding)
|
|
19
|
-
else
|
|
20
|
-
CSV.read(path, "r:#{encoding}")
|
|
21
|
-
end
|
|
10
|
+
rows = if /\.gz$/.match?(path)
|
|
11
|
+
Zlib::GzipReader.open(path) do
|
|
12
|
+
CSV.new(_1).read
|
|
22
13
|
end
|
|
14
|
+
else
|
|
15
|
+
CSV.read(path, encoding: "bom|utf-8")
|
|
23
16
|
end
|
|
24
|
-
|
|
17
|
+
|
|
18
|
+
keys = rows.shift.map(&:to_sym)
|
|
25
19
|
klass = Struct.new(*keys)
|
|
26
|
-
|
|
20
|
+
rows.map { klass.new(*_1) }
|
|
27
21
|
end
|
|
28
22
|
|
|
29
23
|
# Write +rows+ to +path+ as csv. Rows can be an array of hashes, Structs,
|
|
@@ -32,21 +26,21 @@ module Scripto
|
|
|
32
26
|
# used as the column keys instead.
|
|
33
27
|
def csv_write(path, rows, cols: nil)
|
|
34
28
|
atomic_write(path) do |tmp|
|
|
35
|
-
CSV.open(tmp.path,
|
|
29
|
+
CSV.open(tmp.path, "wb") { csv_write0(_1, rows, cols:) }
|
|
36
30
|
end
|
|
37
31
|
end
|
|
38
32
|
|
|
39
33
|
# Write +rows+ to $stdout as a csv. Similar to csv_write.
|
|
40
34
|
def csv_to_stdout(rows, cols: nil)
|
|
41
|
-
CSV($stdout) {
|
|
35
|
+
CSV($stdout) { csv_write0(_1, rows, cols:) }
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
# Returns a string containing +rows+ as a csv. Similar to csv_write.
|
|
45
39
|
def csv_to_s(rows, cols: nil)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
string
|
|
40
|
+
StringIO.new.tap do
|
|
41
|
+
f = CSV.new(_1)
|
|
42
|
+
csv_write0(f, rows, cols:)
|
|
43
|
+
end.string
|
|
50
44
|
end
|
|
51
45
|
|
|
52
46
|
protected
|
|
@@ -60,7 +54,7 @@ module Scripto
|
|
|
60
54
|
# rows
|
|
61
55
|
rows.each do |row|
|
|
62
56
|
row = row.to_h
|
|
63
|
-
csv << cols.map {
|
|
57
|
+
csv << cols.map { row[_1] }
|
|
64
58
|
end
|
|
65
59
|
end
|
|
66
60
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "etc"
|
|
2
|
+
require "fileutils"
|
|
3
3
|
|
|
4
4
|
module Scripto
|
|
5
5
|
module FileCommands
|
|
@@ -55,7 +55,7 @@ module Scripto
|
|
|
55
55
|
def mkdir_if_necessary(dir, owner: nil, mode: nil)
|
|
56
56
|
return if File.exist?(dir) || File.symlink?(dir)
|
|
57
57
|
|
|
58
|
-
mkdir(dir, owner
|
|
58
|
+
mkdir(dir, owner:, mode:)
|
|
59
59
|
true
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -65,7 +65,7 @@ module Scripto
|
|
|
65
65
|
def cp_if_necessary(src, dst, mkdir: false, owner: nil, mode: nil)
|
|
66
66
|
return if File.exist?(dst) && FileUtils.compare_file(src, dst)
|
|
67
67
|
|
|
68
|
-
cp(src, dst, mkdir
|
|
68
|
+
cp(src, dst, mkdir:, owner:, mode:)
|
|
69
69
|
true
|
|
70
70
|
end
|
|
71
71
|
|
|
@@ -115,7 +115,7 @@ module Scripto
|
|
|
115
115
|
# Like rm -rf && mkdir -p. Like all file commands, the operation will be
|
|
116
116
|
# printed out if verbose?.
|
|
117
117
|
def rm_and_mkdir(dir)
|
|
118
|
-
raise "don't do this" if dir ==
|
|
118
|
+
raise "don't do this" if dir == ""
|
|
119
119
|
|
|
120
120
|
FileUtils.rm_rf(dir, verbose: verbose?)
|
|
121
121
|
mkdir(dir)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
|
|
3
|
+
module Scripto
|
|
4
|
+
module LogCommands
|
|
5
|
+
# https://catppuccin.com/palette/
|
|
6
|
+
RESET = "\e[0m"
|
|
7
|
+
GREEN = "\e[1;38;2;255;255;255;48;2;64;160;43m"
|
|
8
|
+
YELLOW = "\e[1;38;2;255;255;255;48;2;251;100;11m"
|
|
9
|
+
RED = "\e[1;38;2;255;255;255;48;2;210;15;57m"
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# logger
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
# Returns the built-in logger. If none has been set, create a new one
|
|
16
|
+
# wrapped around $stdout. Used by banner/warning/fatal.
|
|
17
|
+
def logger
|
|
18
|
+
@logger ||= begin
|
|
19
|
+
level = if options[:verbose]
|
|
20
|
+
Logger::DEBUG
|
|
21
|
+
elsif options[:quiet]
|
|
22
|
+
Logger::ERROR
|
|
23
|
+
else
|
|
24
|
+
Logger::INFO
|
|
25
|
+
end
|
|
26
|
+
Logger.new($stdout, level:, formatter: Formatter.new)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Set the built-in logger.
|
|
31
|
+
def logger=(value)
|
|
32
|
+
@logger = value
|
|
33
|
+
@log_with_color = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# options
|
|
38
|
+
#
|
|
39
|
+
|
|
40
|
+
# Get options
|
|
41
|
+
def options
|
|
42
|
+
@options ||= {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Set options
|
|
46
|
+
def options=(value)
|
|
47
|
+
@options = value.dup
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Should we log in verbose mode?
|
|
51
|
+
def verbose?
|
|
52
|
+
logger.level == Logger::DEBUG
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Should we log in quiet mode?
|
|
56
|
+
def quiet?
|
|
57
|
+
logger.level == Logger::ERROR
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Set logging to verbose (DEBUG)
|
|
61
|
+
def verbose!
|
|
62
|
+
logger.level = Logger::DEBUG
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Set logging to quiet (ERROR)
|
|
66
|
+
def quiet!
|
|
67
|
+
logger.level = Logger::ERROR
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Should we use color? Inferred from $stdout.tty? if not set.
|
|
71
|
+
def log_with_color?
|
|
72
|
+
return @log_with_color if defined?(@log_with_color)
|
|
73
|
+
$stdout.tty?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Set whether we should use color.
|
|
77
|
+
def log_with_color=(value)
|
|
78
|
+
@log_with_color = value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# banner/warning/fatal
|
|
83
|
+
#
|
|
84
|
+
|
|
85
|
+
# Log a colored banner in green.
|
|
86
|
+
def banner(str, log_level: Logger::INFO, color: GREEN)
|
|
87
|
+
s = "#{str} ".ljust(72, " ")
|
|
88
|
+
s = "[#{Time.new.strftime("%H:%M:%S")}] #{s}"
|
|
89
|
+
s = "#{color}#{s}#{RESET}" if log_with_color?
|
|
90
|
+
logger.add(log_level, s)
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Log a yellow warning banner.
|
|
95
|
+
def warning(str)
|
|
96
|
+
banner("Warning: #{str}", log_level: Logger::WARN, color: YELLOW)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Log a red error banner, then exit.
|
|
100
|
+
def fatal(str)
|
|
101
|
+
banner(str, log_level: Logger::FATAL, color: RED)
|
|
102
|
+
exit(1)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Simple log formatter with no timestamp.
|
|
106
|
+
class Formatter < Logger::Formatter
|
|
107
|
+
def call(_severity, _time, _progname, msg)
|
|
108
|
+
"#{msg}\n"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/scripto/main.rb
CHANGED
|
@@ -4,15 +4,12 @@ module Scripto
|
|
|
4
4
|
class Main
|
|
5
5
|
include CsvCommands
|
|
6
6
|
include FileCommands
|
|
7
|
+
include LogCommands
|
|
7
8
|
include MiscCommands
|
|
8
|
-
include PrintCommands
|
|
9
9
|
include RunCommands
|
|
10
10
|
|
|
11
|
-
attr_accessor :options
|
|
12
|
-
|
|
13
11
|
def initialize(options = {})
|
|
14
12
|
self.options = options
|
|
15
|
-
self.verbose = options[:verbose]
|
|
16
13
|
end
|
|
17
14
|
end
|
|
18
15
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "digest/md5"
|
|
2
|
+
require "etc"
|
|
3
3
|
|
|
4
4
|
module Scripto
|
|
5
5
|
module MiscCommands
|
|
6
|
-
BASE_62 = (
|
|
6
|
+
BASE_62 = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a
|
|
7
7
|
|
|
8
8
|
# Who is the current user?
|
|
9
9
|
def whoami
|
|
@@ -12,16 +12,12 @@ module Scripto
|
|
|
12
12
|
|
|
13
13
|
# Return true if the current user is "root".
|
|
14
14
|
def root?
|
|
15
|
-
whoami ==
|
|
15
|
+
whoami == "root"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Return the md5 checksum for the file at +path+.
|
|
19
19
|
def md5_file(path)
|
|
20
|
-
|
|
21
|
-
digest, buf = Digest::MD5.new, ''
|
|
22
|
-
digest.update(buf) while f.read(4096, buf)
|
|
23
|
-
digest.hexdigest
|
|
24
|
-
end
|
|
20
|
+
Digest::MD5.file(path).hexdigest
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
# Return the md5 checksum for +str+.
|
data/lib/scripto/run_commands.rb
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "English"
|
|
2
|
+
require "shellwords"
|
|
3
3
|
|
|
4
4
|
module Scripto
|
|
5
5
|
module RunCommands
|
|
6
6
|
# The error thrown by #run, #run_capture and #run_quietly on failure.
|
|
7
|
-
class
|
|
7
|
+
class RunError < StandardError
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
# Run an external command. Raise
|
|
10
|
+
# Run an external command. Raise RunError if something goes wrong. The
|
|
11
11
|
# command will be echoed if verbose?.
|
|
12
12
|
#
|
|
13
13
|
# Usage is similar to Kernel#system. If +args+ is nil, +command+ will be
|
|
14
|
-
# passed to the shell. If +args+ are included, the +command+ and +args+
|
|
15
|
-
#
|
|
14
|
+
# passed to the shell. If +args+ are included, the +command+ and +args+ will
|
|
15
|
+
# be run directly without the shell.
|
|
16
16
|
def run(command, args = nil)
|
|
17
|
-
|
|
18
|
-
vputs(cmd)
|
|
19
|
-
cmd.run
|
|
17
|
+
CommandLine.new(command, args).run
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
# Run a command and capture the output like backticks. See #run
|
|
@@ -36,7 +34,7 @@ module Scripto
|
|
|
36
34
|
def run_succeeds?(command, args = nil)
|
|
37
35
|
run_quietly(command, args)
|
|
38
36
|
true
|
|
39
|
-
rescue
|
|
37
|
+
rescue RunError
|
|
40
38
|
false
|
|
41
39
|
end
|
|
42
40
|
|
|
@@ -74,24 +72,24 @@ module Scripto
|
|
|
74
72
|
begin
|
|
75
73
|
captured = `#{self}`
|
|
76
74
|
rescue Errno::ENOENT
|
|
77
|
-
raise
|
|
75
|
+
raise RunError, "#{self} failed : ENOENT (No such file or directory)"
|
|
78
76
|
end
|
|
79
77
|
raise!($CHILD_STATUS) if $CHILD_STATUS != 0
|
|
80
78
|
captured
|
|
81
79
|
end
|
|
82
80
|
|
|
83
81
|
def raise!(status)
|
|
84
|
-
if status.termsig == Signal.list[
|
|
82
|
+
if status.termsig == Signal.list["INT"]
|
|
85
83
|
raise "#{self} interrupted"
|
|
86
84
|
end
|
|
87
85
|
|
|
88
|
-
raise
|
|
86
|
+
raise RunError, "#{self} failed : #{status.to_i / 256}"
|
|
89
87
|
end
|
|
90
88
|
|
|
91
89
|
def to_s
|
|
92
90
|
if !args.empty?
|
|
93
|
-
escaped = args.map {
|
|
94
|
-
"#{command} #{escaped.join(
|
|
91
|
+
escaped = args.map { Shellwords.escape(_1) }
|
|
92
|
+
"#{command} #{escaped.join(" ")}"
|
|
95
93
|
else
|
|
96
94
|
command
|
|
97
95
|
end
|