mysigner 0.3.1 → 0.3.2

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.
@@ -1393,6 +1393,8 @@ module Mysigner
1393
1393
 
1394
1394
  mysigner android build
1395
1395
  Build an AAB file for upload to Google Play Console.
1396
+ (An AAB — Android App Bundle, .aab — is the format Google Play
1397
+ requires; the CLI builds and signs it for you, not an APK.)
1396
1398
  Use this for your FIRST upload (required before mysigner ship works).
1397
1399
 
1398
1400
  mysigner android list
@@ -1731,8 +1733,12 @@ module Mysigner
1731
1733
  if keystore_info
1732
1734
  say "🔐 Keystore: #{keystore_info[:name]}", :green
1733
1735
  else
1734
- say '⚠️ No keystore configured - will use debug signing', :yellow
1735
- say " Run 'mysigner android init' to set up release signing", :yellow
1736
+ say '⚠️ No release keystore configured building a DEBUG-SIGNED AAB.', :yellow
1737
+ say ' A debug-signed AAB is fine for local install/testing but Google', :yellow
1738
+ say ' Play will REJECT it on upload. To produce a publishable build:', :yellow
1739
+ say ' • Vault mode: mysigner android init (set up release signing)', :yellow
1740
+ say ' • Local-only mode: pass --keystore-path / --keystore-password to', :yellow
1741
+ say ' `mysigner ship … --platform android`', :yellow
1736
1742
  end
1737
1743
  say ''
1738
1744
  say '⏱️ This may take a few minutes...', :yellow
@@ -1778,16 +1784,21 @@ module Mysigner
1778
1784
  say ' mysigner ship internal --platform android', :green
1779
1785
  say ''
1780
1786
 
1781
- # Open the folder containing the AAB
1782
- aab_dir = File.dirname(aab_path)
1783
- say '📂 Opening folder...', :yellow
1784
- case RUBY_PLATFORM
1785
- when /darwin/
1786
- system('open', aab_dir)
1787
- when /linux/
1788
- system('xdg-open', aab_dir)
1789
- when /mingw|mswin/
1790
- system('explorer', aab_dir.gsub('/', '\\'))
1787
+ # Open the folder containing the AAB — only in an interactive
1788
+ # terminal. On headless / CI / SSH there is no file manager, and
1789
+ # xdg-open/explorer would spew errors AFTER a successful build.
1790
+ if $stdout.tty?
1791
+ aab_dir = File.dirname(aab_path)
1792
+ opener = case RUBY_PLATFORM
1793
+ when /darwin/ then 'open'
1794
+ when /linux/ then 'xdg-open'
1795
+ when /mingw|mswin/ then 'explorer'
1796
+ end
1797
+ if opener
1798
+ say '📂 Opening folder...', :yellow
1799
+ target = opener == 'explorer' ? aab_dir.gsub('/', '\\') : aab_dir
1800
+ system(opener, target, %i[out err] => File::NULL)
1801
+ end
1791
1802
  end
1792
1803
  rescue Build::Detector::NoProjectError => e
1793
1804
  error e.message
@@ -1899,12 +1910,19 @@ module Mysigner
1899
1910
  # Write modified app.json
1900
1911
  File.write(app_json_path, JSON.pretty_generate(config))
1901
1912
 
1902
- begin
1903
- # Delete existing android folder
1904
-
1905
- FileUtils.rm_rf(android_dir)
1913
+ # Back up an existing android/ so a failed prebuild can't destroy a
1914
+ # committed or hand-edited native project — restored on any failure.
1915
+ backup_dir = nil
1916
+ if Dir.exist?(android_dir)
1917
+ # pid + timestamp so a crashed prior run (same pid reused) can't
1918
+ # collide and have its backup wiped by the rm_rf below.
1919
+ backup_dir = "#{android_dir}.mysigner-bak-#{Process.pid}-#{Time.now.to_i}"
1920
+ FileUtils.rm_rf(backup_dir)
1921
+ FileUtils.mv(android_dir, backup_dir)
1922
+ end
1906
1923
 
1907
- # Run expo prebuild
1924
+ begin
1925
+ # Regenerate the native android/ from the version-bumped config.
1908
1926
  Dir.chdir(project_dir) do
