sym 2.8.1 → 3.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.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +31 -30
  3. data/.envrc +7 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +150 -928
  6. data/.travis.yml +16 -26
  7. data/CHANGELOG.md +220 -167
  8. data/Gemfile +1 -0
  9. data/LICENSE +2 -2
  10. data/README.adoc +670 -0
  11. data/Rakefile +10 -4
  12. data/bin/changelog +34 -0
  13. data/bin/sym.completion.bash +6 -4
  14. data/bin/sym.symit.bash +412 -187
  15. data/codecov.yml +29 -0
  16. data/design/sym-class-dependency-future-refactor.png +0 -0
  17. data/design/sym-class-dependency-vertical.png +0 -0
  18. data/design/sym-class-dependency.graffle +0 -0
  19. data/design/sym-class-dependency.png +0 -0
  20. data/design/sym-help.png +0 -0
  21. data/exe/keychain +1 -1
  22. data/exe/sym +5 -2
  23. data/lib/ruby_warnings.rb +7 -0
  24. data/lib/sym.rb +2 -8
  25. data/lib/sym/app.rb +1 -2
  26. data/lib/sym/app/args.rb +3 -2
  27. data/lib/sym/app/cli.rb +34 -21
  28. data/lib/sym/app/cli_slop.rb +9 -2
  29. data/lib/sym/app/commands.rb +1 -1
  30. data/lib/sym/app/commands/base_command.rb +1 -1
  31. data/lib/sym/app/commands/bash_completion.rb +2 -2
  32. data/lib/sym/app/commands/open_editor.rb +1 -1
  33. data/lib/sym/app/commands/password_protect_key.rb +4 -4
  34. data/lib/sym/app/commands/show_examples.rb +1 -1
  35. data/lib/sym/app/input/handler.rb +7 -1
  36. data/lib/sym/app/keychain.rb +15 -9
  37. data/lib/sym/app/output/noop.rb +2 -1
  38. data/lib/sym/app/password/cache.rb +1 -1
  39. data/lib/sym/app/password/providers.rb +2 -3
  40. data/lib/sym/app/private_key/decryptor.rb +2 -2
  41. data/lib/sym/app/private_key/detector.rb +4 -7
  42. data/lib/sym/application.rb +6 -11
  43. data/lib/sym/constants.rb +39 -23
  44. data/lib/sym/data/wrapper_struct.rb +20 -12
  45. data/lib/sym/errors.rb +13 -2
  46. data/lib/sym/extensions/instance_methods.rb +7 -8
  47. data/lib/sym/extensions/stdlib.rb +0 -1
  48. data/lib/sym/extensions/with_retry.rb +1 -1
  49. data/lib/sym/extensions/with_timeout.rb +1 -1
  50. data/lib/sym/version.rb +54 -5
  51. data/sym.gemspec +36 -35
  52. metadata +102 -66
  53. data/.codeclimate.yml +0 -30
  54. data/README.md +0 -623
  55. data/lib/sym/app/password/providers/drb_provider.rb +0 -41
data/Rakefile CHANGED
@@ -1,7 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
  require 'yard'
4
-
5
+ require 'timeout'
5
6
 
6
7
  def shell(*args)
7
8
  puts "running: #{args.join(' ')}"
@@ -24,13 +25,18 @@ end
24
25
  task :build => :permissions
25
26
 
26
27
  YARD::Rake::YardocTask.new(:doc) do |t|
