invar 0.9.0 → 0.9.1
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/RELEASE_NOTES.md +15 -0
- data/lib/invar/rake/task/config.rb +51 -0
- data/lib/invar/rake/task/namespaced.rb +26 -0
- data/lib/invar/rake/task/secrets.rb +130 -0
- data/lib/invar/rake/task/status.rb +17 -0
- data/lib/invar/rake/tasks.rb +24 -205
- data/lib/invar/reality.rb +2 -4
- data/lib/invar/scope.rb +12 -2
- data/lib/invar/version.rb +1 -1
- data/lib/invar.rb +20 -4
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2346259fc26689ffca5386f39e982de60dc9f2d9c03399f85023e05a47802f7a
|
4
|
+
data.tar.gz: b6e2cea0c52fd0b52eb833036844c03d3a714d83e0572dc5dcbb4ae083c61cf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a9556b30d2c17fda9373551ecf6fffeaacabd35aa0e40bb883c78e8bd94642bf4bebbeea054b5f9a9681b0bb23b6ee8c30860afb732f94b9d7887e62e0c0c47
|
7
|
+
data.tar.gz: a80954a8dbfc4c42d3f57b94cb2830a8e598eda85cd95f37ff431a1ab2b253fa64d4c65f9aacba8cfa718f5b5f1082b05f78c0d1f97d2ae7bc58b4392c4e19f0
|
data/RELEASE_NOTES.md
CHANGED
@@ -19,6 +19,21 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
19
19
|
|
20
20
|
* none
|
21
21
|
|
22
|
+
## [0.9.1] - 2023-09-30
|
23
|
+
|
24
|
+
### Major Changes
|
25
|
+
|
26
|
+
* none
|
27
|
+
|
28
|
+
### Minor Changes
|
29
|
+
|
30
|
+
* none
|
31
|
+
|
32
|
+
### Bugfixes
|
33
|
+
|
34
|
+
* Improved handling of test-only methods when trying to access them via `#method`
|
35
|
+
* Fixed TTY detection
|
36
|
+
|
22
37
|
## [0.9.0] - 2023-09-24
|
23
38
|
|
24
39
|
### Major Changes
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'namespaced'
|
4
|
+
|
5
|
+
module Invar
|
6
|
+
module Rake
|
7
|
+
module Task
|
8
|
+
# Rake task handler for actions to do with configuration files
|
9
|
+
class ConfigFileHandler < NamespacedFileTask
|
10
|
+
# Creates a config file in the appropriate location
|
11
|
+
def create
|
12
|
+
config_dir.mkpath
|
13
|
+
file_path.write CONFIG_TEMPLATE
|
14
|
+
file_path.chmod 0o600
|
15
|
+
|
16
|
+
warn "Created file: #{ file_path }"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Edits the existing config file in the appropriate location
|
20
|
+
def edit
|
21
|
+
content = $stdin.stat.pipe? ? $stdin.read : nil
|
22
|
+
file_path = configs_file
|
23
|
+
|
24
|
+
if content
|
25
|
+
file_path.write content
|
26
|
+
else
|
27
|
+
system ENV.fetch('EDITOR', 'editor'), file_path.to_s, exception: true
|
28
|
+
end
|
29
|
+
|
30
|
+
warn "File saved to: #{ file_path }"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def configs_file
|
36
|
+
@locator.find 'config.yml'
|
37
|
+
rescue ::Invar::FileLocator::FileNotFoundError => e
|
38
|
+
warn <<~ERR
|
39
|
+
Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
|
40
|
+
#{ CREATE_SUGGESTION }
|
41
|
+
ERR
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def filename
|
46
|
+
'config.yml'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../tasks'
|
4
|
+
|
5
|
+
module Invar
|
6
|
+
module Rake
|
7
|
+
module Task
|
8
|
+
# Abstract class for tasks that use a namespace for file searching
|
9
|
+
class NamespacedFileTask
|
10
|
+
def initialize(namespace)
|
11
|
+
@locator = FileLocator.new(namespace)
|
12
|
+
end
|
13
|
+
|
14
|
+
def file_path
|
15
|
+
config_dir / filename
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def config_dir
|
21
|
+
@locator.search_paths.first
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'namespaced'
|
4
|
+
|
5
|
+
module Invar
|
6
|
+
module Rake
|
7
|
+
module Task
|
8
|
+
# Rake task handler for actions on the secrets file.
|
9
|
+
class SecretsFileHandler < NamespacedFileTask
|
10
|
+
# Instructions hint for how to handle secret keys.
|
11
|
+
SECRETS_INSTRUCTIONS = <<~INST
|
12
|
+
Generated key. Save this key to a secure password manager, you will need it to edit the secrets.yml file:
|
13
|
+
INST
|
14
|
+
|
15
|
+
SWAP_EXT = 'tmp'
|
16
|
+
|
17
|
+
# Creates a new encrypted secrets file and prints the generated encryption key to STDOUT
|
18
|
+
def create(content: SECRETS_TEMPLATE)
|
19
|
+
encryption_key = Lockbox.generate_key
|
20
|
+
|
21
|
+
write_encrypted_file(file_path,
|
22
|
+
encryption_key: encryption_key,
|
23
|
+
content: content,
|
24
|
+
permissions: PrivateFile::DEFAULT_PERMISSIONS)
|
25
|
+
|
26
|
+
warn SECRETS_INSTRUCTIONS
|
27
|
+
puts encryption_key
|
28
|
+
end
|
29
|
+
|
30
|
+
# Updates the file with new content.
|
31
|
+
#
|
32
|
+
# Either the content is provided over STDIN or the default editor is opened with the decrypted contents of
|
33
|
+
# the secrets file. After closing the editor, the file will be updated with the new encrypted contents.
|
34
|
+
def edit
|
35
|
+
content = $stdin.stat.pipe? ? $stdin.read : nil
|
36
|
+
|
37
|
+
edit_encrypted_file(secrets_file, content: content)
|
38
|
+
|
39
|
+
warn "File saved to #{ secrets_file }"
|
40
|
+
end
|
41
|
+
|
42
|
+
def rotate
|
43
|
+
file_path = secrets_file
|
44
|
+
|
45
|
+
decrypted = read_encrypted_file(file_path, encryption_key: determine_key(file_path))
|
46
|
+
|
47
|
+
swap_file = file_path.dirname / [file_path.basename, SWAP_EXT].join('.')
|
48
|
+
file_path.rename swap_file
|
49
|
+
|
50
|
+
begin
|
51
|
+
create content: decrypted
|
52
|
+
swap_file.delete
|
53
|
+
rescue StandardError
|
54
|
+
swap_file.rename file_path.to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def secrets_file
|
61
|
+
@locator.find 'secrets.yml'
|
62
|
+
rescue ::Invar::FileLocator::FileNotFoundError => e
|
63
|
+
warn <<~ERR
|
64
|
+
Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
|
65
|
+
#{ CREATE_SUGGESTION }
|
66
|
+
ERR
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
|
70
|
+
def filename
|
71
|
+
'secrets.yml'
|
72
|
+
end
|
73
|
+
|
74
|
+
def read_encrypted_file(file_path, encryption_key:)
|
75
|
+
lockbox = build_lockbox(encryption_key)
|
76
|
+
lockbox.decrypt(file_path.binread)
|
77
|
+
end
|
78
|
+
|
79
|
+
def write_encrypted_file(file_path, encryption_key:, content:, permissions: nil)
|
80
|
+
lockbox = build_lockbox(encryption_key)
|
81
|
+
|
82
|
+
encrypted_data = lockbox.encrypt(content)
|
83
|
+
|
84
|
+
config_dir.mkpath
|
85
|
+
# TODO: replace File.opens with photo_path.binwrite(uri.data) once FakeFS can handle it
|
86
|
+
File.open(file_path.to_s, 'wb') { |f| f.write encrypted_data }
|
87
|
+
file_path.chmod permissions if permissions
|
88
|
+
|
89
|
+
warn "Saved file: #{ file_path }"
|
90
|
+
end
|
91
|
+
|
92
|
+
def edit_encrypted_file(file_path, content: nil)
|
93
|
+
encryption_key = determine_key(file_path)
|
94
|
+
|
95
|
+
content ||= invoke_editor(file_path, encryption_key: encryption_key)
|
96
|
+
|
97
|
+
write_encrypted_file(file_path, encryption_key: encryption_key, content: content)
|
98
|
+
end
|
99
|
+
|
100
|
+
def invoke_editor(file_path, encryption_key:)
|
101
|
+
Tempfile.create(file_path.basename.to_s) do |tmp_file|
|
102
|
+
decrypted = read_encrypted_file(file_path, encryption_key: encryption_key)
|
103
|
+
|
104
|
+
tmp_file.write(decrypted)
|
105
|
+
tmp_file.rewind # rewind needed because file does not get closed after write
|
106
|
+
system ENV.fetch('EDITOR', 'editor'), tmp_file.path, exception: true
|
107
|
+
tmp_file.read
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def determine_key(file_path)
|
112
|
+
encryption_key = Lockbox.master_key
|
113
|
+
|
114
|
+
if encryption_key.nil? && $stdin.tty?
|
115
|
+
warn "Enter master key to decrypt #{ file_path }:"
|
116
|
+
encryption_key = $stdin.noecho(&:gets).strip
|
117
|
+
end
|
118
|
+
|
119
|
+
encryption_key
|
120
|
+
end
|
121
|
+
|
122
|
+
def build_lockbox(encryption_key)
|
123
|
+
Lockbox.new(key: encryption_key)
|
124
|
+
rescue ArgumentError => e
|
125
|
+
raise SecretsFileEncryptionError, e
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'namespaced'
|
4
|
+
|
5
|
+
module Invar
|
6
|
+
module Rake
|
7
|
+
module Task
|
8
|
+
# Rake task handler for actions that just show information about the system
|
9
|
+
class StatusHandler < NamespacedFileTask
|
10
|
+
# Prints the current paths to be searched in
|
11
|
+
def show_paths
|
12
|
+
warn @locator.search_paths.join("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/invar/rake/tasks.rb
CHANGED
@@ -3,29 +3,24 @@
|
|
3
3
|
require 'invar'
|
4
4
|
|
5
5
|
require 'rake'
|
6
|
-
require 'io/console'
|
7
6
|
require 'tempfile'
|
8
7
|
|
8
|
+
require_relative 'task/config'
|
9
|
+
require_relative 'task/secrets'
|
10
|
+
require_relative 'task/status'
|
11
|
+
|
9
12
|
module Invar
|
10
|
-
# Rake task
|
13
|
+
# Rake task module for Invar-related tasks.
|
14
|
+
#
|
15
|
+
# The specific rake task implementations are delegated to handlers in Invar::Rake::Task
|
11
16
|
#
|
12
|
-
#
|
13
|
-
# path is nice and short)
|
17
|
+
# @see Invar::Rake::Tasks.define
|
14
18
|
module Rake
|
15
19
|
# RakeTask builder class. Use Tasks.define to generate the needed tasks.
|
16
20
|
class Tasks
|
17
21
|
include ::Rake::Cloneable
|
18
22
|
include ::Rake::DSL
|
19
23
|
|
20
|
-
# Template config YAML file
|
21
|
-
CONFIG_TEMPLATE = SECRETS_TEMPLATE = <<~YML
|
22
|
-
---
|
23
|
-
YML
|
24
|
-
|
25
|
-
CREATE_SUGGESTION = <<~SUGGESTION
|
26
|
-
Maybe you used the wrong namespace or need to create the file with bundle exec rake invar:init?
|
27
|
-
SUGGESTION
|
28
|
-
|
29
24
|
# Shorthand for Invar::Rake::Tasks.new.define
|
30
25
|
#
|
31
26
|
# @param (see #define)
|
@@ -62,8 +57,8 @@ module Invar
|
|
62
57
|
task :init, [:mode] do |_task, args|
|
63
58
|
mode = args.mode
|
64
59
|
|
65
|
-
config = ::Invar::Rake::
|
66
|
-
secrets = ::Invar::Rake::
|
60
|
+
config = ::Invar::Rake::Task::ConfigFileHandler.new(app_namespace)
|
61
|
+
secrets = ::Invar::Rake::Task::SecretsFileHandler.new(app_namespace)
|
67
62
|
|
68
63
|
case mode
|
69
64
|
when 'config'
|
@@ -84,7 +79,7 @@ module Invar
|
|
84
79
|
def define_config_task(app_namespace)
|
85
80
|
desc 'Edit the config in your default editor'
|
86
81
|
task :configs do
|
87
|
-
::Invar::Rake::
|
82
|
+
::Invar::Rake::Task::ConfigFileHandler.new(app_namespace).edit
|
88
83
|
end
|
89
84
|
|
90
85
|
# alias
|
@@ -94,7 +89,7 @@ module Invar
|
|
94
89
|
def define_secrets_tasks(app_namespace)
|
95
90
|
desc 'Edit the encrypted secrets file in your default editor'
|
96
91
|
task :secrets do
|
97
|
-
::Invar::Rake::
|
92
|
+
::Invar::Rake::Task::SecretsFileHandler.new(app_namespace).edit
|
98
93
|
end
|
99
94
|
|
100
95
|
# alias
|
@@ -102,14 +97,14 @@ module Invar
|
|
102
97
|
|
103
98
|
desc 'Encrypt the secrets file with a new generated key'
|
104
99
|
task :rotate do
|
105
|
-
::Invar::Rake::
|
100
|
+
::Invar::Rake::Task::SecretsFileHandler.new(app_namespace).rotate
|
106
101
|
end
|
107
102
|
end
|
108
103
|
|
109
104
|
def define_info_tasks(app_namespace)
|
110
105
|
desc 'Show directories to be searched for the given namespace'
|
111
106
|
task :paths do
|
112
|
-
::Invar::Rake::
|
107
|
+
::Invar::Rake::Task::StatusHandler.new(app_namespace).show_paths
|
113
108
|
end
|
114
109
|
end
|
115
110
|
|
@@ -136,194 +131,18 @@ module Invar
|
|
136
131
|
warn msg
|
137
132
|
exit 1
|
138
133
|
end
|
134
|
+
end
|
139
135
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
def file_path
|
147
|
-
config_dir / filename
|
148
|
-
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
def config_dir
|
153
|
-
@locator.search_paths.first
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Configuration file actions.
|
158
|
-
class ConfigFileHandler < NamespacedFileTask
|
159
|
-
# Creates a config file in the appropriate location
|
160
|
-
def create
|
161
|
-
config_dir.mkpath
|
162
|
-
file_path.write CONFIG_TEMPLATE
|
163
|
-
file_path.chmod 0o600
|
164
|
-
|
165
|
-
warn "Created file: #{ file_path }"
|
166
|
-
end
|
167
|
-
|
168
|
-
# Edits the existing config file in the appropriate location
|
169
|
-
def edit
|
170
|
-
content = $stdin.tty? ? nil : $stdin.read
|
171
|
-
file_path = configs_file
|
172
|
-
|
173
|
-
if content
|
174
|
-
file_path.write content
|
175
|
-
else
|
176
|
-
system ENV.fetch('EDITOR', 'editor'), file_path.to_s, exception: true
|
177
|
-
end
|
178
|
-
|
179
|
-
warn "File saved to: #{ file_path }"
|
180
|
-
end
|
181
|
-
|
182
|
-
private
|
183
|
-
|
184
|
-
def configs_file
|
185
|
-
@locator.find 'config.yml'
|
186
|
-
rescue ::Invar::FileLocator::FileNotFoundError => e
|
187
|
-
warn <<~ERR
|
188
|
-
Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
|
189
|
-
#{ CREATE_SUGGESTION }
|
190
|
-
ERR
|
191
|
-
exit 1
|
192
|
-
end
|
193
|
-
|
194
|
-
def filename
|
195
|
-
'config.yml'
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# Secrets file actions.
|
200
|
-
class SecretsFileHandler < NamespacedFileTask
|
201
|
-
# Instructions hint for how to handle secret keys.
|
202
|
-
SECRETS_INSTRUCTIONS = <<~INST
|
203
|
-
Generated key. Save this key to a secure password manager, you will need it to edit the secrets.yml file:
|
204
|
-
INST
|
205
|
-
|
206
|
-
SWAP_EXT = 'tmp'
|
207
|
-
|
208
|
-
# Creates a new encrypted secrets file and prints the generated encryption key to STDOUT
|
209
|
-
def create(content: SECRETS_TEMPLATE)
|
210
|
-
encryption_key = Lockbox.generate_key
|
211
|
-
|
212
|
-
write_encrypted_file(file_path,
|
213
|
-
encryption_key: encryption_key,
|
214
|
-
content: content,
|
215
|
-
permissions: PrivateFile::DEFAULT_PERMISSIONS)
|
216
|
-
|
217
|
-
warn SECRETS_INSTRUCTIONS
|
218
|
-
puts encryption_key
|
219
|
-
end
|
220
|
-
|
221
|
-
# Updates the file with new content.
|
222
|
-
#
|
223
|
-
# Either the content is provided over STDIN or the default editor is opened with the decrypted contents of
|
224
|
-
# the secrets file. After closing the editor, the file will be updated with the new encrypted contents.
|
225
|
-
def edit
|
226
|
-
content = $stdin.tty? ? nil : $stdin.read
|
227
|
-
|
228
|
-
edit_encrypted_file(secrets_file, content: content)
|
229
|
-
|
230
|
-
warn "File saved to #{ secrets_file }"
|
231
|
-
end
|
232
|
-
|
233
|
-
def rotate
|
234
|
-
file_path = secrets_file
|
235
|
-
|
236
|
-
decrypted = read_encrypted_file(file_path, encryption_key: determine_key(file_path))
|
237
|
-
|
238
|
-
swap_file = file_path.dirname / [file_path.basename, SWAP_EXT].join('.')
|
239
|
-
file_path.rename swap_file
|
240
|
-
|
241
|
-
begin
|
242
|
-
create content: decrypted
|
243
|
-
swap_file.delete
|
244
|
-
rescue StandardError
|
245
|
-
swap_file.rename file_path.to_s
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
private
|
250
|
-
|
251
|
-
def secrets_file
|
252
|
-
@locator.find 'secrets.yml'
|
253
|
-
rescue ::Invar::FileLocator::FileNotFoundError => e
|
254
|
-
warn <<~ERR
|
255
|
-
Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
|
256
|
-
#{ CREATE_SUGGESTION }
|
257
|
-
ERR
|
258
|
-
exit 1
|
259
|
-
end
|
260
|
-
|
261
|
-
def filename
|
262
|
-
'secrets.yml'
|
263
|
-
end
|
264
|
-
|
265
|
-
def read_encrypted_file(file_path, encryption_key:)
|
266
|
-
lockbox = build_lockbox(encryption_key)
|
267
|
-
lockbox.decrypt(file_path.binread)
|
268
|
-
end
|
269
|
-
|
270
|
-
def write_encrypted_file(file_path, encryption_key:, content:, permissions: nil)
|
271
|
-
lockbox = build_lockbox(encryption_key)
|
272
|
-
|
273
|
-
encrypted_data = lockbox.encrypt(content)
|
274
|
-
|
275
|
-
config_dir.mkpath
|
276
|
-
# TODO: replace File.opens with photo_path.binwrite(uri.data) once FakeFS can handle it
|
277
|
-
File.open(file_path.to_s, 'wb') { |f| f.write encrypted_data }
|
278
|
-
file_path.chmod permissions if permissions
|
279
|
-
|
280
|
-
warn "Saved file: #{ file_path }"
|
281
|
-
end
|
282
|
-
|
283
|
-
def edit_encrypted_file(file_path, content: nil)
|
284
|
-
encryption_key = determine_key(file_path)
|
285
|
-
|
286
|
-
content ||= invoke_editor(file_path, encryption_key: encryption_key)
|
287
|
-
|
288
|
-
write_encrypted_file(file_path, encryption_key: encryption_key, content: content)
|
289
|
-
end
|
290
|
-
|
291
|
-
def invoke_editor(file_path, encryption_key:)
|
292
|
-
Tempfile.create(file_path.basename.to_s) do |tmp_file|
|
293
|
-
decrypted = read_encrypted_file(file_path, encryption_key: encryption_key)
|
294
|
-
|
295
|
-
tmp_file.write(decrypted)
|
296
|
-
tmp_file.rewind # rewind needed because file does not get closed after write
|
297
|
-
system(ENV.fetch('EDITOR', 'editor'), tmp_file.path, exception: true)
|
298
|
-
tmp_file.read
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def determine_key(file_path)
|
303
|
-
encryption_key = Lockbox.master_key
|
304
|
-
|
305
|
-
if encryption_key.nil? && $stdin.respond_to?(:noecho)
|
306
|
-
warn "Enter master key to decrypt #{ file_path }:"
|
307
|
-
encryption_key = $stdin.noecho(&:gets).strip
|
308
|
-
end
|
309
|
-
|
310
|
-
encryption_key
|
311
|
-
end
|
312
|
-
|
313
|
-
def build_lockbox(encryption_key)
|
314
|
-
Lockbox.new(key: encryption_key)
|
315
|
-
rescue ArgumentError => e
|
316
|
-
raise SecretsFileEncryptionError, e
|
317
|
-
end
|
318
|
-
end
|
136
|
+
# Namespace module for task handler implementations
|
137
|
+
module Task
|
138
|
+
# Template config YAML file
|
139
|
+
CONFIG_TEMPLATE = SECRETS_TEMPLATE = <<~YML
|
140
|
+
---
|
141
|
+
YML
|
319
142
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
def show_paths
|
324
|
-
warn @locator.search_paths.join("\n")
|
325
|
-
end
|
326
|
-
end
|
143
|
+
CREATE_SUGGESTION = <<~SUGGESTION
|
144
|
+
Maybe you used the wrong namespace or need to create the file with bundle exec rake invar:init?
|
145
|
+
SUGGESTION
|
327
146
|
end
|
328
147
|
end
|
329
148
|
end
|
data/lib/invar/reality.rb
CHANGED
@@ -152,11 +152,9 @@ module Invar
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def resolve_key(pathname, locator, prompt)
|
155
|
-
|
156
|
-
|
157
|
-
read_keyfile(key_file)
|
155
|
+
read_keyfile locator.find pathname
|
158
156
|
rescue FileLocator::FileNotFoundError
|
159
|
-
if $stdin.
|
157
|
+
if $stdin.tty?
|
160
158
|
warn prompt
|
161
159
|
$stdin.noecho(&:gets).strip
|
162
160
|
else
|
data/lib/invar/scope.rb
CHANGED
@@ -25,12 +25,18 @@ module Invar
|
|
25
25
|
alias / fetch
|
26
26
|
alias [] fetch
|
27
27
|
|
28
|
-
def method_missing(
|
29
|
-
|
28
|
+
def method_missing(method_name, *args)
|
29
|
+
guard_test_methods method_name
|
30
30
|
|
31
31
|
super
|
32
32
|
end
|
33
33
|
|
34
|
+
def respond_to_missing?(method_name, include_all)
|
35
|
+
guard_test_methods method_name
|
36
|
+
|
37
|
+
super method_name, include_all
|
38
|
+
end
|
39
|
+
|
34
40
|
# Returns a hash representation of this scope and subscopes.
|
35
41
|
#
|
36
42
|
# @return [Hash] a hash representation of this scope
|
@@ -51,6 +57,10 @@ module Invar
|
|
51
57
|
|
52
58
|
private
|
53
59
|
|
60
|
+
def guard_test_methods(method_name)
|
61
|
+
raise ::Invar::ImmutableRealityError, ::Invar::ImmutableRealityError::PRETEND_MSG if method_name == :pretend
|
62
|
+
end
|
63
|
+
|
54
64
|
def known_keys
|
55
65
|
@data.keys.sort.collect { |k| ":#{ k }" }.join(', ')
|
56
66
|
end
|
data/lib/invar/version.rb
CHANGED
data/lib/invar.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Needed for TTY input handling
|
4
|
+
# Do not remove; it missing will not always break tests due to test environments requiring it themselves
|
5
|
+
require 'io/console'
|
6
|
+
|
3
7
|
require_relative 'invar/version'
|
4
8
|
require_relative 'invar/errors'
|
5
9
|
require_relative 'invar/reality'
|
@@ -15,12 +19,24 @@ module Invar
|
|
15
19
|
end
|
16
20
|
|
17
21
|
class << self
|
18
|
-
def method_missing(
|
19
|
-
|
20
|
-
raise ::Invar::ImmutableRealityError, ::Invar::ImmutableRealityError::HOOK_MSG
|
21
|
-
end
|
22
|
+
def method_missing(method_name)
|
23
|
+
guard_test_hooks method_name
|
22
24
|
|
23
25
|
super
|
24
26
|
end
|
27
|
+
|
28
|
+
def respond_to_missing?(method_name, include_all)
|
29
|
+
guard_test_hooks method_name
|
30
|
+
|
31
|
+
super method_name, include_all
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def guard_test_hooks(method_name)
|
37
|
+
return unless [:after_load, :clear_hooks].include? method_name
|
38
|
+
|
39
|
+
raise ::Invar::ImmutableRealityError, ::Invar::ImmutableRealityError::HOOK_MSG
|
40
|
+
end
|
25
41
|
end
|
26
42
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: invar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robin Miller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-schema
|
@@ -63,6 +63,10 @@ files:
|
|
63
63
|
- lib/invar/errors.rb
|
64
64
|
- lib/invar/file_locator.rb
|
65
65
|
- lib/invar/private_file.rb
|
66
|
+
- lib/invar/rake/task/config.rb
|
67
|
+
- lib/invar/rake/task/namespaced.rb
|
68
|
+
- lib/invar/rake/task/secrets.rb
|
69
|
+
- lib/invar/rake/task/status.rb
|
66
70
|
- lib/invar/rake/tasks.rb
|
67
71
|
- lib/invar/reality.rb
|
68
72
|
- lib/invar/scope.rb
|