1909
1927
  success = system('npx', 'expo', 'prebuild', '--platform', 'android', '--clean')
1910
1928
  raise 'expo prebuild failed' unless success
@@ -1919,8 +1937,28 @@ module Mysigner
1919
1937
  File.expand_path('~/Library/Android/sdk')
1920
1938
  File.write(local_props_path, "sdk.dir=#{sdk_path}\n") if Dir.exist?(sdk_path)
1921
1939
  end
1940
+
1941
+ # Success — drop the backup of the old android/.
1942
+ FileUtils.rm_rf(backup_dir) if backup_dir
1943
+ rescue StandardError
1944
+ # Restore the original android/ so a failed regeneration never
1945
+ # leaves the user worse off than before.
1946
+ if backup_dir && Dir.exist?(backup_dir)
1947
+ begin
1948
+ FileUtils.rm_rf(android_dir)
1949
+ FileUtils.mv(backup_dir, android_dir)
1950
+ rescue StandardError => restore_err
1951
+ # Don't let a restore failure silently strand the user's
1952
+ # native project inside the backup dir — tell them exactly
1953
+ # where it is and how to recover it.
1954
+ say "⚠️ Could not auto-restore your android/ folder: #{restore_err.message}", :red
1955
+ say " Your original android/ is preserved at: #{backup_dir}", :yellow
1956
+ say " Recover it with: mv '#{backup_dir}' '#{android_dir}'", :yellow
1957
+ end
1958
+ end
1959
+ raise
1922
1960
  ensure
1923
- # Restore original app.json
1961
+ # Always restore the original app.json
1924
1962
  File.write(app_json_path, original_content)
1925
1963
  end
1926
1964
  end
@@ -2116,17 +2154,23 @@ module Mysigner
2116
2154
  gradle_args = ['./gradlew', 'bundleRelease', '--warning-mode=all']
2117
2155
  gradle_args << "-PversionCode=#{version_code_override}" if version_code_override
2118
2156
 
2157
+ # Write the init script when we need to inject signing OR a
2158
+ # versionCode override (a bare -PversionCode is ignored by stock
2159
+ # build.gradle, so versionCode must flow through the init script).
2119
2160
  injector = nil
2120
2161
  env = {}
2121
- if keystore_info
2162
+ if keystore_info || version_code_override
2122
2163
  injector = Mysigner::Signing::GradleSigningInjector.new
2123
2164
  init_path = injector.write_init_script!
2124
- env = keystore_info[:signing_env_vars] || injector.env_vars(
2125
- keystore_path: keystore_info[:path],
2126
- store_password: keystore_info[:password],
2127
- key_password: keystore_info[:key_password],
2128
- key_alias: keystore_info[:key_alias]
2129
- )
2165
+ if keystore_info
2166
+ env = keystore_info[:signing_env_vars] || injector.env_vars(
2167
+ keystore_path: keystore_info[:path],
2168
+ store_password: keystore_info[:password],
2169
+ key_password: keystore_info[:key_password],
2170
+ key_alias: keystore_info[:key_alias]
2171
+ )
2172
+ end
2173
+ env['MYSIGNER_VERSION_CODE'] = version_code_override.to_s if version_code_override
2130
2174
  gradle_args.insert(1, '--init-script', init_path)
2131
2175
  end
2132
2176
 
@@ -2145,9 +2189,10 @@ module Mysigner
2145
2189
  # Find the AAB
2146
2190
  aab_path = File.join(android_dir, 'app/build/outputs/bundle/release/app-release.aab')
2147
2191
  unless File.exist?(aab_path)
2148
- # Try alternate paths
2192
+ # Fall back to the NEWEST matching AAB so a stale/wrong-flavor
2193
+ # artifact from a previous build is never returned.
2149
2194
  alt_paths = Dir.glob(File.join(android_dir, 'app/build/outputs/bundle/*/*.aab'))
2150
- aab_path = alt_paths.first if alt_paths.any?
2195
+ aab_path = alt_paths.max_by { |f| File.mtime(f) } if alt_paths.any?
2151
2196
  end
2152
2197
  aab_path
2153
2198
  end
@@ -147,7 +147,22 @@ module Mysigner
147
147
  say 'Running local Signing::Validator (server checks skipped in local-only mode).', :yellow
148
148
  say ''
149
149
 
