kbsecret 1.2.0 → 1.3.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/bin/kbsecret +6 -14
  4. data/lib/kbsecret/cli/command/abstract.rb +51 -0
  5. data/lib/kbsecret/cli/command/conf.rb +42 -0
  6. data/lib/kbsecret/cli/command/cp.rb +54 -0
  7. data/lib/kbsecret/cli/command/dump_fields.rb +53 -0
  8. data/lib/kbsecret/cli/command/env.rb +66 -0
  9. data/lib/kbsecret/cli/command/generator.rb +62 -0
  10. data/lib/kbsecret/cli/command/generators.rb +37 -0
  11. data/lib/kbsecret/cli/command/list.rb +48 -0
  12. data/lib/kbsecret/cli/command/login.rb +66 -0
  13. data/lib/kbsecret/cli/command/new.rb +82 -0
  14. data/lib/kbsecret/cli/command/pass.rb +51 -0
  15. data/lib/kbsecret/cli/command/raw_edit.rb +46 -0
  16. data/lib/kbsecret/cli/command/rm.rb +58 -0
  17. data/lib/kbsecret/cli/command/session.rb +128 -0
  18. data/lib/kbsecret/cli/command/sessions.rb +47 -0
  19. data/lib/kbsecret/cli/command/stash_file.rb +62 -0
  20. data/lib/kbsecret/cli/command/todo.rb +78 -0
  21. data/lib/kbsecret/cli/command.rb +45 -0
  22. data/lib/kbsecret/cli.rb +6 -4
  23. data/lib/kbsecret/config.rb +4 -3
  24. data/lib/kbsecret/record/abstract.rb +2 -2
  25. data/lib/kbsecret/record.rb +9 -7
  26. data/lib/kbsecret/version.rb +1 -1
  27. metadata +22 -20
  28. data/lib/kbsecret/cli/kbsecret-conf +0 -31
  29. data/lib/kbsecret/cli/kbsecret-cp +0 -44
  30. data/lib/kbsecret/cli/kbsecret-dump-fields +0 -39
  31. data/lib/kbsecret/cli/kbsecret-env +0 -53
  32. data/lib/kbsecret/cli/kbsecret-generator +0 -42
  33. data/lib/kbsecret/cli/kbsecret-generators +0 -28
  34. data/lib/kbsecret/cli/kbsecret-list +0 -36
  35. data/lib/kbsecret/cli/kbsecret-login +0 -53
  36. data/lib/kbsecret/cli/kbsecret-new +0 -68
  37. data/lib/kbsecret/cli/kbsecret-pass +0 -37
  38. data/lib/kbsecret/cli/kbsecret-raw-edit +0 -31
  39. data/lib/kbsecret/cli/kbsecret-rm +0 -44
  40. data/lib/kbsecret/cli/kbsecret-session +0 -105
  41. data/lib/kbsecret/cli/kbsecret-sessions +0 -38
  42. data/lib/kbsecret/cli/kbsecret-stash-file +0 -48
  43. data/lib/kbsecret/cli/kbsecret-todo +0 -47
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "clipboard"
4
+
5
+ module KBSecret
6
+ class CLI
7
+ module Command
8
+ # The implementation of `kbsecret pass`.
9
+ class Pass < Abstract
10
+ def initialize(argv)
11
+ super(argv) do |cli|
12
+ cli.slop do |o|
13
+ o.banner = <<~HELP
14
+ Usage:
15
+ kbsecret pass [options] <record>
16
+ HELP
17
+
18
+ o.string "-s", "--session", "the session to search in", default: :default
19
+ o.bool "-c", "--clipboard", "dump the password in the clipboard"
20
+ end
21
+
22
+ cli.dreck do
23
+ string :label
24
+ end
25
+
26
+ cli.ensure_session!
27
+ end
28
+ end
29
+
30
+ # @see Command::Abstract#setup!
31
+ def setup!
32
+ @record = cli.session[cli.args[:label]]
33
+ end
34
+
35
+ # @see Command::Abstract#validate!
36
+ def validate!
37
+ cli.die "No such login record." unless @record && @record.type == :login
38
+ end
39
+
40
+ # @see Command::Abstract#run!
41
+ def run!
42
+ if cli.opts.clipboard?
43
+ Clipboard.copy @record.password
44
+ else
45
+ puts @record.password
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret raw-edit`.
7
+ class RawEdit < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret raw-edit [options] <record>
14
+ HELP
15
+
16
+ o.string "-s", "--session", "the session to search in", default: :default
17
+ end
18
+
19
+ cli.dreck do
20
+ string :label
21
+ end
22
+
23
+ cli.ensure_session!
24
+ end
25
+ end
26
+
27
+ # @see Command::Abstract#setup!
28
+ def setup!
29
+ @record = cli.session[cli.args[:label]]
30
+ end
31
+
32
+ # @see Command::Abstract#validate!
33
+ def validate!
34
+ cli.die "Missing $EDITOR." unless ENV["EDITOR"]
35
+ cli.die "No such record." unless @record
36
+ end
37
+
38
+ # @see Command::Abstract#run!
39
+ def run!
40
+ Process.spawn("#{ENV["EDITOR"]} #{@record.path}")
41
+ @record.sync! # just to bump the timestamp
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+
5
+ module KBSecret
6
+ class CLI
7
+ module Command
8
+ # The implementation of `kbsecret rm`.
9
+ class Rm < Abstract
10
+ def initialize(argv)
11
+ super(argv) do |cli|
12
+ cli.slop do |o|
13
+ o.banner = <<~HELP
14
+ Usage:
15
+ kbsecret rm [options] <record [record ...]>
16
+ HELP
17
+
18
+ o.string "-s", "--session", "the session containing the record", default: :default
19
+ o.bool "-i", "--interactive", "ask for confirmation before deleting"
20
+ end
21
+
22
+ cli.dreck do
23
+ list :string, :labels
24
+ end
25
+
26
+ cli.ensure_session!
27
+ end
28
+ end
29
+
30
+ # @see Command::Abstract#setup!
31
+ def setup!
32
+ @selected_records = cli.session.records.select do |record|
33
+ cli.args[:labels].include? record.label
34
+ end
35
+ end
36
+
37
+ # @see Command::Abstract#validate!
38
+ def validate!
39
+ cli.die "No such record(s)." if @selected_records.empty?
40
+ end
41
+
42
+ # @see Command::Abstract#run!
43
+ def run!
44
+ $VERBOSE = nil # tty-prompt blasts us with irrelevant warnings on 2.4
45
+
46
+ tty = TTY::Prompt.new
47
+
48
+ confirm = if cli.opts.interactive?
49
+ tty.yes?("Delete '#{selected_records.join(", ")}'?")
50
+ else true
51
+ end
52
+
53
+ @selected_records.each { |r| cli.session.delete_record(r.label) } if confirm
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret session`.
7
+ class Session < Abstract
8
+ # The list of subcommands supported by `kbsecret session`.
9
+ SUBCOMMANDS = %w[new rm].freeze
10
+
11
+ def initialize(argv)
12
+ super(argv) do |cli|
13
+ cli.slop cmds: SUBCOMMANDS do |o|
14
+ o.banner = <<~HELP
15
+ Usage:
16
+ kbsecret session [options] <new|rm> <label>
17
+ HELP
18
+
19
+ o.string "-t", "--team", "the team to create the session under"
20
+ o.array "-u", "--users", "the Keybase users", default: [Keybase::Local.current_user]
21
+ o.string "-r", "--root", "the secret root directory"
22
+ o.bool "-c", "--create-team", "create the Keybase team if it does not exist"
23
+ o.bool "-f", "--force", "force creation (ignore overwrites, etc.)"
24
+ o.bool "-n", "--no-notify", "do not send a notification to session members"
25
+ o.bool "-d", "--delete", "unlink the session in addition to deconfiguration"
26
+ end
27
+
28
+ cli.dreck do
29
+ string :command
30
+ string :session
31
+ end
32
+
33
+ cli.ensure_session! :argument if cli.args[:command] == "rm"
34
+ end
35
+ end
36
+
37
+ # @see Command::Abstract#setup!
38
+ def setup!
39
+ @label = cli.args[:session]
40
+ @subcmd = cli.args[:command]
41
+ end
42
+
43
+ # @see Command::Abstract#validate!
44
+ def validate!
45
+ cli.die "Unknown subcommand: #{@subcmd}." unless SUBCOMMANDS.include?(@subcmd)
46
+ end
47
+
48
+ # @see Command::Abstract#run!
49
+ def run!
50
+ case @subcmd
51
+ when "new"
52
+ new_session
53
+ when "rm"
54
+ rm_session
55
+ end
56
+ end
57
+
58
+ # @api private
59
+ # @return [void]
60
+ def new_session
61
+ if Config.session?(@label) && !cli.opts.force?
62
+ cli.die "Refusing to overwrite a session without --force."
63
+ end
64
+
65
+ if cli.opts[:team]
66
+ teams = Keybase::Local::Team.list_self_memberships.teams
67
+
68
+ unless teams.map(&:fq_name).include?(cli.opts[:team])
69
+ if cli.opts.create_team?
70
+ cli.guard do
71
+ Keybase::Local::Team.create_team cli.opts[:team]
72
+ Keybase::Local::Team.add_members cli.opts[:team], users: [{
73
+ username: Keybase::Local.current_user,
74
+ role: "admin",
75
+ }]
76
+ end
77
+ else
78
+ cli.die "No such team (either nonexistent or non-member)."
79
+ end
80
+ end
81
+
82
+ Config.configure_session(@label, team: cli.opts[:team], root: @label)
83
+ else
84
+ cli.die "Missing `-r', `--root' option." unless cli.opts[:root]
85
+
86
+ cli.opts[:users].each do |user|
87
+ cli.die "Nonexistent Keybase user: #{user}." unless Keybase::API.user? user
88
+ end
89
+
90
+ unless cli.opts[:users].include? Keybase::Local.current_user
91
+ cli.warn "You didn't include yourself in the user list, but I'll add you."
92
+ cli.opts[:users] << Keybase::Local.current_user
93
+ end
94
+
95
+ Config.configure_session(@label, users: cli.opts[:users], root: cli.opts[:root])
96
+
97
+ unless cli.opts.no_notify? && cli.opts[:users] != [Keybase::Local.current_user]
98
+ users = cli.opts[:users].join(",")
99
+
100
+ Keybase::Local::Chat.send_message cli.opts[:users], <<~MESSAGE
101
+ You've been added to a KBSecret session!
102
+
103
+ To access this session, please run the following:
104
+
105
+ ```
106
+ $ kbsecret session new -r '#{cli.opts[:root]}' -u #{users} <label>
107
+ ```
108
+
109
+ If you don't have KBSecret installed, you can install it from `gem`:
110
+
111
+ ```
112
+ $ gem install kbsecret
113
+ ```
114
+ MESSAGE
115
+ end
116
+ end
117
+ end
118
+
119
+ # @api private
120
+ # @return [void]
121
+ def rm_session
122
+ cli.session.unlink! if cli.opts.delete?
123
+ Config.deconfigure_session @label
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret sessions`.
7
+ class Sessions < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret sessions [options]
14
+ HELP
15
+
16
+ o.bool "-a", "--show-all", "show each session in depth (i.e. metadata)"
17
+ end
18
+ end
19
+ end
20
+
21
+ # @see Command::Abstract#run!
22
+ def run!
23
+ Config.session_labels.each do |sess_name|
24
+ session_hash = Config.session(sess_name)
25
+ session = cli.guard { Session[sess_name] }
26
+
27
+ puts sess_name
28
+
29
+ next unless cli.opts.show_all?
30
+
31
+ if session_hash[:team]
32
+ puts <<~DETAIL
33
+ \tTeam: #{session_hash[:team]}
34
+ \tSecrets root: #{session_hash[:root]} (#{session.path})
35
+ DETAIL
36
+ else
37
+ puts <<~DETAIL
38
+ \tUsers: #{session_hash[:users].join(", ")}
39
+ \tSecrets root: #{session_hash[:root]} (#{session.path})
40
+ DETAIL
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module KBSecret
6
+ class CLI
7
+ module Command
8
+ # The implementation of `kbsecret stash-file`.
9
+ class StashFile < Abstract
10
+ def initialize(argv)
11
+ super(argv) do |cli|
12
+ cli.slop do |o|
13
+ o.banner = <<~HELP
14
+ Usage:
15
+ kbsecret stash-file <record> [file]
16
+ HELP
17
+
18
+ o.string "-s", "--session", "the session to add to", default: :default
19
+ o.bool "-f", "--force", "force creation (ignore overwrites, etc.)"
20
+ o.bool "-b", "--base64", "encode the file as base64"
21
+ end
22
+
23
+ cli.dreck do
24
+ string :label
25
+ string :filename
26
+ end
27
+
28
+ cli.ensure_session!
29
+ end
30
+ end
31
+
32
+ # @see Command::Abstract#setup!
33
+ def setup!
34
+ @label = cli.args[:label]
35
+ @filename = cli.args[:filename]
36
+ end
37
+
38
+ # @see Command::Abstract#validate!
39
+ def validate!
40
+ if cli.session.record?(@label) && !cli.opts.force?
41
+ cli.die "Refusing to overwrite a record without --force."
42
+ end
43
+
44
+ cli.die "No such file." unless File.file?(@filename) || @filename == "-"
45
+ end
46
+
47
+ # @see Command::Abstract#run!
48
+ def run!
49
+ contents = if @filename == "-"
50
+ STDIN.read
51
+ elsif File.file?(@filename)
52
+ File.read(@filename)
53
+ end
54
+
55
+ contents = Base64.encode64(contents) if cli.opts.base64?
56
+
57
+ cli.guard { cli.session.add_record(:unstructured, @label, contents) }
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret todo`.
7
+ class Todo < Abstract
8
+ # The list of subcommands supported by `kbsecret todo`.
9
+ SUBCOMMANDS = %w[start suspend complete].freeze
10
+
11
+ def initialize(argv)
12
+ super(argv) do |cli|
13
+ cli.slop cmds: SUBCOMMANDS do |o|
14
+ o.banner = <<~HELP
15
+ Usage:
16
+ kbsecret todo <start|suspend|complete> <record>
17
+ HELP
18
+
19
+ o.string "-s", "--session", "the session to search in", default: :default
20
+ end
21
+
22
+ cli.dreck do
23
+ string :command
24
+ string :label
25
+ end
26
+
27
+ cli.ensure_session!
28
+ end
29
+ end
30
+
31
+ # @see Command::Abstract#setup!
32
+ def setup!
33
+ @todo = cli.session[cli.args[:label]]
34
+ @subcmd = cli.args[:command]
35
+ end
36
+
37
+ # @see Command::Abstract#validate!
38
+ def validate!
39
+ cli.die "No such todo record:." unless @todo && @todo.type == :todo
40
+ cli.die "Unknown subcommand: #{@subcmd}." unless SUBCOMMANDS.include?(@subcmd)
41
+ end
42
+
43
+ # @see Command::Abstract#setup!
44
+ def run!
45
+ case @subcmd
46
+ when "start" then start_todo
47
+ when "suspend" then suspend_todo
48
+ when "complete" then complete_todo
49
+ end
50
+ end
51
+
52
+ # Starts the todo associated with the current invocation, unless already started.
53
+ # @return [void]
54
+ def start_todo
55
+ cli.die "That task is already started!" if @todo.started?
56
+ @todo.start!
57
+ puts "#{@todo.label}: '#{@todo.todo}' marked as started at #{@todo.start}"
58
+ end
59
+
60
+ # Suspends the todo associated with the current invocation, unless already suspended.
61
+ # @return [void]
62
+ def suspend_todo
63
+ cli.die "That task is already suspended!" if @todo.suspended?
64
+ @todo.suspend!
65
+ puts "#{@todo.label}: '#{@todo.todo}' marked as suspended at #{@todo.stop}"
66
+ end
67
+
68
+ # Completes the todo associated with the current invocation, unless already completed.
69
+ # @return [void]
70
+ def complete_todo
71
+ cli.die "That task is already completed!" if @todo.completed?
72
+ @todo.complete!
73
+ puts "#{@todo.label}: '#{@todo.todo}' marked as completed at #{@todo.stop}"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # we have to require abstract first because ruby's module resolution is bad
4
+ require_relative "command/abstract"
5
+ Dir[File.join(__dir__, "command/*.rb")].each { |t| require_relative t }
6
+
7
+ module KBSecret
8
+ class CLI
9
+ # The namespace for {KBSecret}'s internal commands.
10
+ module Command
11
+ module_function
12
+
13
+ # @return [Array<Class>] the class objects of all non-abstract commands
14
+ def command_classes
15
+ klasses = constants.map(&Command.method(:const_get)).grep(Class)
16
+ klasses.delete(Command::Abstract)
17
+ klasses
18
+ end
19
+
20
+ # @return [Array<String>] the CLI-friendly names of all commands
21
+ def command_names
22
+ command_classes.map(&:command_name)
23
+ end
24
+
25
+ # @param command_name [String] the CLI-friendly name of the command
26
+ # @return [Class, nil] the command class corresponding to the given name, or `nil`
27
+ def command_for(command_name)
28
+ klass = command_classes.find { |c| c.command_name == command_name }
29
+ # TODO: raise here if nil?
30
+ klass
31
+ end
32
+
33
+ # @param command_name [String] the CLI-friendly name of the command to run
34
+ # @param args [Array<String>] the arguments, if any, to pass to the command
35
+ # @return [void]
36
+ def run!(command_name, *args)
37
+ klass = command_for command_name
38
+ cmd = klass.new(args)
39
+ cmd.setup!
40
+ cmd.validate!
41
+ cmd.run!
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/kbsecret/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "cli/command"
4
+
3
5
  require "slop"
