2pass 2.0.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1236ce26a8191128890f5d302ae31ebe607f51dd6018bdbc1dd561f782909748
4
- data.tar.gz: b5b6cb1c22ce47f83df9a1f403ffce5628ae1d7a9d496b3ccbcc7c79f6a4726b
3
+ metadata.gz: 135196a393e121fb8b71d5295fdedc19f3bccb24885ce81cf630be59d2b7c64e
4
+ data.tar.gz: 7f5684e81780732115fcf6b9d5a071cfd1e8a05eaa48246e35f7fd3841a702ce
5
5
  SHA512:
6
- metadata.gz: da6f2bacdade151d6ab41d815aec44f4f21dbd5b14ee330a06a4bf34efcda2b1d7d7f1eefbf455058002ca524f1369e577245692f5329b6a2938348c5799a6c9
7
- data.tar.gz: 165926fcf9576c49b62a628beb4ec16cff3ea01a58d07854f88c5e7891d73c40af22406183dfbacd138794fb477c90094e8d208844eac9188adc828c6dd22765
6
+ metadata.gz: e1c997e62173b57c596adf21c9636bfd7ed3417a5f0611d497db1caa486a18e113ad37b234bec8b3334fcea282c5f99f2ec9dbd1b4e328222b1439f281629586
7
+ data.tar.gz: 9697b86fe839dc5d29f7786ab2e278986fbdb64bd2fc89e142f6e8bc812f919a66d3b57afcb97e71e099d806cc1411c56c12f5117facf6174ee780de71812b14
data/README.md CHANGED
@@ -39,9 +39,13 @@ Then build the gem and install it.
39
39
 
40
40
  ```sh
41
41
  2pass -h
42
- 2pass get <vault_name> <id>
43
- 2pass list <vault_name>
44
- 2pass add <vault_name> <id> <value>
42
+ 2pass get <vault_name> <id> [env]
43
+ 2pass list <vault_name> [env]
44
+ 2pass list <vault_name> [env] --json
45
+ 2pass list <vault_name> [env] --dotenv
46
+ 2pass add <vault_name> <id> <value> [env]
47
+ 2pass link <vault_name> <target_path> [env]
48
+ 2pass update
45
49
  ```
46
50
  You can also pass an optional environment to most commands:
47
51
  `2pass get myproject mykey production`
data/bin/2pass CHANGED
@@ -5,13 +5,14 @@ require_relative "../lib/2pass"
5
5
  def help_message
6
6
  puts <<~HELP
7
7
  Usage:
8
- 2pass add <vault_name> <id> <value> <env> - Add new secret. env is optional
9
- 2pass get <vault_name> <id> <env> - Get content by ID from the specified vault. env is optional
10
- 2pass list <vault_name> <env> - List the content of the specified vault as key=value pairs. env is optional
11
- 2pass list <vault_name> <env> --json - List the content of the specified vault as JSON. env is optional
12
- 2pass list <vault_name> <env> --dotenv - List the content of the specified vault as key=value pairs. env is optional
13
- 2pass link <vault_name> <target_path> <env> - Create a symlink for an existing vault. Useful when the vault is stored in a synced place (iCloud, Dropbox, etc.). env is optional
14
- 2pass -h - Display this help message
8
+ 2pass add <vault_name> <id> <value> [env] - Add new secret. env is optional
9
+ 2pass get <vault_name> <id> [env] - Get content by ID from the specified vault. env is optional
10
+ 2pass list <vault_name> [env] - List the content of the specified vault as key=value pairs. env is optional
11
+ 2pass list <vault_name> [env] --json - List the content of the specified vault as JSON. env is optional
12
+ 2pass list <vault_name> [env] --dotenv - List the content of the specified vault as key=value pairs. env is optional
13
+ 2pass link <vault_name> <target_path> [env] - Create a symlink for an existing vault. Useful when the vault is stored in a synced place (iCloud, Dropbox, etc.). env is optional
14
+ 2pass update - Update 2pass to the latest RubyGems release
15
+ 2pass -h - Display this help message
15
16
  HELP
16
17
  end
17
18
 
@@ -43,31 +44,35 @@ if options[:version]
43
44
  exit
44
45
  end
45
46
 