150
- project_info = Mysigner::Build::Detector.detect
150
+ # Read-only detection: never run an expo prebuild from `validate`,
151
+ # and turn a genuine "no project here" into a clean error instead
152
+ # of a raw NoProjectError backtrace.
153
+ project_info = begin
154
+ Mysigner::Build::Detector.detect(allow_prebuild: false)
155
+ rescue Mysigner::Build::Detector::NoProjectError => e
156
+ error e.message
157
+ exit 1
158
+ end
159
+
160
+ if project_info[:needs_prebuild]
161
+ say 'ℹ️ Expo managed project detected — no native iOS project generated yet.', :yellow
162
+ say ' Run `mysigner ship` (or `npx expo prebuild`) to create it, then re-run validate.', :yellow
163
+ return
164
+ end
165
+
151
166
  parser = Mysigner::Build::Parser.new(project_info)
152
167
  target_name = parser.main_target.name
153
168
 
@@ -251,13 +251,25 @@ module Mysigner
251
251
  # Display config (with masked tokens)
252
252
  def display
253
253
  current_org_name = org_name(@current_organization_id) || '(not set)'
254
- current_token = api_token(@current_organization_id)
254
+ # Never let an undecryptable token turn `mysigner config` into a crash —
255
+ # render it as an actionable placeholder instead.
256
+ current_token = begin
257
+ api_token(@current_organization_id)
258
+ rescue ConfigError
259
+ :unreadable
260
+ end
261
+
262
+ token_display = case current_token
263
+ when nil then '(not set)'
264
+ when :unreadable then '(unreadable — run mysigner login)'
265
+ else mask_token(current_token)
266
+ end
255
267
 
256
268
  display_data = {
257
269
  api_url: @api_url || '(not set)',
258
270
  user_email: @user_email || '(not set)',
259
271
  current_organization: "#{current_org_name} (ID: #{@current_organization_id || 'not set'})",
260
- current_token: current_token ? mask_token(current_token) : '(not set)'
272
+ current_token: token_display
261
273
  }
262
274
 
263
275
  # Show all organizations