4
6
  require "dreck"
5
7
  require "abbrev"
@@ -47,14 +49,14 @@ module KBSecret
47
49
  #
48
50
  # cmd.opts # => Slop::Result
49
51
  # cmd.args # => Dreck::Result
50
- def self.create(&block)
51
- CLI.new(&block)
52
+ def self.create(argv = ARGV, &block)
53
+ CLI.new(argv, &block)
52
54
  end
53
55
 
54
56
  # @api private
55
57
  # @deprecated see {create}
56
- def initialize
57
- @argv = ARGV.dup
58
+ def initialize(argv = ARGV)
59
+ @argv = argv.dup
58
60
  guard { yield self }
59
61
  end
60
62
 
@@ -65,7 +65,7 @@ module KBSecret
65
65
  # Writes the user's configuration to disk.
66
66
  # @return [void]
67
67
  def self.sync!
68
- File.open(CONFIG_FILE, "w") { |io| io.write @config.to_yaml }
68
+ File.write(CONFIG_FILE, @config.to_yaml)
69
69
  end
70
70
 
71
71
  # Retrieve a configured value.
@@ -167,8 +167,6 @@ module KBSecret
167
167
  user_config = if File.exist?(CONFIG_FILE)
168
168
  YAML.load_file(CONFIG_FILE)