27
- t.files = %w(lib/**/*.rb exe/*.rb - README.md LICENSE)
28
- t.options.unshift('--title','"Sym – Symmetric Key Encryption for Your Data"')
29
- t.after = ->() { exec('open doc/index.html') }
28
+ t.files = %w(lib/**/*.rb exe/*.rb - README.adoc CHANGELOG.md LICENSE)
29
+ t.options.unshift('--title', '"Sym – Symmetric Encryption for Humins"')
30
+ t.after = -> { Thread.new { sleep 5; exec('open doc/index.html') } }
30
31
  end
31
32
 
32
33
  RSpec::Core::RakeTask.new(:spec)
33
34
 
35
+ RuboCop::RakeTask.new
36
+
34
37
  task :default => :spec
35
38
 
36
39
 
40
+
41
+
42
+
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ # vim: ft=bash
3
+ unset DEBUG
4
+
5
+ [[ -d ~/.bashmatic ]] || bash -c "$(curl -fsSL http://bit.ly/bashmatic-1-2-0)"
6
+ source "${HOME}/.bashmatic/init.sh" 1>/dev/null
7
+
8
+ function chlog() {
9
+ run.set-all abort-on-error show-output-on
10
+
11
+ command -v github_changelog_generator >/dev/null || {
12
+ h1 'Installing changelog ruby gem...'
13
+ gem.install github_changelog_generator
14
+ }
15
+
16
+ if [[ -z "${GITHUB_TOKEN}" ]]; then
17
+ error "Please set GITHUB_TOKEN environment variable."
18
+ return 1
19
+ else
20
+ info "GitHub token found, starting CHANGELOG generation..."
21
+ fi
22
+
23
+ run "github_changelog_generator --no-verbose -u kigster -p sym -t ${GITHUB_TOKEN}"
24
+ }
25
+
26
+ chlog "$@"
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
@@ -14,14 +14,16 @@
14
14
  bash_version=$(bash --version | awk '{FS="version"}{print $4}')
15
15
  bash_version=${bash_version:0:1}
16
16
 
17
- declare -a bash_completion_locations=(/usr/local/etc/bash_completion /usr/etc/bash_completion /etc/bash_completion)
18
- loaded=false
19
- for file in ${bash_completion_locations[@]}; do
17
+ [[ -z $(type _filedir 2>/dev/null) ]] && {
18
+ declare -a bash_completion_locations=(/usr/local/etc/bash_completion /usr/etc/bash_completion /etc/bash_completion)
19
+ loaded=false
20
+ for file in ${bash_completion_locations[@]}; do
20
21
  [[ -s ${file} ]] && {
21
22
  source ${file}
22
23
  break
23
24
  }
24
- done
25
+ done
26
+ }
25
27
 
26
28
  _sym_long_opts() {
27
29
  sym -h | grep -- '--' | egrep '^ -' | awk '{print $2}' | sort
@@ -7,12 +7,12 @@
7
7
  #
8
8
  #==============================================================================
9
9
  #
10
- # The purpose of this script is to transparently edit application secrets in a
11
- # Rails apps or other repos. It simplifies the process of key import, as well
12
- # as the direct editing.
10
+ # The purpose of this script is to transparently edit application secrets in
11
+ # Rails apps or other projects. It simplifies the process of key import, as well
12
+ # as the direct editing, as well as multi-file encryption/decryption routines.
13
13
  #
14
- # If you set some or (ideally) ALL variables below to values specific to your
15
- # system, things will get easy.
14
+ # The idea is that you set some of the variables below to values specific to your
15
+ # system and working with encrypted files will become very easy.
16
16
  #
17
17
  # SYMIT__FOLDER is a relative folder to your project root, under which you
18
18
  # might keep ALL of your encrypted files. Alternatively, if you keep encrypted
@@ -25,7 +25,7 @@
25
25
  # export SYMIT__FOLDER="config"
26
26
  #
27
27
  # # this will be the name of your key in OS-X KeyChain
28
- # export SYMIT__KEY="MY_KEYCHAIN_NAME"
28
+ # export SYMIT__KEY="my-org.engineering.dev" # just a name
29
29
  #
30
30
  # # This is the extension given to the encrypted files. Ideally, leave it
31
31
  # # be as ".enc"
@@ -43,15 +43,41 @@
43
43
  # your Rails application. Neat, no?
44
44
  #
45
45
 
46
- ( [[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || \
46
+
47
+ # Check if we are being sourced in, or run as a script:
48
+ ( [[ -n ${ZSH_EVAL_CONTEXT} && ${ZSH_EVAL_CONTEXT} =~ :file$ ]] || \
47
49
  [[ -n $BASH_VERSION && $0 != "$BASH_SOURCE" ]]) && _s_=1 || _s_=0
48
50
 
49
51
  (( $_s_ )) && _is_sourced=1
50
52
  (( $_s_ )) || _is_sourced=0
51
53
 
52
- function __lib::color::setup() {
53
- if [[ -z "${setup_colors_loaded}" ]]; then
54
+ # Set all the defaults
55
+ function __symit::init() {
56
+ export SYMIT__EXTENSION=${SYMIT__EXTENSION:-'.enc'}
57
+ export SYMIT__FOLDER=${SYMIT__FOLDER:-'.'}
58
+ export SYMIT__KEY=${SYMIT__KEY}
59
+ export SYMIT__MIN_VERSION='latest'
60
+ }
61
+
62
+ # Returns name of the current shell, eg 'bash'
63
+ function __lib::shell::name() {
64
+ echo $(basename $(printf $SHELL))
65
+ }
66
+
67
+ # Returns 'yes' if current shell is BASH
68
+ function __lib::shell::is_bash() {
69
+ [[ $(__lib::shell::name) == "bash" ]] && echo yes
70
+ }
54
71
 
72
+ # Returns a number representing shell version, eg.
73
+ # 3 or 4 for BASH v3 and v4 respectively.
74
+ function __lib::bash::version_number() {
75
+ echo $BASH_VERSION | awk 'BEGIN{FS="."}{print $1}'
76
+ }
77
+
78
+ # Enable all colors, but only if the STDOUT is a terminal
79
+ function __lib::color::setup() {
80
+ if [[ -t 1 ]]; then
55
81
  export txtblk='\e[0;30m' # Black - Regular
56
82
  export txtred='\e[0;31m' # Red
57
83
  export txtgrn='\e[0;32m' # Green
@@ -91,15 +117,66 @@ function __lib::color::setup() {
91
117
  export clr='\e[0m' # Text Reset
92
118
  export txtrst='\e[0m' # Text Reset
93
119
  export rst='\e[0m' # Text Reset
94
- export GREP_COLOR=32
95
- export setup_colors_loaded=1
120
+ fi
121
+ }
122
+
123
+ # Unset all the colors, in case we a being piped into
124
+ # something else.
125
+ function __lib::color::reset() {
126
+ export txtblk=
127
+ export txtred=
128
+ export txtgrn=
129
+ export txtylw=
130
+ export txtblu=
131
+ export txtpur=
132
+ export txtcyn=
133
+ export txtwht=
134
+
135
+ export bldblk=
136
+ export bldred=
137
+ export bldgrn=
138
+ export bldylw=
139
+ export bldblu=
140
+ export bldpur=
141
+ export bldcyn=
142
+ export bldwht=
143
+
144
+ export unkblk=
145
+ export undred=
146
+ export undgrn=
147
+ export undylw=
148
+ export undblu=
149
+ export undpur=
150
+ export undcyn=
151
+ export undwht=
152
+
153
+ export bakblk=
154
+ export bakred=
155
+ export bakgrn=
156
+ export bakylw=
157
+ export bakblu=
158
+ export bakpur=
159
+ export bakcyn=
160
+ export bakwht=
161
+
162
+ export clr=
163
+ export txtrst=
164
+ export rst=
165
+ }
166
+
167
+ # Enable or disable the colors based on whether the STDOUT
168
+ # is a proper terminal, or a pipe.
169
+ function __lib::stdout::configure() {
170
+ if [[ -t 1 ]]; then
171
+ __lib::color::setup
96
172
  else
97
- [[ -n ${DEBUG} ]] && echo "colors already loaded..."
173
+ __lib::color::reset
98
174
  fi
99
175
  }
100
176
 
101
- ((${setup_colors_loaded})) ||__lib::color::setup
177
+ __lib::stdout::configure
102
178
 
179
+ # Check if we are being run as a script, and if so — bail.
103
180
  (( $_s_ )) || {
104
181
  printf "${bldred}This script is meant to be sourced into your environment,\n"
105
182
  printf "not run on a command line.${clr} \n\n"
@@ -108,11 +185,12 @@ function __lib::color::setup() {
108
185
  printf "or run the following command:\n\n"
109
186
 
110
187
  printf " \$ ${bldgrn}sym -B ~/.bash_profile${clr}\n\n"
111
-
188
+
112
189
  printf "${bldblu}Thanks for using Sym!${clr}\n"
113
190
  exit 1
114
191
  }
115
192
 
193
+ # Horizontal line, width of the full terminal
116
194
  function __lib::color::hr() {
117
195
  local cols=${1:-${COLUMNS}}
118
196
  local char=${2:-"—"}
@@ -123,152 +201,270 @@ function __lib::color::hr() {
123
201
  printf "${clr}\n"
124
202
  }
125
203
 
204
+ # Large header, all caps
126
205
  function __lib::color::h1() {
127
206
  local title=$(echo "$*" | tr 'a-z' 'A-Z')
128
207
  len=${#title}
129
208
  printf "${bldylw}${title}\n"
130
- __lib::color::hr ${len} ''
209
+ __lib::color::hr ${len} ''
131
210
  }
132
211
 
212
+ # Smaller header
133
213
  function __lib::color::h2() {
134
214
  printf "${bldpur}$*${clr}\n"
135
215
  }
136
216
 
137
- function __lib::color::cursor_to_col() {
217
+ # Shift cursor by N positions to the right
218
+ function __lib::color::cursor-right-by() {
138
219
  position=$1
139
- echo -en "\e[${position}C"
220
+ printf "\e[${position}C"
140
221
  }
141
222
 
142
- function __lib::color::cursor_to_row() {
223
+ # Shift cursor by N positions to the left
224
+ function __lib::color::cursor-left-by() {
143
225
  position=$1
144
- echo -en "\e[${position}H"
226
+ printf "\e[${position}D"
145
227
  }
146
228
 
147
- function __symit::init() {
148
- export SYMIT__EXTENSION=${SYMIT__EXTENSION:-'.enc'}
149
- export SYMIT__FOLDER=${SYMIT__FOLDER:-'.'}
150
- export SYMIT__KEY=${SYMIT__KEY}
229
+ # Shift cursor by N positions up
230
+ function __lib::color::cursor-up-by() {
231
+ position=$1
232
+ printf "\e[${position}A"
233
+ }
234
+
235
+ # Shift cursor by N positions down
236
+ function __lib::color::cursor-down-by() {
237
+ position=$1
238
+ printf "\e[${position}B"
239
+ }
240
+
241
+ # Convert a version string such as "1.50.17" to an integer
242
+ # 101050017 for numeric comparison:
243
+ function __lib::ver-to-i() {
244
+ version=${1}
245
+ echo ${version} | awk 'BEGIN{FS="."}{ printf "1%02d%03.3d%03.3d", $1, $2, $3}'
246
+ }
247
+
248
+ # Convert a result of __lib::ver-to-i() back to a regular version.
249
+ function __lib::i-to-ver() {
250
+ version=${1}
251
+ /usr/bin/env ruby -e "ver='${version}'; printf %Q{%d.%d.%d}, ver[1..2].to_i, ver[3..5].to_i, ver[6..8].to_i"
151
252
  }
253
+
254
+ # Prints Usage
152
255
  function __symit::usage() {
153
- __lib::color::setup
256
+ echo
154
257
  __lib::color::h1 "symit"
258
+
155
259
  printf "
156
- This a BASH wrapper for the encryption tool (ruby gem) 'Sym'. It streamlines
157
- editing encrypted of files, importing and securing your key, and other
158
- actions. The wrapper can be configured with ENV variables, or CLI flags.\n"
260
+ ${bldylw}symit${bldgrn} is a powerful BASH helper, that enhances the CLI encryption
261
+ tool called ${bldred}Sym${clr}, which is a Ruby Gem.
262
+
263
+ Sym has an extensive CLI interface, but it only handles one
264
+ encryption/decryption operation per invocation. With this script, you can
265
+ auto decrypt all files in a given folder, you can import the key in a
266
+ simpler way, and you can save into the environment sym configuration that
267
+ will be used. It also streamlines editing of encrypted files in a given
268
+ folder. Symit can be configured either with the ENV variables, or using
269
+ the CLI flags.\n"
159
270
 
160
271
  printf "
161
- The easiest way to take advantage of this wrapper is to set the following
272
+ The recommended way to use ${bldred}symit${clr} is to set the following
162
273
  environment variables, which removes the need to pass these values via the
163
- flags. These variables default to the shown values if not set elsewhere:${txtylw}
274
+ flags. These variables default to the shown values if not set elsewhere:
275
+
276
+ Perhaps the most critically important variable to set is ${txtylw}SYMIT__KEY${clr}:
277
+ ${txtylw}
278
+ export SYMIT__KEY='my-org.my-app.dev'
279
+ eg: export SYMIT__KEY='github.web.development'
280
+ ${clr}
281
+ The ${txtcya}key${clr} can resolve to a file name, or a name of ENV variable,
282
+ a keychain entry, or be the actual key (not recommended!). See the following
283
+ link for more info:
284
+
285
+ ${undblu}https://github.com/kigster/sym#resolving-the--k-argument${clr}
286
+
287
+ Additional configuration is available through these variables:
288
+ ${txtylw}
289
+ export SYMIT__EXTENSION='${SYMIT__EXTENSION}'
290
+ export SYMIT__FOLDER='${SYMIT__FOLDER}'
291
+ export SYMIT__MIN_VERSION='latest'
292
+ ${clr}
293
+ The last variable defines the minimum Sym version desired. Set it to
294
+ 'latest' to have symit auto-upgrade Sym every time it is invoked.
164
295
 
165
- export SYMIT__EXTENSION='${SYMIT__EXTENSION}'
166
- export SYMIT__FOLDER='${SYMIT__FOLDER}'
167
- export SYMIT__KEY='${SYMIT__KEY}'
168
296
  ${clr}\n"
169
297
 
170
298
  __lib::color::h2 "Usage:"
171
- printf " ${bldgrn}symit [ action ] [ partial-file-path ] [ flags ]${clr}\n\n"
299
+ printf " ${bldgrn}symit [ action ] [ file-path/pattern ] [ flags ]${clr}\n\n"
172
300
 
173
301
  __lib::color::h2 "Actions:"
174
302
  printf " Action is the first word that defaults to ${bldylw}edit${clr}.\n\n"
175
- printf " Valid actions are:\n"
176
- printf " ${bldylw}— install ${bldblk}ensures you are on the latest gem version\n"
177
- printf " ${bldylw}— generate ${bldblk}create a new secure key, and copies it to \n"
303
+ printf " ${bldcya}Valid actions are below, starting with the Key import or creation:${clr}\n\n"
304
+ printf " ${bldylw}— generate ${clr}create a new secure key, and copies it to the\n"
178
305
  printf " clipboard (if supported), otherwise prints to STDOUT\n"
179
- printf " Key is required, and used as a name within OSX KeyChain\n\n"
180
- printf " ${bldylw}— import [key] [insecure]\n"
181
- printf " ${bldblk}imports the key from clipboard and adds password\n"
182
- printf " encryption unless 'insecure' is passed in\n\n"
183
- printf " ${bldylw}— edit ${bldblk}Finds all files, and opens them in $EDITOR\n"
184
- printf " ${bldylw}— encrypt ${bldblk}Encrypts files matching file-path\n"
185
- printf " ${bldylw} decrypt ${bldblk}Adds the extension to file pattern and decrypts\n"
186
- printf " ${bldylw}— auto ${bldblk}encrypts decrypted file, and vice versa\n"
306
+ printf " Key name (set via SYMIT__KEY or -k flag) is required,\n"
307
+ printf " and is used as the KeyChain entry name for the new key.\n\n"
308
+ printf " ${bldylw} import [insecure]\n"
309
+ printf " ${clr}imports the key from clipboard and adds password\n"
310
+ printf " encryption unless 'insecure' is passed in. Same as above\n"
311
+ printf " in relation with the key parameter.\n\n"
312
+ printf " ${bldcya}The following actions require the file pattern/path argument:${clr}\n"
313
+ printf " ${bldylw}— edit ${clr}Finds all files, and opens them in $EDITOR\n"
314
+ printf " ${bldylw}— encrypt ${clr}Encrypts files matching file-path\n"
315
+ printf " ${bldylw}— decrypt ${clr}Adds the extension to file pattern and decrypts\n"
316
+ printf " ${bldylw}— auto ${clr}encrypts decrypted file, and vice versa\n"
187
317
 
188
318
  echo
189
319
  __lib::color::h2 "Flags:"
190
- printf " -f | --folder DIR ${bldblk}Top level folder to search.${clr}\n"
191
- printf " -k | --key KEY ${bldblk}Key identifier${clr}\n"
192
- printf " -x | --extension EXT ${bldblk}Default extension of encrypted files.${clr}\n"
193
- printf " -n | --dry-run ${bldblk}Print stuff, but don't do it${clr}\n"
194
- printf " -h | --help ${bldblk}Show this help message${clr}\n"
320
+ printf " -f | --folder DIR ${clr}Top level folder to search.${clr}\n"
321
+ printf " -k | --key KEY ${clr}Key identifier${clr}\n"
322
+ printf " -x | --extension EXT ${clr}Default extension of encrypted files.${clr}\n"
323
+ printf " -n | --dry-run ${clr}Print stuff, but dont do it${clr}\n"
324
+ printf " -a | --all-files ${clr}If provided ALL FILES are operated on${clr}\n"
325
+ printf " ${clr}Use with CAUTION!${clr}\n"
326
+ printf " -v | --verbose ${clr}Print more stuff${clr}\n"
327
+ printf " -q | --quiet ${clr}Print less stuff${clr}\n"
328
+ printf " -h | --help ${clr}Show this help message${clr}\n"
195
329
 
196
330
  echo
197
331
  __lib::color::h2 'Encryption key identifier can be:'
198
- printf "${clr}\
332
+ printf "${clr}"
333
+
334
+ printf '
199
335
  1. name of the keychain item storing the keychain (secure)
200
336
  2. name of the environment variable storing the Key (*)
201
337
  3. name of the file storing the key (*)
202
- 4. the key itself (*)
203
-
204
- ${bldred}(*) 2-4 are insecure UNLESS the key is encrypted with a password.${clr}
338
+ 4. the key itself (*)'
205
339
 
206
- Please refer to README about generating password protected keys:
340
+ echo
341
+ printf "${bldred}"
342
+ printf '
343
+ (*) 2-4 are insecure UNLESS the key is encrypted with a password.'; echo
344
+ printf "${clr}\
345
+ Please refer to README about generating password protected keys:\n
207
346
  ${bldblu}${undblu}https://github.com/kigster/sym#generating-the-key--examples${clr}\n\n"
208
-
209
347
  echo
210
348
 
211
349
  __lib::color::h1 'Examples:'
212
350
 
213
- printf " Ex1: To import a key securely,\n"
214
- printf " \$ ${bldgrn}symit${bldblu} import key ${clr}\n\n"
351
+ printf " To import a key securely, first copy the key to your clipboard,\n"
352
+ printf " and then run the following command, pasting the key when asked:\n\n"
353
+ printf " ❯ ${bldgrn}symit${bldblu} import key ${clr}\n\n"
215
354
 
216
- printf " Ex2.: To encrypt (or decrypt) ALL files in the 'config' directory:${clr}\n"
217
- printf " \$ ${bldgrn}symit${bldblu} encrypt|decrypt -a -f config ${clr}\n\n"
355
+ printf " To encrypt or decrypt ALL files in the 'config' directory:${clr}\n\n"
356
+ printf " ${bldgrn}symit${bldblu} encrypt|decrypt -a -f config ${clr}\n\n"
218
357
 
219
- printf " Ex3: To decrypt all *.yml.enc files in the 'config' directory:${clr}\n"
220
- printf " \$ ${bldgrn}symit${bldblu} decrypt '*.yml' -f config ${clr}\n\n"
358
+ printf " To decrypt all *.yml.enc files in the 'config' directory:${clr}\n\n"
359
+ printf " ${bldgrn}symit${bldblu} decrypt '*.yml' -f config ${clr}\n\n"
221
360
 
222
- printf " Ex4: To edit an encrypted file ${txtblu}config/application.yml.enc${clr}\n"
223
- printf " \$ ${bldgrn}symit${bldblu} application.yml${clr}\n\n"
361
+ printf " To edit an encrypted file ${txtblu}config/application.yml.enc${clr}\n\n"
362
+ printf " ${bldgrn}symit${bldblu} application.yml${clr}\n\n"
224
363
 
225
- printf " Ex5.: To auto decrypt a file ${txtblu}config/settings/crypt/pass.yml.enc${clr}\n"
226
- printf " \$ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n"
364
+ printf " To auto decrypt a file ${txtblu}config/settings/crypt/pass.yml.enc${clr}\n\n"
365
+ printf " ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n"
227
366
 
228
- printf " Ex6.: To automatically decide to either encrypt or decrypt a file,\n"
229
- printf " based on the file extension. First example encrypts the file, second\n"
230
- printf " decrypts it (because file extension is .enc):${clr}\n"
231
- printf " \$ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml${clr}\n"
232
- printf " \$ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n"
367
+ printf " To automatically decide to either encrypt or decrypt a file,\n"
368
+ printf " based on the file extension use 'auto' command. The first line below\n"
369
+ printf " encrypts the file, second decrypts it, because the file extension is .enc:${clr}\n\n"
233
370
 
234
- printf " Ex7.: To encrypt a file ${txtblu}config/settings.yml${clr}\n"
235
- printf " \$ ${bldgrn}symit${bldblu} encrypt config/settings.yml${clr}\n\n"
371
+ printf " ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml${clr}\n"
372
+ printf " ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n"
236
373
 
374
+ printf " To encrypt a file ${txtblu}config/settings.yml${clr}\n"
375
+ printf " ❯ ${bldgrn}symit${bldblu} encrypt config/settings.yml${clr}\n\n"
237
376
  }
377
+
238
378
  function __datum() {
239
379
  date +"%m/%d/%Y.%H:%M:%S"
240
380
  }
241
381
 
382
+ function __warn() {
383
+ __lib::color::cursor-left-by 1000
384
+ printf "${bldylw}$* ${bldylw}\n"
385
+ }
242
386
  function __err() {
243
- #__lib::color::cursor_to_col 0
244
- printf "${txtpur}[$(__datum)] ${bldred}ERROR: ${txterr}$* ${bldylw}\n"
387
+ __lib::color::cursor-left-by 1000
388
+ printf "${bldred}ERROR: ${txtred}$* ${bldylw}\n"
245
389
  }
246
390
 
247
391
  function __inf() {
248
- #__lib::color::cursor_to_col 0
249
- printf "${txtpur}[$(__datum)] ${bldgrn}INFO: ${clr}${bldblu}$*${clr}\n"
392
+ [[ ${cli__opts__quiet} ]] && return
393
+ __lib::color::cursor-left-by 1000
394
+ printf "${txtblu}$*${clr}\n"
395
+ }
396
+
397
+ function __dbg() {
398
+ [[ ${cli__opts__verbose} ]] || return
399
+ __lib::color::cursor-left-by 1000
400
+ printf "${txtgrn}$*${clr}\n"
401
+ }
402
+
403
+ function __lib::command::print() {
404
+ __inf "${bldylw}❯ ${bldcya}$*${clr}"
405
+ }
406
+
407
+ function __symit::sym::installed_version() {
408
+ __lib::ver-to-i $(gem list | grep sym | awk '{print $2}' | sed 's/(//g;s/)//g')
409
+ }
410
+
411
+ function __symit::sym::latest_version() {
412
+ __lib::ver-to-i $(gem query --remote -n '^sym$' | awk '{print $2}' | sed 's/(//g;s/)//g')
413
+ }
414
+
415
+ function __symit::install::update() {
416
+ local desired_version=$1
417
+ shift
418
+ local current_version=$2
419
+ shift
420
+ local version_args=$*
421
+
422
+ __inf "updating sym to version ${bldylw}$(__lib::i-to-ver ${desired_version})${clr}..."
423
+ printf "${bldblu}" >&1
424
+ echo y | gem uninstall sym --force -x 2>/dev/null
425
+ printf "${clr}" >&1
426
+
427
+ command="gem install sym ${version_args} "
428
+ eval "${command}" >/dev/null
429
+ code=$?
430
+ printf "${clr}" >&2
431
+ if [[ ${code} != 0 ]]; then
432
+ __err "gem install returned ${code}, with command ${bldylw}${command}"
433
+ return 127
434
+ fi
435
+ current_version=$(__symit::sym::installed_version)
436
+ __inf "sym version ${bldylw}$(__lib::i-to-ver ${current_version}) was successfully installed."
250
437
  }
251
438
 
252
439
  function __symit::install::gem() {
253
- __inf "Verifying current Sym version, please wait..."
254
- if [[ -z "${_symit__installed}" ]]; then
255
- current_version=$(gem list | grep sym | awk '{print $2}' | sed 's/(//g;s/)//g')
256
- if [[ -z "${current_version}" ]]; then
257
- gem install sym
440
+ if [[ -n ${__symit_last_checked_at} ]]; then
441
+ now=$(date +'%s')
442
+ if [[ $(( $now - ${__symit_last_checked_at} )) -lt 3600 ]]; then
443
+ return
444
+ fi
445
+ fi
446
+
447
+ export __symit_last_checked_at=${now:-$(date +'%s')}
448
+
449
+ __inf "Verifying current sym version, please wait..."
450
+ current_version=$(__symit::sym::installed_version)
451
+ if [[ -n ${SYMIT__MIN_VERSION} ]]; then
452
+ if [[ ${SYMIT__MIN_VERSION} -eq 'latest' ]]; then
453
+ desired_version=$(__symit::sym::latest_version)
454
+ version_args=''
258
455
  else
259
- local help=$(sym -h 2>&1)
260
- unset SYM_ARGS
261
- remote_version=$(gem search sym | egrep '^sym \(' | awk '{print $2}' | sed 's/(//g;s/)//g')
262
- if [[ "${remote_version}" != "${current_version}" ]]; then
263
- __inf "detected an older ${bldgrn}sym (${current_version})"
264
- __inf "installing ${bldgrn}sym (${remote_version})${clr}..."
265
- echo y | gem uninstall sym -a 2> /dev/null
266
- gem install sym
267
- export _symit__installed="yes"
268
- __inf "Installed sym version ${bldylw}$(sym --version)"
269
- else
270
- __inf "${bldgrn}sym${clr} ${txtblu}is on the latest version ${remote_version} already\n"
271
- fi
456
+ desired_version=$( __lib::ver-to-i ${SYMIT__MIN_VERSION})
457
+ version_args=" --version ${SYMIT__MIN_VERSION}"
458
+ fi
459
+
460
+ if [[ "${desired_version}" != "${current_version}" ]]; then
461
+ __symit::install::update "${desired_version}" "${current_version}" "${version_args}"
462
+ else
463
+ __inf "${bldgrn}sym${clr} ${txtblu}is on the correct version ${bldylw}$(__lib::i-to-ver ${desired_version})${txtblu} already"
464
+ fi
465
+ else
466
+ if [[ -z ${current_version} ]] ; then
467
+ __dbg "installing latest version of ${bldylw}sym..."
272
468
  fi
273
469
  fi
274
470
  }
@@ -278,55 +474,62 @@ function __symit::files() {
278
474
  }
279
475
 
280
476
  function __symit::files::cmd() {
281
- if [[ -n ${cli__opts[file]} && -n ${cli__opts[extension]} ]]; then
282
- local folder
283
- if [[ ${cli__opts[file]} =~ '/' ]]; then
284
- folder="${cli__opts[folder]}/$(dirname ${cli__opts[file]})"
285
- else
286
- folder="${cli__opts[folder]}"
477
+ if [[ -n ${cli__opts__file} && -n ${cli__opts__extension} ]]; then
478
+
479
+ local folder=${cli__opts__folder}
480
+ local file="${cli__opts__file}"
481
+ local ext="${cli__opts__extension}"
482
+
483
+ if [[ ${file} =~ '/' ]]; then
484
+ if [[ ${folder} == '.' ]]; then
485
+ folder="$(dirname ${file})"
486
+ else
487
+ folder="${folder}/$(dirname ${file})"
488
+ fi
489
+ file="$(basename ${file})"
287
490
  fi
288
- local file="$(basename ${cli__opts[file]})"
289
- local ext="${cli__opts[extension]}"
290
491
 
291
- if [[ "${cli__opts[action]}" == "auto" || "${cli__opts[action]}" == "encrypt" ]] ; then
292
- #find ${folder} -name "${file}" >&2
492
+ if [[ "${cli__opts__action}" == "encrypt" ]] ; then
293
493
  printf "find ${folder} -name '${file}' -and -not -name '*${ext}'"
294
- else
295
- #find ${folder} -name "${file}${ext}" >&2
296
- printf "find ${folder} -name '${file}${ext}'"
494
+ elif [[ "${cli__opts__action}" == "auto" ]] ; then
495
+ printf "find ${folder} -name '${file}'"
496
+ else # edit, decrypt
497
+ [[ ${file} =~ "${ext}" ]] || file="${file}${ext}"
498
+ printf "find ${folder} -name '${file}'"
297
499
  fi
298
500
  fi
299
501
  }
300
502
 
301
503
  function __symit::command() {
302
504
  file=${1}
303
- if [[ -n "${cli__opts[key]}" && -n "${cli__opts[extension]}" ]]; then
304
- action="${cli__opts[action]}"
305
- flags="${sym__actions[${action}]}"
505
+ if [[ -n "${cli__opts__key}" && -n "${cli__opts__extension}" ]]; then
506
+ action="${cli__opts__action}"
507
+ v="sym__actions__${action}"
508
+ flags="${!v}"
306
509
  if [[ ${action} =~ "key" ]]; then
307
- [[ -n ${cli__opts[verbose]} ]] && printf "processing key import action ${bldylw}${action}${clr}\n" >&2
308
- printf "sym ${flags} ${cli__opts[key]} "
510
+ [[ -n ${cli__opts__verbose} ]] && printf "processing key import action ${bldylw}${action}${clr}\n" >&2
511
+ printf "sym ${flags} ${cli__opts__key} "
309
512
  elif [[ ${action} =~ "generate" ]] ; then
310
- [[ -n ${cli__opts[verbose]} ]] && printf "processing generate key action ${bldylw}${action}${clr}\n" >&2
513
+ [[ -n ${cli__opts__verbose} ]] && printf "processing generate key action ${bldylw}${action}${clr}\n" >&2
311
514
  if [[ -n $(which pbcopy) ]]; then
312
515
  out_key=/tmp/outkey
313
- command="sym ${flags} ${cli__opts[key]} -q -o ${out_key}; cat ${out_key} | pbcopy; rm -f ${out_key}"
516
+ command="sym ${flags} ${cli__opts__key} -q -o ${out_key}; cat ${out_key} | pbcopy; rm -f ${out_key}"
314
517
  printf "${command}"
315
518
  else
316
- printf "sym ${flags} ${cli__opts[key]} "
519
+ printf "sym ${flags} ${cli__opts__key} "
317
520
  fi
318
521
  elif [[ -n ${file} ]] ; then
319
- ext="${cli__opts[extension]}"
522
+ ext="${cli__opts__extension}"
320
523
  [[ -z ${ext} ]] && ext='.enc'
321
524
  ext=$(echo ${ext} | sed -E 's/[\*\/,.]//g')
322
525
  if [[ ${action} =~ "encrypt" ]]; then
323
- printf "sym ${flags} ${file} -ck ${cli__opts[key]} -o ${file}.${ext}"
526
+ printf "sym ${flags} ${file} -ck ${cli__opts__key} -o ${file}.${ext}"
324
527
  elif [[ ${action} =~ "decrypt" ]]; then
325
528
  new_name=$(echo ${file} | sed "s/\.${ext}//g")
326
529
  [[ "${new_name}" == "${file}" ]] && name="${file}.decrypted"
327
- printf "sym ${flags} ${file} -ck ${cli__opts[key]} -o ${new_name}"
530
+ printf "sym ${flags} ${file} -ck ${cli__opts__key} -o ${new_name}"
328
531
  else
329
- printf "sym ${flags} ${file} -ck ${cli__opts[key]} "
532
+ printf "sym ${flags} ${file} -ck ${cli__opts__key} "
330
533
  fi
331
534
  else
332
535
  printf "printf \"ERROR: not sure how to generate a correct command\\n\""
@@ -346,39 +549,38 @@ function __symit::exit() {
346
549
  }
347
550
 
348
551
  function __symit::print_cli_args() {
349
- local -A args=$@
350
- __inf "action ${bldylw}: ${cli__opts[action]}${clr}"
351
- __inf "key ${bldylw}: ${cli__opts[key]}${clr}"
352
- __inf "file ${bldylw}: ${cli__opts[file]}${clr}"
353
- __inf "extension ${bldylw}: ${cli__opts[extension]}${clr}"
354
- __inf "folder ${bldylw}: ${cli__opts[folder]}${clr}"
355
- __inf "verbose ${bldylw}: ${cli__opts[verbose]}${clr}"
356
- __inf "dry_run ${bldylw}: ${cli__opts[dry_run]}${clr}"
552
+ __dbg "action ${bldylw}: ${cli__opts__action}${clr}"
553
+ __dbg "key ${bldylw}: ${cli__opts__key}${clr}"
554
+ __dbg "file ${bldylw}: ${cli__opts__file}${clr}"
555
+ __dbg "extension ${bldylw}: ${cli__opts__extension}${clr}"
556
+ __dbg "folder ${bldylw}: ${cli__opts__folder}${clr}"
557
+ __dbg "verbose ${bldylw}: ${cli__opts__verbose}${clr}"
558
+ __dbg "dry_run ${bldylw}: ${cli__opts__dry_run}${clr}"
357
559
  }
358
560
 
359
561
  function __symit::args::needs_file() {
360
- if [[ "${cli__opts[action]}" == 'edit' || \
361
- "${cli__opts[action]}" == 'auto' || \
362
- "${cli__opts[action]}" == 'encrypt' || \
363
- "${cli__opts[action]}" == 'decrypt' ]]; then
562
+ if [[ "${cli__opts__action}" == 'edit' || \
563
+ "${cli__opts__action}" == 'auto' || \
564
+ "${cli__opts__action}" == 'encrypt' || \
565
+ "${cli__opts__action}" == 'decrypt' ]]; then
364
566
  printf 'yes'
365
567
  fi
366
568
  }
367
569
 
368
570
  function __symit::validate_args() {
369
- if [[ -n $(__symit::args::needs_file) && -z ${cli__opts[file]} ]]; then
571
+ if [[ -n $(__symit::args::needs_file) && -z ${cli__opts__file} ]]; then
370
572
  __err "missing file argument, config/application.yml"
371
573
  return $(__symit::exit 2)
372
574
  fi
373
575
 
374
- if [[ -z "${cli__opts[key]}" ]]; then
576
+ if [[ -z "${cli__opts__key}" ]]; then
375
577
  __err "Key was not defined, pass it with ${bldblu}-k KEY_ID${bldred}"
376
578
  __err "or set it via ${bldgrn}\$SYMIT__KEY${bldred} variable."
377
579
  return $(__symit::exit 4)
378
580
  fi
379
581
 
380
- if [[ -z ${cli__opts[extension]} ]]; then
381
- cli__opts[extension]='.enc'
582
+ if [[ -z ${cli__opts__extension} ]]; then
583
+ cli__opts__extension='.enc'
382
584
  fi
383
585
  }
384
586
 
@@ -386,25 +588,23 @@ function __symit::run() {
386
588
  __symit::cleanup
387
589
  __symit::init
388
590
 
389
- declare -A cli__opts=(
390
- [verbose]=''
391
- [key]=${SYMIT__KEY}
392
- [extension]=${SYMIT__EXTENSION}
393
- [folder]=${SYMIT__FOLDER}
394
- [dry_run]=''
395
- [action]=edit
396
- [file]=''
397
- )
398
-
399
- declare -A sym__actions=(
400
- [generate]=' -cpgx '
401
- [edit]=' -t '
402
- [encrypt]='-e -f '
403
- [decrypt]='-d -f '
404
- [auto]=' -n '
405
- [key_secure]=' -iqcpx '
406
- [key_insecure]=' -iqcx '
407
- )
591
+ cli__opts__verbose=''
592
+ cli__opts__quiet=''
593
+ cli__opts__key=${SYMIT__KEY}
594
+ cli__opts__extension=${SYMIT__EXTENSION}
595
+ cli__opts__folder=${SYMIT__FOLDER}
596
+ cli__opts__dry_run=''
597
+ cli__opts__action=edit
598
+ cli__opts__file=''
599
+
600
+ sym__actions__generate=' -cpgx '
601
+ sym__actions__edit=' -t '
602
+ sym__actions__encrypt='-e -f '
603
+ sym__actions__decrypt='-d -f '
604
+ sym__actions__auto=' -n '
605
+ sym__actions__key_secure=' -iqcpx '
606
+ sym__actions__key_insecure=' -iqcx '
607
+ sym__actions__install='install'
408
608
 
409
609
  if [[ -z $1 ]]; then
410
610
  __symit::usage
@@ -425,7 +625,7 @@ function __symit::run() {
425
625
  if [[ -z $1 ]]; then
426
626
  __err "-k/--key requires an argument" && return $(__symit::exit 1)
427
627
  else
428
- cli__opts[key]=$1
628
+ cli__opts__key=$1
429
629
  shift
430
630
  fi
431
631
  ;;
@@ -435,7 +635,7 @@ function __symit::run() {
435
635
  if [[ -z $1 ]]; then
436
636
  __err "-x/--extension requires an argument" && return $(__symit::exit 1)
437
637
  else
438
- cli__opts[extension]=${1}
638
+ cli__opts__extension=${1}
439
639
  shift
440
640
  fi
441
641
  ;;
@@ -445,35 +645,40 @@ function __symit::run() {
445
645
  if [[ -z $1 ]]; then
446
646
  __err "-f/--folder requires an argument" && return $(__symit::exit 1)
447
647
  else
448
- cli__opts[folder]=${1}
648
+ cli__opts__folder=${1}
449
649
  shift
450
650
  fi
451
651
  ;;
452
652
 
453
653
  -a|--all-files)
454
654
  shift
455
- cli__opts[file]="'*'"
655
+ cli__opts__file="'*'"
456
656
  ;;
457
657
 
458
658
  -n|--dry-run)
459
659
  shift
460
- cli__opts[dry_run]="yes"
660
+ cli__opts__dry_run="yes"
461
661
  ;;
462
662
 
463
663
  -v|--verbose)
464
664
  shift
465
- cli__opts[verbose]="yes"
665
+ cli__opts__verbose="yes"
666
+ ;;
667
+
668
+ -q|--quiet)
669
+ shift
670
+ cli__opts__quiet="yes"
466
671
  ;;
467
672
 
468
673
  import|key)
469
674
  shift
470
- cli__opts[action]="key_secure"
675
+ cli__opts__action="key_secure"
471
676
  ;;
472
677
 
473
678
  insecure)
474
679
  shift
475
- if [[ "${cli__opts[action]}" == 'key_secure' ]] ; then
476
- cli__opts[action]="key_insecure"
680
+ if [[ "${cli__opts__action}" == 'key_secure' ]] ; then
681
+ cli__opts__action="key_insecure"
477
682
  fi
478
683
  ;;
479
684
 
@@ -491,10 +696,14 @@ function __symit::run() {
491
696
 
492
697
  ?*)
493
698
  param=$1
494
- if [[ -n "${sym__actions[${param}]}" ]]; then
495
- cli__opts[action]=${param}
699
+ v="sym__actions__${param}"
700
+ if [[ ! ${param} =~ '.' && -n "${!v}" ]]; then
701
+ __dbg "Action ${bldylw}${param}${clr} is a valid action."
702
+ cli__opts__action=${param}
496
703
  else
497
- cli__opts[file]=${1}
704
+ __dbg "Parameter ${bldylw}${param}${clr} is not a valid action,"
705
+ __dbg "therefore it must be a file pattern."
706
+ cli__opts__file=${1}
498
707
  fi
499
708
  shift
500
709
  ;;
@@ -505,12 +714,12 @@ function __symit::run() {
505
714
  esac
506
715
  done
507
716
 
508
- [[ -n ${cli__opts[verbose]} ]] &&__symit::print_cli_args
717
+ [[ -n "${cli__opts__verbose}" ]] && __symit::print_cli_args
509
718
 
510
- if [[ "${cli__opts[action]}" == 'install' ]]; then
511
- if [[ -n ${cli__opts[dry_run]} ]]; then
512
- __inf "This command verifies that Sym is properly installed,"
513
- __inf "and if not found — installs it."
719
+ if [[ "${cli__opts__action}" == 'install' ]]; then
720
+ if [[ -n ${cli__opts__dry_run} ]]; then
721
+ __dbg "This command verifies that Sym is properly installed,"
722
+ __dbg "and if not found — installs it."
514
723
  return $(__symit::exit 0)
515
724
  else
516
725
  __symit::install::gem
@@ -520,37 +729,53 @@ function __symit::run() {
520
729
 
521
730
  __symit::validate_args
522
731
 
732
+ code=$?
733
+ if [[ ${code} != 0 ]]; then
734
+ return $(__symit::exit ${code})
735
+ fi
736
+
737
+ __symit::install::gem
738
+
523
739
  changed_count=0
524
740
 
525
- if [[ -n "${cli__opts[dry_run]}" ]] ; then
526
- __lib::color::h1 "Dry Run — printing commands that would be run:"
741
+ if [[ -n "${cli__opts__dry_run}" ]] ; then
742
+ __lib::color::h1 "DRY RUN"
527
743
  for file in $(__symit::files); do
528
744
  printf " \$ ${bldblu}$(__symit::command ${file})${clr}\n"
529
745
  done
530
746
  else
531
- if [[ -n "${cli__opts[file]}" ]]; then
532
- [[ -n ${cli__opts[verbose]} ]] && __inf $(__symit::files)
747
+ if [[ -n "${cli__opts__file}" ]]; then
748
+ [[ -n ${cli__opts__verbose} ]] && __dbg $(__symit::files)
533
749
  declare -a file_list
750
+
534
751
  for file in $(__symit::files); do
535
- file_list=(${file} "${file_list[*]}")
536
- __inf "❯ ${bldblu}$(__symit::command ${file})${clr}"
537
- eval $(__symit::command ${file})
538
- code=$?; [[ ${code} != 0 ]] && __err "sym returned non-zero code ${code}"
752
+ local cmd="$(__symit::command ${file})"
753
+ __lib::command::print "${cmd}"
754
+ eval "${cmd}"
755
+ code=$?; [[ ${code} != 0 ]] && __err "command '${bldblu}${cmd}${bldred}' exited with code ${bldylw}${code}"
756
+ changed_count=$(( ${changed_count} + 1))
539
757
  done
540
- if [[ ${#file_list} == 0 ]]; then
541
- __inf "No files matched your specification. The following 'find' command"
542
- __inf "ran to find them: \n"
543
- __inf " ${bldylw}$(__symit::files::cmd)${clr}\n\n"
758
+
759
+ if [[ ${changed_count} == 0 ]]; then
760
+ printf "${undylw}Bad news:${clr}\n\n"
761
+ __warn " No files matched your specification. The following 'find' command"
762
+ __warn " ran to find the file you requested. Please change the name, and "
763
+ __warn " try again.\n"
764
+ __warn " ${bldblu}$(__symit::files::cmd)${clr}\n\n"
544
765
  return $(__symit::exit 5)
545
766
  fi
546
- else
547
- [[ -n ${cli__opts[verbose]} ]] && __inf $(__symit::command)
548
- eval $(__symit::command)
549
- code=$?; [[ ${code} != 0 ]] && return $(__symits::exit ${code})
767
+
768
+ else # opts[file]
769
+ cmd=$(__symit::command)
770
+ __lib::command::print "${cmd}"
771
+ eval "${cmd}"
772
+ code=$?; [[ ${code} != 0 ]] && return $(__symit::exit ${code})
773
+ changed_count=$(( ${changed_count} + 1))
550
774
  fi
551
775
  fi
552
776
  }
553
777
 
554
778
  function symit() {
779
+ __lib::stdout::configure
555
780
  __symit::run $@
556
781
  }