@@ -14,6 +14,20 @@ module Mysigner
14
14
  allprojects {
15
15
  afterEvaluate { project ->
16
16
  if (!project.hasProperty('android')) return
17
+
18
+ // versionCode override (MySigner auto-increment). Applied here so it
19
+ // actually takes effect even when app/build.gradle hard-codes
20
+ // versionCode in defaultConfig — a plain -PversionCode project
21
+ // property is silently ignored by stock build.gradle files.
22
+ // Gate to the APPLICATION module only: com.android.library modules
23
+ // have no versionCode in the AGP 7/8 library DSL, so assigning it
24
+ // there raises. allprojects+afterEvaluate fires for every Android
25
+ // subproject, hence the explicit application-plugin check.
26
+ def vcRaw = System.getenv('MYSIGNER_VERSION_CODE')
27
+ if (vcRaw && vcRaw.isInteger() && project.plugins.hasPlugin('com.android.application')) {
28
+ project.android.defaultConfig.versionCode = vcRaw.toInteger()
29
+ }
30
+
17
31
  def storePw = System.getenv('MYSIGNER_STORE_PASSWORD')
18
32
  def keyPw = System.getenv('MYSIGNER_KEY_PASSWORD')
19
33
  def aliasName = System.getenv('MYSIGNER_KEY_ALIAS')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mysigner
4
- VERSION = '0.3.1'
4
+ VERSION = '0.3.2'
5
5
  end
data/mysigner.gemspec CHANGED
@@ -38,14 +38,19 @@ Gem::Specification.new do |spec|
38
38
 
39
39
  \e[32m✓\e[0m You're ready to automate iOS & Android code signing.
40
40
 
41
- \e[35mNext steps:\e[0m
42
- • \e[33mRun\e[0m \e[1m`mysigner onboard`\e[0m Guided first-time setup (API URL, org, token)
43
- \e[33mRun\e[0m \e[1m`mysigner login`\e[0m – Skip onboarding if you already have a token
44
- • \e[33mRun\e[0m \e[1m`mysigner doctor`\e[0m – Validate your development environment
45
- \e[33mRun\e[0m \e[1m`mysigner help`\e[0m – Explore every command in the toolbox
46
-
47
- \e[35miOS:\e[0m mysigner ship testflight
48
- \e[35mAndroid:\e[0m mysigner ship internal --platform android
41
+ \e[35mTwo ways to use it:\e[0m
42
+ • \e[1mWith a free My Signer account\e[0m (keys stored & synced for you):
43
+ \e[33mRun\e[0m \e[1m`mysigner onboard`\e[0m
44
+ • \e[1mNo account — your keys stay on this machine\e[0m (nothing sent to a server):
45
+ \e[33mRun\e[0m \e[1m`mysigner --local-only onboard`\e[0m
46
+ \e[33mNot sure? Start with --local-only.\e[0m
47
+
48
+ \e[35mAlso handy:\e[0m
49
+ • \e[33mRun\e[0m \e[1m`mysigner doctor`\e[0m – Check your dev environment (JDK, SDK, Xcode…)
50
+ • \e[33mRun\e[0m \e[1m`mysigner help`\e[0m – Explore every command
51
+
52
+ \e[35miOS (needs a Mac):\e[0m mysigner ship testflight
53
+ \e[35mAndroid:\e[0m mysigner ship internal --platform android
49
54
 
50
55
  \e[36mDocs:\e[0m https://mysigner.dev/docs/commands
51
56
  MSG
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mysigner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurgen Leka
@@ -287,14 +287,15 @@ metadata:
287
287
  rubygems_mfa_required: 'true'
288
288
  post_install_message: "\e[36m╔══════════════════════════════════════════════════════════════╗\e[0m\n\e[36m║
289
289
  \e[1m\U0001F680 Welcome to My Signer CLI\e[0m\e[36m ║\e[0m\n\e[36m╚══════════════════════════════════════════════════════════════╝\e[0m\n\n\e[32m✓\e[0m
290
- \ You're ready to automate iOS & Android code signing.\n\n\e[35mNext steps:\e[0m\n
291
- \ • \e[33mRun\e[0m \e[1m`mysigner onboard`\e[0m – Guided first-time setup (API
292
- URL, org, token)\n\e[33mRun\e[0m \e[1m`mysigner login`\e[0m Skip onboarding
293
- if you already have a token\n • \e[33mRun\e[0m \e[1m`mysigner doctor`\e[0m
294
- Validate your development environment\n\e[33mRun\e[0m \e[1m`mysigner help`\e[0m
295
- \ Explore every command in the toolbox\n\n\e[35miOS:\e[0m mysigner
296
- ship testflight\n\e[35mAndroid:\e[0m mysigner ship internal --platform android\n\n\e[36mDocs:\e[0m
297
- https://mysigner.dev/docs/commands\n"
290
+ \ You're ready to automate iOS & Android code signing.\n\n\e[35mTwo ways to use
291
+ it:\e[0m\n • \e[1mWith a free My Signer account\e[0m (keys stored & synced for
292
+ you):\n \e[33mRun\e[0m \e[1m`mysigner onboard`\e[0m\n • \e[1mNo account —
293
+ your keys stay on this machine\e[0m (nothing sent to a server):\n \e[33mRun\e[0m
294
+ \e[1m`mysigner --local-only onboard`\e[0m\n \e[33mNot sure? Start with --local-only.\e[0m\n\n\e[35mAlso
295
+ handy:\e[0m\n • \e[33mRun\e[0m \e[1m`mysigner doctor`\e[0m – Check your dev environment
296
+ (JDK, SDK, Xcode…)\n\e[33mRun\e[0m \e[1m`mysigner help`\e[0m – Explore every
297
+ command\n\n\e[35miOS (needs a Mac):\e[0m mysigner ship testflight\n\e[35mAndroid:\e[0m
298
+ \ mysigner ship internal --platform android\n\n\e[36mDocs:\e[0m https://mysigner.dev/docs/commands\n"
298
299
  rdoc_options: []
299
300
  require_paths:
300
301
  - lib
@@ -309,7 +310,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
309
310
  - !ruby/object:Gem::Version
310
311
  version: '0'
311
312
  requirements: []
312
- rubygems_version: 4.0.11
313
+ rubygems_version: 3.6.9
313
314
  specification_version: 4
314
315
  summary: CLI tool for iOS and Android code signing automation via My Signer API
315
316
  test_files: []