mysigner 0.3.3 → 0.3.4
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/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/exe/mysigner +9 -0
- data/lib/mysigner/build/android_executor.rb +11 -8
- data/lib/mysigner/build/detector.rb +18 -2
- data/lib/mysigner/cleanup/private_keys_purger.rb +5 -0
- data/lib/mysigner/cli/auth_commands.rb +20 -3
- data/lib/mysigner/cli/build_commands.rb +18 -7
- data/lib/mysigner/cli/concerns/helpers.rb +62 -0
- data/lib/mysigner/cli/resource_commands.rb +4 -0
- data/lib/mysigner/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8d828d29516e7df219e7a43cc21f66fe5dcfdce881f7ffb2168217626b6e204
|
|
4
|
+
data.tar.gz: 75444b456a228e57784a3b5376b57da4abe1963550b53b735d32836de3d0e981
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6aa433a444788a4205115f0165d4d264137921b6c06d00af81b4096272297141f1c275f848a798e8327e64d0151b50611ba7b313d7ceaa7a10ead7854a70863f
|
|
7
|
+
data.tar.gz: 18c7b935f4d59817d9286fbb95c9b9d21803c17930b74bb770fda57494c862a064764abfd3cd2000e5a2ec1c5738823d5df83021febc00adb90d2a39727db909
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to My Signer CLI will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.4] - 2026-06-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Smart dependency setup for Expo / React-Native projects: if `node_modules` is missing, `mysigner ship` / `android build` now detect the project's package manager from the lockfile (npm/yarn/pnpm/bun), suggest the EXACT install command, and offer to run it interactively — or auto-run with `--setup` / `MYSIGNER_AUTO_SETUP=1` for CI. It never silently installs (a wrong package manager can corrupt the lockfile).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- No more raw backtraces: a top-level safety net turns any unhandled error into one clean line (full trace under `DEBUG=1`). Specific crash fixes: `onboard --local-only` on a mistyped key/keystore path, `switch` on non-interactive input, and the startup legacy-key cleanup on a read-only HOME.
|
|
15
|
+
- On an Expo prebuild failure, the precise "run `<pm> install`" message is no longer followed by a generic, irrelevant "No Android Project Found → check build.gradle" checklist; that checklist now appears only when there is genuinely no Android project.
|
|
16
|
+
- React-Native pre-build dependency install now uses the project's own package manager (not a blind `npm install` that can corrupt a yarn/pnpm lockfile), shows its output, and fails loudly instead of dying later with a cryptic Gradle error.
|
|
17
|
+
|
|
8
18
|
## [0.3.3] - 2026-06-25
|
|
9
19
|
|
|
10
20
|
### Fixed
|
data/Gemfile.lock
CHANGED
data/exe/mysigner
CHANGED
|
@@ -108,4 +108,13 @@ rescue Errno::ENOENT => e
|
|
|
108
108
|
warn 'On Linux or Windows you can still build Android: mysigner ship internal --platform android'
|
|
109
109
|
warn e.full_message(highlight: false) if ENV['DEBUG']
|
|
110
110
|
exit 1
|
|
111
|
+
rescue StandardError => e
|
|
112
|
+
# Last-resort safety net: a published CLI should never dump a raw Ruby
|
|
113
|
+
# backtrace at the user. Turn any unhandled error into one clean line; show the
|
|
114
|
+
# trace only under DEBUG. (SystemExit / Interrupt are not StandardError, so
|
|
115
|
+
# normal `exit` and Ctrl-C still pass through untouched.)
|
|
116
|
+
warn "✗ Something went wrong: #{e.message}"
|
|
117
|
+
warn 'Run the same command with DEBUG=1 for details, or `mysigner doctor` to check your setup.'
|
|
118
|
+
warn e.full_message(highlight: false) if ENV['DEBUG']
|
|
119
|
+
exit 1
|
|
111
120
|
end
|
|
@@ -250,14 +250,17 @@ module Mysigner
|
|
|
250
250
|
system('npx cap sync android > /dev/null 2>&1')
|
|
251
251
|
end
|
|
252
252
|
when :react_native
|
|
253
|
-
# React Native:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
# React Native: install JS deps if missing. Use the project's OWN
|
|
254
|
+
# package manager (blindly running `npm install` on a yarn/pnpm project
|
|
255
|
+
# corrupts the lockfile), keep output visible, and fail loud — a silent
|
|
256
|
+
# install failure otherwise surfaces as a cryptic Gradle autolink error.
|
|
257
|
+
dir = @project_info[:directory]
|
|
258
|
+
unless File.exist?(File.join(dir, 'node_modules'))
|
|
259
|
+
require 'mysigner/build/detector'
|
|
260
|
+
cmd = Detector.install_command(Detector.detect_package_manager(dir))
|
|
261
|
+
puts "📦 Installing JavaScript dependencies (#{cmd})..."
|
|
262
|
+
ok = Dir.chdir(dir) { system(*cmd.split) }
|
|
263
|
+
raise BuildError, "`#{cmd}` failed — install dependencies manually, then re-run." unless ok
|
|
261
264
|
end
|
|
262
265
|
when :flutter
|
|
263
266
|
# Flutter: ensure dependencies are fetched
|
|
@@ -83,9 +83,9 @@ module Mysigner
|
|
|
83
83
|
# Fail fast (before shelling out) for the two common prebuild blockers.
|
|
84
84
|
def self.ensure_expo_prereqs!(directory, platform)
|
|
85
85
|
unless Dir.exist?("#{directory}/node_modules/expo")
|
|
86
|
+
install = install_command(detect_package_manager(directory))
|
|
86
87
|
raise NoProjectError, expo_prebuild_failed_message(
|
|
87
|
-
platform, hint:
|
|
88
|
-
'(or yarn/pnpm install) in the project first.'
|
|
88
|
+
platform, hint: "JavaScript dependencies aren't installed. Run `#{install}` in the project first."
|
|
89
89
|
)
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -106,6 +106,22 @@ module Mysigner
|
|
|
106
106
|
nil
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Detect the JS package manager from the lockfile so we suggest the EXACT
|
|
110
|
+
# install command for this project. Running the wrong one (e.g. `npm
|
|
111
|
+
# install` on a yarn/pnpm project) can corrupt the dependency tree, so we
|
|
112
|
+
# never guess blindly.
|
|
113
|
+
def self.detect_package_manager(directory)
|
|
114
|
+
return :yarn if File.exist?("#{directory}/yarn.lock")
|
|
115
|
+
return :pnpm if File.exist?("#{directory}/pnpm-lock.yaml")
|
|
116
|
+
return :bun if File.exist?("#{directory}/bun.lockb") || File.exist?("#{directory}/bun.lock")
|
|
117
|
+
|
|
118
|
+
:npm
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.install_command(pkg_manager)
|
|
122
|
+
{ yarn: 'yarn install', pnpm: 'pnpm install', bun: 'bun install' }.fetch(pkg_manager, 'npm install')
|
|
123
|
+
end
|
|
124
|
+
|
|
109
125
|
def self.expo_prebuild_failed_message(platform, hint: nil)
|
|
110
126
|
native = platform == :android ? 'Android' : 'iOS'
|
|
111
127
|
parts = ["Failed to generate #{native} project with expo prebuild."]
|
|
@@ -34,6 +34,11 @@ module Mysigner
|
|
|
34
34
|
|
|
35
35
|
FileUtils.mkdir_p(File.dirname(marker_path))
|
|
36
36
|
FileUtils.touch(marker_path)
|
|
37
|
+
rescue SystemCallError => e
|
|
38
|
+
# A read-only / unwritable HOME must never abort startup (this runs at
|
|
39
|
+
# require time, before any command). The purger is idempotent, so it
|
|
40
|
+
# simply retries next run. Surface only under verbose.
|
|
41
|
+
warn "mysigner: skipped legacy-key cleanup (#{e.class})" if ENV['MYSIGNER_VERBOSE'] == '1'
|
|
37
42
|
end
|
|
38
43
|
end
|
|
39
44
|
end
|
|
@@ -227,7 +227,16 @@ module Mysigner
|
|
|
227
227
|
# untouched for backward compatibility.
|
|
228
228
|
if local_only?
|
|
229
229
|
emit_local_only_banner
|
|
230
|
-
|
|
230
|
+
begin
|
|
231
|
+
return onboard_local_only
|
|
232
|
+
rescue LocalOnlyOnboardError => e
|
|
233
|
+
# Ordinary user mistakes (mistyped key path, empty alias, etc.)
|
|
234
|
+
# must read as a clean message, not a raw backtrace.
|
|
235
|
+
error e.message
|
|
236
|
+
say ''
|
|
237
|
+
say "Re-run 'mysigner --local-only onboard' once that's fixed.", :yellow
|
|
238
|
+
exit 1
|
|
239
|
+
end
|
|
231
240
|
end
|
|
232
241
|
|
|
233
242
|
say '🚀 My Signer Setup Guide', :cyan
|
|
@@ -971,7 +980,13 @@ module Mysigner
|
|
|
971
980
|
end
|
|
972
981
|
match
|
|
973
982
|
else
|
|
974
|
-
org_index = ask("Select organization (1-#{organizations_list.length}, or 'q' to cancel):")
|
|
983
|
+
org_index = ask("Select organization (1-#{organizations_list.length}, or 'q' to cancel):").to_s.strip
|
|
984
|
+
|
|
985
|
+
if org_index.empty?
|
|
986
|
+
say 'No selection. To switch non-interactively, pass an org ID:', :yellow
|
|
987
|
+
say ' mysigner switch <ID>', :cyan
|
|
988
|
+
return
|
|
989
|
+
end
|
|
975
990
|
|
|
976
991
|
if org_index.downcase == 'q'
|
|
977
992
|
say 'Cancelled', :yellow
|
|
@@ -1003,7 +1018,9 @@ module Mysigner
|
|
|
1003
1018
|
say ' 3. Paste it below'
|
|
1004
1019
|
say ''
|
|
1005
1020
|
|
|
1006
|
-
|
|
1021
|
+
# .to_s.strip so a non-tty/empty paste can't crash on nil.downcase
|
|
1022
|
+
# (and stray whitespace around a pasted token is trimmed).
|
|
1023
|
+
new_token = ask("Paste API token for '#{selected_org[:name]}' (or 'q' to cancel):", echo: false).to_s.strip
|
|
1007
1024
|
say ''
|
|
1008
1025
|
|
|
1009
1026
|
if new_token.downcase == 'q' || new_token.empty?
|
|
@@ -113,6 +113,8 @@ module Mysigner
|
|
|
113
113
|
# (e.g. https://appstoreconnect.apple.com/apps/<APPLE_APP_ID>/...).
|
|
114
114
|
method_option :apple_id, type: :string, banner: 'APPLE_APP_ID',
|
|
115
115
|
desc: 'App Store Connect app id (overrides bundleId lookup in --local-only mode)'
|
|
116
|
+
method_option :setup, type: :boolean, default: false,
|
|
117
|
+
desc: 'Auto-install missing JS dependencies (npm/yarn/pnpm) without prompting'
|
|
116
118
|
def ship(target)
|
|
117
119
|
ios_targets = %w[testflight appstore]
|
|
118
120
|
android_targets = %w[internal alpha beta production]
|
|
@@ -921,6 +923,10 @@ module Mysigner
|
|
|
921
923
|
config = load_config
|
|
922
924
|
client = create_client(config)
|
|
923
925
|
|
|
926
|
+
# Expo / React-Native projects need JS deps installed before the
|
|
927
|
+
# native build — offer to install them (or auto with --setup).
|
|
928
|
+
maybe_install_node_deps!(Dir.pwd)
|
|
929
|
+
|
|
924
930
|
overall_start = Time.now
|
|
925
931
|
timings = {}
|
|
926
932
|
aab_path = nil
|
|
@@ -1334,13 +1340,18 @@ module Mysigner
|
|
|
1334
1340
|
say ''
|
|
1335
1341
|
say "Error: #{e.message}", :red
|
|
1336
1342
|
say ''
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1343
|
+
# The framework-specific detector errors (Expo/Capacitor/RN/Flutter)
|
|
1344
|
+
# already name the exact fix; only show the generic "where's your
|
|
1345
|
+
# android project" checklist for the truly-generic case.
|
|
1346
|
+
if e.message.include?('No Android project found')
|
|
1347
|
+
say '💡 No Android Project Found: How to fix', :cyan
|
|
1348
|
+
say ''
|
|
1349
|
+
say " → Make sure you're in an Android project directory", :yellow
|
|
1350
|
+
say ' → Check for build.gradle or build.gradle.kts file', :yellow
|
|
1351
|
+
say ' → For React Native: cd android && check build.gradle exists', :yellow
|
|
1352
|
+
say ' → For Flutter: check android/app/build.gradle exists', :yellow
|
|
1353
|
+
say ''
|
|
1354
|
+
end
|
|
1344
1355
|
exit 1
|
|
1345
1356
|
rescue Upload::PlayStoreUploader::MissingLocalCredentialsError => e
|
|
1346
1357
|
# mysigner-43 — local-only requested but no credentials stored.
|
|
@@ -187,6 +187,68 @@ module Mysigner
|
|
|
187
187
|
exit 1
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
+
# For an Expo / React-Native project, the native android build can't run
|
|
191
|
+
# until JS dependencies are installed. Rather than failing deep inside
|
|
192
|
+
# `expo prebuild`, detect it up front and: auto-run with --setup /
|
|
193
|
+
# MYSIGNER_AUTO_SETUP, OR offer to run it interactively, OR print the
|
|
194
|
+
# EXACT install command for this project's package manager and exit.
|
|
195
|
+
# We never silently run a package install (it's slow and the wrong
|
|
196
|
+
# manager can corrupt the lockfile) — only with consent or an opt-in.
|
|
197
|
+
def maybe_install_node_deps!(project_dir = Dir.pwd)
|
|
198
|
+
require 'json'
|
|
199
|
+
require 'mysigner/build/detector'
|
|
200
|
+
|
|
201
|
+
pkg_path = File.join(project_dir, 'package.json')
|
|
202
|
+
return unless File.exist?(pkg_path)
|
|
203
|
+
return if Dir.exist?(File.join(project_dir, 'node_modules'))
|
|
204
|
+
|
|
205
|
+
pkg = begin
|
|
206
|
+
JSON.parse(File.read(pkg_path))
|
|
207
|
+
rescue StandardError
|
|
208
|
+
{}
|
|
209
|
+
end
|
|
210
|
+
deps = {}
|
|
211
|
+
deps.merge!(pkg['dependencies']) if pkg['dependencies'].is_a?(Hash)
|
|
212
|
+
deps.merge!(pkg['devDependencies']) if pkg['devDependencies'].is_a?(Hash)
|
|
213
|
+
# Only Expo / React-Native projects need node deps to produce a native
|
|
214
|
+
# Android build; a plain native/Flutter project does not.
|
|
215
|
+
return unless deps.key?('expo') || deps.key?('react-native')
|
|
216
|
+
|
|
217
|
+
cmd = Mysigner::Build::Detector.install_command(
|
|
218
|
+
Mysigner::Build::Detector.detect_package_manager(project_dir)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
auto = setup_requested?
|
|
222
|
+
auto ||= $stdin.tty? && yes_with_default?(
|
|
223
|
+
"JavaScript dependencies aren't installed (no node_modules). Run `#{cmd}` now?", :cyan
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
unless auto
|
|
227
|
+
error "JavaScript dependencies aren't installed (no node_modules)."
|
|
228
|
+
say 'Install them first, then re-run:', :yellow
|
|
229
|
+
say " #{cmd}", :cyan
|
|
230
|
+
say '(or pass --setup / set MYSIGNER_AUTO_SETUP=1 to let mysigner run it for you)', :yellow
|
|
231
|
+
exit 1
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
say "📦 Installing JavaScript dependencies: #{cmd}", :cyan
|
|
235
|
+
ok = Dir.chdir(project_dir) { system(*cmd.split) }
|
|
236
|
+
unless ok
|
|
237
|
+
error "`#{cmd}` failed — install dependencies manually, then re-run."
|
|
238
|
+
exit 1
|
|
239
|
+
end
|
|
240
|
+
say '✓ Dependencies installed.', :green
|
|
241
|
+
say ''
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Whether the user opted into automatic setup (package install / prebuild)
|
|
245
|
+
# via the --setup flag or MYSIGNER_AUTO_SETUP=1. Commands without a
|
|
246
|
+
# --setup option still honour the env var.
|
|
247
|
+
def setup_requested?
|
|
248
|
+
flag = respond_to?(:options) && options.respond_to?(:[]) ? options[:setup] : nil
|
|
249
|
+
flag == true || ENV['MYSIGNER_AUTO_SETUP'] == '1'
|
|
250
|
+
end
|
|
251
|
+
|
|
190
252
|
# Local-only mode is active when any of, in precedence order:
|
|
191
253
|
# 1. --local-only / --no-local-only flag on this invocation
|
|
192
254
|
# 2. MYSIGNER_LOCAL_ONLY env var
|
|
@@ -1647,6 +1647,10 @@ module Mysigner
|
|
|
1647
1647
|
project_dir = Dir.pwd
|
|
1648
1648
|
is_expo = expo_project?(project_dir)
|
|
1649
1649
|
|
|
1650
|
+
# Expo / React-Native: offer to install JS deps (or auto with
|
|
1651
|
+
# --setup / MYSIGNER_AUTO_SETUP) before the native build.
|
|
1652
|
+
maybe_install_node_deps!(project_dir)
|
|
1653
|
+
|
|
1650
1654
|
# For Expo, we may need to regenerate android folder with correct versionCode
|
|
1651
1655
|
# Get package name from app.json first if Expo
|
|
1652
1656
|
if is_expo
|
data/lib/mysigner/version.rb
CHANGED