46
- if ARGV.length < 2
47
+ if ARGV.empty?
47
48
  help_message
48
49
  exit(1)
49
50
  end
50
51
 
51
- command, vault_name, *args = ARGV
52
+ command, *args = ARGV
52
53
 
53
54
  begin
54
55
  case command&.to_sym
55
56
  when :get
56
- if args.length < 1
57
+ if args.length < 2 || args.length > 3
57
58
  help_message
58
59
  exit(1)
59
60
  end
60
- id, env = args[0], args[1]
61
+ vault_name, id, env = args[0], args[1], args[2]
61
62
  puts TwoPass.get_secret(vault_name, id, env: env)
62
63
  when :add
63
- if args.length != 2
64
+ if args.length < 3 || args.length > 4
64
65
  help_message
65
66
  exit(1)
66
67
  end
67
- id, value, env = args[0], args[1], args[2]
68
+ vault_name, id, value, env = args[0], args[1], args[2], args[3]
68
69
  TwoPass.add_secret(vault_name, id, value, env: env)
69
70
  when :list
70
- env = args[0]
71
+ if args.length < 1 || args.length > 2
72
+ help_message
73
+ exit(1)
74
+ end
75
+ vault_name, env = args[0], args[1]
71
76
  if options[:format] == :json
72
77
  puts TwoPass.list_content(vault_name, env: env, format: :json)
73
78
  elsif options[:format] == :dotenv
@@ -75,18 +80,25 @@ begin
75
80
  else
76
81
  puts TwoPass.list_content(vault_name, env: env)
77
82
  end
78
- exit(1)
83
+ exit(0)
79
84
  when :link
80
- if args.length < 1
85
+ if args.length < 2 || args.length > 3
81
86
  help_message
82
87
  exit(1)
83
88
  end
84
- target_path, env = args[0], args[1]
89
+ vault_name, target_path, env = args[0], args[1], args[2]
85
90
  TwoPass.create_symlink(vault_name, target_path, env: env)
91
+ when :update
92
+ if args.any?
93
+ help_message
94
+ exit(1)
95
+ end
96
+ puts TwoPass.update_cli
86
97
  when :help
87
98
  help_message
88
99
  else
89
100
  help_message
101
+ exit(1)
90
102
  end
91
103
  rescue => e
92
104
  STDERR.puts e.message
data/lib/2pass/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module TwoPass
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
data/lib/2pass.rb CHANGED
@@ -4,6 +4,9 @@ require "optparse"
4
4
  require "json"
5
5
  require "yaml"
6
6
  require "fileutils"
7
+ require "net/http"
8
+ require "uri"
9
+ require "rubygems/version"
7
10
  require "terminal-table"
8
11
 
9
12
  require_relative "2pass/version"
@@ -15,13 +18,32 @@ module TwoPass
15
18
  def create_symlink(vault_name, target_path, env: nil)
16
19
  FileUtils.mkdir_p(VAULT_DIR)
17
20
  symlink_path = vault_file_path(vault_name, env: env)
21
+ expanded_target_path = File.expand_path(target_path)
22
+
23
+ unless File.exist?(expanded_target_path)
24
+ raise "Target vault file does not exist: #{expanded_target_path}"
25
+ end
26
+
27
+ unless File.readable?(expanded_target_path)
28
+ raise "Target vault file is not readable: #{expanded_target_path}"
29
+ end
30
+
31
+ if File.symlink?(symlink_path)
32
+ existing_target = File.expand_path(File.readlink(symlink_path), File.dirname(symlink_path))
33
+ if existing_target == expanded_target_path
34
+ puts "Symlink already exists: #{symlink_path} -> #{expanded_target_path}"
35
+ return
36
+ end
37
+
38
+ raise "A different symlink already exists at #{symlink_path}: #{existing_target}"
39
+ end
18
40
 
19
41
  if File.exist?(symlink_path)
20
- puts "Symlink already exists: #{symlink_path}"
21
- else
22
- File.symlink(target_path, symlink_path)
23
- puts "Created symlink: #{symlink_path} -> #{target_path}"
42
+ raise "A regular file already exists at #{symlink_path}. Remove it before creating a symlink."
24
43
  end
44
+
45
+ File.symlink(expanded_target_path, symlink_path)
46
+ puts "Created symlink: #{symlink_path} -> #{expanded_target_path}"
25
47
  end