169
169
  else
170
- FileUtils.mkdir_p CONFIG_DIR
171
- FileUtils.mkdir_p CUSTOM_TYPES_DIR
172
170
  DEFAULT_CONFIG
173
171
  end
174
172
 
@@ -182,6 +180,9 @@ module KBSecret
182
180
  @config[:sessions].merge!(DEFAULT_SESSION)
183
181
  @config[:generators].merge!(DEFAULT_GENERATOR)
184
182
 
183
+ FileUtils.mkdir_p CONFIG_DIR
184
+ FileUtils.mkdir_p CUSTOM_TYPES_DIR
185
+
185
186
  sync!
186
187
  end
187
188
  end
@@ -5,7 +5,7 @@ require "forwardable"
5
5
 
6
6
  module KBSecret
7
7
  module Record
8
- # Represents an abstract kbsecret record that can be subclassed to produce
8
+ # Represents an abstract {KBSecret} record that can be subclassed to produce
9
9
  # more useful records.
10
10
  # @abstract
11
11
  class Abstract
@@ -61,7 +61,7 @@ module KBSecret
61
61
  @timestamp = Time.now.to_i
62
62
  sync!
63
63
  end
64
- ]
64
+ ], __FILE__, __LINE__ - 10
65
65
  end
66
66
 
67
67
  # @param field [Symbol] the field's name
@@ -12,22 +12,24 @@ Dir[File.join(__dir__, "record/*.rb")].each { |t| require_relative t }
12
12
  module KBSecret
13
13
  # The namespace for {KBSecret} record types.
14
14
  module Record
15
+ module_function
16
+
15
17
  # @return [Array<Class>] the class objects of all non-abstract record types
16
- def self.record_classes
18
+ def record_classes
17
19
  klasses = constants.map(&Record.method(:const_get)).grep(Class)
18
20
  klasses.delete(Record::Abstract)
19
21
  klasses
20
22
  end
21
23
 
22
24
  # @return [Array<Symbol>] the types of all records
23
- def self.record_types
25
+ def record_types
24
26
  record_classes.map(&:type)
25
27
  end
26
28
 
27
29
  # @param type [String, Symbol] the record type
28
30
  # @return [Class] the record class corresponding to the given type
29
31
  # @raise [Exceptions::RecordTypeUnknownError] if the requested type is unknown
30
- def self.class_for(type)
32
+ def class_for(type)
31
33
  klass = record_classes.find { |c| c.type == type.to_sym }
32
34
  raise Exceptions::RecordTypeUnknownError, type unless klass
33
35
  klass
@@ -35,7 +37,7 @@ module KBSecret
35
37
 
36
38
  # @param type [String, Symbol] the record type
37
39
  # @return [Boolean] whether a record class exists of the given type
38
- def self.type?(type)
40
+ def type?(type)
39
41
  return false unless type
40
42
  record_types.include?(type.to_sym)
41
43
  end
@@ -46,11 +48,11 @@ module KBSecret
46
48
  # @return [Record::AbstractRecord] the loaded record
47
49
  # @raise [Exceptions::RecordLoadError] if an error occurs during record loading
48
50
  # @api private
49
- def self.load_record!(session, path)
51
+ def load_record!(session, path)
50
52
  hsh = JSON.parse(File.read(path), symbolize_names: true)
51
53
  klass = class_for hsh[:type]
52
- klass&.load!(session, hsh)
53
- rescue JSON::JSONError
54
+ klass.load!(session, hsh)
55
+ rescue Exceptions::RecordTypeUnknownError, JSON::JSONError
54
56
  raise Exceptions::RecordLoadError, path
55
57
  end
56
58
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module KBSecret
4
4
  # kbsecret's current version
5
- VERSION = "1.2.0"
5
+ VERSION = "1.3.0.pre.1"
6
6
  end