26
48
 
27
49
  def save_vault(vault_name, data, env: nil)
@@ -81,6 +103,22 @@ module TwoPass
81
103
  end
82
104
  end
83
105
 
106
+ def update_cli
107
+ latest_version = fetch_latest_version
108
+ current = Gem::Version.new(VERSION)
109
+ latest = Gem::Version.new(latest_version)
110
+
111
+ if current >= latest
112
+ return "2pass is already up to date (#{VERSION})."
113
+ end
114
+
115
+ unless run_update_command
116
+ raise "Failed to update 2pass. Try running: #{update_command.join(' ')}"
117
+ end
118
+
119
+ "Updated 2pass from #{VERSION} to #{latest_version}. Restart your shell session if needed."
120
+ end
121
+
84
122
  private
85
123
 
86
124
  def load_vault(vault_name, env: nil)
@@ -88,6 +126,8 @@ module TwoPass
88
126
  return [] unless File.exist?(file_path)
89
127
 
90
128
  YAML.load_file(file_path, symbolize_names: true) || []
129
+ rescue Errno::EACCES, Errno::EPERM => e
130
+ raise permission_error_message(file_path, e)
91
131
  rescue Psych::SyntaxError => e
92
132
  STDERR.puts "Error parsing YAML file: #{e.message}"
93
133
  []
@@ -99,5 +139,55 @@ module TwoPass
99
139
  [vault_name, env].compact.join(".")
100
140
  ].join("/") + ".yml"
101
141
  end
142
+
143
+ def permission_error_message(file_path, error)
144
+ message = +"Unable to read vault file: #{file_path}\n#{error.class}: #{error.message}"
145
+ symlink_target = nil
146
+
147
+ if File.symlink?(file_path)
148
+ symlink_target = File.expand_path(File.readlink(file_path), File.dirname(file_path))
149
+ message << "\nSymlink target: #{symlink_target}"
150
+ end
151
+
152
+ path_to_check = symlink_target || file_path
153
+ if path_to_check.include?("/Library/Mobile Documents/")
154
+ message << "\nThe vault points to iCloud Drive. macOS may be blocking your terminal app."
155
+ message << "\nCheck System Settings > Privacy & Security > Files and Folders"
156
+ message << "\nfor your terminal app and enable iCloud Drive (and Full Disk Access if needed)."
157
+ end
158
+
159
+ message
160
+ rescue StandardError
161
+ "Unable to read vault file: #{file_path}\n#{error.class}: #{error.message}"
162
+ end
163
+
164
+ def fetch_latest_version
165
+ uri = URI("https://rubygems.org/api/v1/gems/2pass.json")
166
+ response = Net::HTTP.start(
167
+ uri.host,
168
+ uri.port,
169
+ use_ssl: true,
170
+ open_timeout: 5,
171
+ read_timeout: 5
172
+ ) do |http|
173
+ http.get(uri.request_uri)
174
+ end
175
+
176
+ unless response.is_a?(Net::HTTPSuccess)
177
+ raise "Unable to check latest version (HTTP #{response.code})"
178
+ end
179
+
180
+ JSON.parse(response.body).fetch("version")
181
+ rescue JSON::ParserError, KeyError => e
182
+ raise "Unable to parse latest version from RubyGems: #{e.message}"
183
+ end
184
+
185
+ def run_update_command
186
+ system(*update_command)
187
+ end
188
+
189
+ def update_command
190
+ [Gem.ruby, "-S", "gem", "update", "2pass"]
191
+ end
102
192
  end
103
193
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: 2pass
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Olivier
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-03-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -105,7 +104,6 @@ homepage: https://2pass.xyz
105
104
  licenses:
106
105
  - MIT
107
106
  metadata: {}
108
- post_install_message:
109
107
  rdoc_options: []
110
108
  require_paths:
111
109
  - lib
@@ -120,8 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
118
  - !ruby/object:Gem::Version
121
119
  version: '0'
122
120
  requirements: []
123
- rubygems_version: 3.5.11
124
- signing_key:
121
+ rubygems_version: 4.0.3
125
122
  specification_version: 4
126
123
  summary: A CLI app for managing secrets
127
124
  test_files: []