mysigner 0.1.3 → 0.1.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/.gitignore +4 -0
- data/.rubocop_todo.yml +23 -9
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/certificate_.cer +0 -0
- data/exe/mysigner +17 -1
- data/iOS_App_Store_Profile.mobileprovision +1 -0
- data/iOS_Distribution_Certificate.cer +1 -0
- data/lib/mysigner/build/android_executor.rb +37 -11
- data/lib/mysigner/cleanup/private_keys_purger.rb +41 -0
- data/lib/mysigner/cli/auth_commands.rb +42 -18
- data/lib/mysigner/cli/build_commands.rb +307 -117
- data/lib/mysigner/cli/concerns/helpers.rb +32 -0
- data/lib/mysigner/cli/diagnostic_commands.rb +8 -1
- data/lib/mysigner/cli/resource_commands.rb +304 -114
- data/lib/mysigner/cli.rb +8 -0
- data/lib/mysigner/config.rb +68 -4
- data/lib/mysigner/export/exporter.rb +6 -1
- data/lib/mysigner/signing/gradle_signing_injector.rb +67 -0
- data/lib/mysigner/signing/keystore_manager.rb +50 -25
- data/lib/mysigner/upload/app_store_automation.rb +46 -1
- data/lib/mysigner/upload/asc_rest_uploader.rb +119 -0
- data/lib/mysigner/upload/play_store_uploader.rb +77 -40
- data/lib/mysigner/upload/uploader.rb +41 -12
- data/lib/mysigner/version.rb +1 -1
- data/profile_.mobileprovision +0 -0
- metadata +8 -2
- data/.DS_Store +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4fc28e312d574d8651626a8915fc22df043031afcab2166c92a14b353ca47e5d
|
|
4
|
+
data.tar.gz: 47261b2578073972306d83c241c68048e629345af59aad58142f490e658c1a62
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb2adad7a713e9b3ee93284d479bf2114fb87a998b51411406827f08ac5ac06f7f8c89c3eb62dd4aed7350de836562d5aeb645b7bf1a1bffb9a9b13bedec072f
|
|
7
|
+
data.tar.gz: 07eac231d1cfd32c46d238ac17678dc1406a4d6c29eab89a315fbf74bcaed224a985d6d87f18963698d59c5df5bfd48be21bbd52ba7dad66f6e0c4595b19ecda
|
data/.gitignore
CHANGED
data/.rubocop_todo.yml
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config --auto-gen-only-exclude`
|
|
3
|
-
# on 2026-
|
|
3
|
+
# on 2026-04-24 18:30:19 UTC using RuboCop version 1.86.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
-
# Offense count:
|
|
9
|
+
# Offense count: 7
|
|
10
10
|
# This cop supports safe autocorrection (--autocorrect).
|
|
11
11
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
12
12
|
# URISchemes: http, https
|
|
@@ -18,7 +18,18 @@ Layout/LineLength:
|
|
|
18
18
|
- 'mysigner.gemspec'
|
|
19
19
|
- 'spec/cli/ship_appstore_spec.rb'
|
|
20
20
|
|
|
21
|
-
# Offense count:
|
|
21
|
+
# Offense count: 2
|
|
22
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
23
|
+
Lint/NonAtomicFileOperation:
|
|
24
|
+
Exclude:
|
|
25
|
+
- 'lib/mysigner/cli/resource_commands.rb'
|
|
26
|
+
|
|
27
|
+
# Offense count: 12
|
|
28
|
+
Lint/UnreachableCode:
|
|
29
|
+
Exclude:
|
|
30
|
+
- 'lib/mysigner/cli/resource_commands.rb'
|
|
31
|
+
|
|
32
|
+
# Offense count: 45
|
|
22
33
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
23
34
|
Metrics/AbcSize:
|
|
24
35
|
Exclude:
|
|
@@ -30,6 +41,7 @@ Metrics/AbcSize:
|
|
|
30
41
|
- 'lib/mysigner/cli/validate_commands.rb'
|
|
31
42
|
- 'lib/mysigner/signing/validator.rb'
|
|
32
43
|
- 'lib/mysigner/signing/wizard.rb'
|
|
44
|
+
- 'lib/mysigner/upload/app_store_automation.rb'
|
|
33
45
|
- 'lib/mysigner/upload/app_store_submission.rb'
|
|
34
46
|
|
|
35
47
|
# Offense count: 11
|
|
@@ -37,8 +49,6 @@ Metrics/AbcSize:
|
|
|
37
49
|
# AllowedMethods: refine
|
|
38
50
|
Metrics/BlockLength:
|
|
39
51
|
Exclude:
|
|
40
|
-
- 'spec/**/*'
|
|
41
|
-
- '*.gemspec'
|
|
42
52
|
- 'lib/mysigner/cli/auth_commands.rb'
|
|
43
53
|
- 'lib/mysigner/cli/build_commands.rb'
|
|
44
54
|
- 'lib/mysigner/cli/diagnostic_commands.rb'
|
|
@@ -57,7 +67,7 @@ Metrics/ClassLength:
|
|
|
57
67
|
Exclude:
|
|
58
68
|
- 'lib/mysigner/signing/wizard.rb'
|
|
59
69
|
|
|
60
|
-
# Offense count:
|
|
70
|
+
# Offense count: 26
|
|
61
71
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
62
72
|
Metrics/CyclomaticComplexity:
|
|
63
73
|
Exclude:
|
|
@@ -68,8 +78,9 @@ Metrics/CyclomaticComplexity:
|
|
|
68
78
|
- 'lib/mysigner/cli/resource_commands.rb'
|
|
69
79
|
- 'lib/mysigner/cli/validate_commands.rb'
|
|
70
80
|
- 'lib/mysigner/signing/wizard.rb'
|
|
81
|
+
- 'lib/mysigner/upload/app_store_automation.rb'
|
|
71
82
|
|
|
72
|
-
# Offense count:
|
|
83
|
+
# Offense count: 45
|
|
73
84
|
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
74
85
|
Metrics/MethodLength:
|
|
75
86
|
Exclude:
|
|
@@ -92,14 +103,16 @@ Metrics/ModuleLength:
|
|
|
92
103
|
- 'lib/mysigner/cli/diagnostic_commands.rb'
|
|
93
104
|
- 'lib/mysigner/cli/resource_commands.rb'
|
|
94
105
|
|
|
95
|
-
# Offense count:
|
|
106
|
+
# Offense count: 5
|
|
96
107
|
# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
|
|
97
108
|
Metrics/ParameterLists:
|
|
98
109
|
Exclude:
|
|
99
110
|
- 'lib/mysigner/build/executor.rb'
|
|
100
111
|
- 'lib/mysigner/signing/keystore_manager.rb'
|
|
112
|
+
- 'lib/mysigner/upload/asc_rest_uploader.rb'
|
|
113
|
+
- 'lib/mysigner/upload/play_store_uploader.rb'
|
|
101
114
|
|
|
102
|
-
# Offense count:
|
|
115
|
+
# Offense count: 27
|
|
103
116
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
104
117
|
Metrics/PerceivedComplexity:
|
|
105
118
|
Exclude:
|
|
@@ -110,3 +123,4 @@ Metrics/PerceivedComplexity:
|
|
|
110
123
|
- 'lib/mysigner/cli/resource_commands.rb'
|
|
111
124
|
- 'lib/mysigner/cli/validate_commands.rb'
|
|
112
125
|
- 'lib/mysigner/signing/wizard.rb'
|
|
126
|
+
- 'lib/mysigner/upload/app_store_automation.rb'
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
data/certificate_.cer
ADDED
|
File without changes
|
data/exe/mysigner
CHANGED
|
@@ -3,4 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
require 'mysigner'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# Normalize `mysigner <cmd> [args…] --help` → `mysigner help <cmd>`. Thor
|
|
7
|
+
# treats extra positional args as a fatal argument-count error, so without
|
|
8
|
+
# this rewrite users who type `mysigner ship testflight --help` see
|
|
9
|
+
# `ERROR: "mysigner ship" was called with arguments ["testflight", "--help"]`
|
|
10
|
+
# instead of the usage docs. Rewriting at the entry point keeps every
|
|
11
|
+
# subcommand's `--help` behaviour predictable.
|
|
12
|
+
def rewrite_help_flag!(argv)
|
|
13
|
+
return argv unless argv.any? { |a| ['--help', '-h'].include?(a) }
|
|
14
|
+
|
|
15
|
+
# Don't touch `help` itself or `--version` / bare `-v` invocations.
|
|
16
|
+
first = argv.find { |a| !a.start_with?('-') }
|
|
17
|
+
return argv if first.nil? || first == 'help'
|
|
18
|
+
|
|
19
|
+
['help', first]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Mysigner::CLI.start(rewrite_help_flag!(ARGV))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Server error
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Server error
|
|
@@ -91,11 +91,23 @@ module Mysigner
|
|
|
91
91
|
# Handle framework-specific pre-build steps
|
|
92
92
|
run_pre_build_steps
|
|
93
93
|
|
|
94
|
-
#
|
|
95
|
-
|
|
94
|
+
# Phase 0: inject signing via Gradle init-script + env vars. Passwords
|
|
95
|
+
# never appear in argv (no -Pandroid.injected.signing.*=PLAINTEXT).
|
|
96
|
+
injector = nil
|
|
97
|
+
if @keystore_path && File.exist?(@keystore_path)
|
|
98
|
+
require 'mysigner/signing/gradle_signing_injector'
|
|
99
|
+
injector = Mysigner::Signing::GradleSigningInjector.new
|
|
100
|
+
@signing_init_script_path = injector.write_init_script!
|
|
101
|
+
end
|
|
96
102
|
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
begin
|
|
104
|
+
# Build command with signing properties referencing the init script
|
|
105
|
+
cmd = build_gradle_command(task)
|
|
106
|
+
execute_with_output(cmd)
|
|
107
|
+
ensure
|
|
108
|
+
injector&.cleanup!
|
|
109
|
+
@signing_init_script_path = nil
|
|
110
|
+
end
|
|
99
111
|
end
|
|
100
112
|
|
|
101
113
|
def ensure_java_home!
|
|
@@ -233,20 +245,34 @@ module Mysigner
|
|
|
233
245
|
cmd_parts << '&&'
|
|
234
246
|
end
|
|
235
247
|
|
|
248
|
+
# Phase 0: export signing env vars inline so they're only visible to
|
|
249
|
+
# the child process, not in argv. The Gradle init script below reads
|
|
250
|
+
# MYSIGNER_STORE_FILE / MYSIGNER_STORE_PASSWORD / MYSIGNER_KEY_ALIAS /
|
|
251
|
+
# MYSIGNER_KEY_PASSWORD and configures signingConfigs.release.
|
|
252
|
+
if @keystore_path && File.exist?(@keystore_path) && @signing_init_script_path
|
|
253
|
+
cmd_parts << "export MYSIGNER_STORE_FILE=#{shell_escape(File.absolute_path(@keystore_path))}"
|
|
254
|
+
cmd_parts << '&&'
|
|
255
|
+
cmd_parts << "export MYSIGNER_STORE_PASSWORD=#{shell_escape(@keystore_password)}" if @keystore_password
|
|
256
|
+
cmd_parts << '&&' if @keystore_password
|
|
257
|
+
cmd_parts << "export MYSIGNER_KEY_ALIAS=#{shell_escape(@key_alias)}" if @key_alias
|
|
258
|
+
cmd_parts << '&&' if @key_alias
|
|
259
|
+
cmd_parts << "export MYSIGNER_KEY_PASSWORD=#{shell_escape(@key_password)}" if @key_password
|
|
260
|
+
cmd_parts << '&&' if @key_password
|
|
261
|
+
end
|
|
262
|
+
|
|
236
263
|
# Change to android directory and run gradle
|
|
237
264
|
cmd_parts << "cd #{shell_escape(android_dir)}"
|
|
238
265
|
cmd_parts << '&&'
|
|
239
266
|
cmd_parts << gradle_cmd
|
|
240
|
-
cmd_parts << task
|
|
241
267
|
|
|
242
|
-
#
|
|
243
|
-
if @
|
|
244
|
-
cmd_parts <<
|
|
245
|
-
cmd_parts <<
|
|
246
|
-
cmd_parts << "-Pandroid.injected.signing.key.alias=#{shell_escape(@key_alias)}" if @key_alias
|
|
247
|
-
cmd_parts << "-Pandroid.injected.signing.key.password=#{shell_escape(@key_password)}" if @key_password
|
|
268
|
+
# Reference the init script (contains the signing-config override)
|
|
269
|
+
if @signing_init_script_path
|
|
270
|
+
cmd_parts << '--init-script'
|
|
271
|
+
cmd_parts << shell_escape(@signing_init_script_path)
|
|
248
272
|
end
|
|
249
273
|
|
|
274
|
+
cmd_parts << task
|
|
275
|
+
|
|
250
276
|
# Add version code override if provided (no file modification needed)
|
|
251
277
|
cmd_parts << "-PversionCode=#{@version_code}" if @version_code
|
|
252
278
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Mysigner
|
|
6
|
+
module Cleanup
|
|
7
|
+
class PrivateKeysPurger
|
|
8
|
+
LEGACY_DIRS = [
|
|
9
|
+
'~/.private_keys',
|
|
10
|
+
'~/.appstoreconnect/private_keys'
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
def marker_path
|
|
14
|
+
File.expand_path('~/.mysigner/.private_keys_purged')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
return if ENV['MYSIGNER_USE_LEGACY_ASC'] == '1'
|
|
19
|
+
return if File.exist?(marker_path)
|
|
20
|
+
|
|
21
|
+
LEGACY_DIRS.each do |dir|
|
|
22
|
+
expanded = File.expand_path(dir)
|
|
23
|
+
Dir.glob(File.join(expanded, 'AuthKey_*.p8')).each do |path|
|
|
24
|
+
File.delete(path)
|
|
25
|
+
warn "mysigner: deleted legacy private key #{path}" if ENV['MYSIGNER_VERBOSE'] == '1'
|
|
26
|
+
rescue Errno::ENOENT
|
|
27
|
+
# Raced with another process — already gone.
|
|
28
|
+
rescue Errno::EACCES, Errno::EPERM => e
|
|
29
|
+
# Owned by another user. Skip this file but keep going so the
|
|
30
|
+
# marker still gets written — otherwise the purger retries on
|
|
31
|
+
# every CLI invocation and keeps failing on the same file.
|
|
32
|
+
warn "mysigner: could not delete #{path} (#{e.class}); skipping" if ENV['MYSIGNER_VERBOSE'] == '1'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
FileUtils.mkdir_p(File.dirname(marker_path))
|
|
37
|
+
FileUtils.touch(marker_path)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -5,7 +5,7 @@ module Mysigner
|
|
|
5
5
|
module AuthCommands
|
|
6
6
|
def self.included(base)
|
|
7
7
|
base.class_eval do
|
|
8
|
-
desc 'version', 'Show
|
|
8
|
+
desc 'version', 'Show version information'
|
|
9
9
|
def version
|
|
10
10
|
say "My Signer CLI v#{Mysigner::VERSION}", :cyan
|
|
11
11
|
say ''
|
|
@@ -13,6 +13,9 @@ module Mysigner
|
|
|
13
13
|
say "Install: #{File.expand_path('../../..', __dir__)}", :white
|
|
14
14
|
say "Config: #{Config::CONFIG_FILE}", :white
|
|
15
15
|
say ''
|
|
16
|
+
say 'Repository: https://github.com/mysigner-dev/mysigner-cli', :white
|
|
17
|
+
say 'Issues: https://github.com/mysigner-dev/mysigner-cli/issues', :white
|
|
18
|
+
say ''
|
|
16
19
|
say 'Docs: https://mysigner.dev/docs/commands', :white
|
|
17
20
|
say 'Support: https://mysigner.dev/landing#contact', :white
|
|
18
21
|
end
|
|
@@ -738,10 +741,14 @@ module Mysigner
|
|
|
738
741
|
end
|
|
739
742
|
end
|
|
740
743
|
|
|
741
|
-
desc 'switch', 'Switch between organizations (for multi-org users)'
|
|
744
|
+
desc 'switch [ORG_ID]', 'Switch between organizations (for multi-org users)'
|
|
742
745
|
long_desc <<~DESC
|
|
743
746
|
Switch to a different organization.
|
|
744
747
|
|
|
748
|
+
Interactive (no argument): prompts you with a numbered list.
|
|
749
|
+
Non-interactive: pass an organization ID directly, e.g.
|
|
750
|
+
mysigner switch 7
|
|
751
|
+
|
|
745
752
|
With organization-specific tokens, you'll need a token for each
|
|
746
753
|
organization you want to access. This command will:
|
|
747
754
|
- Show all organizations you're a member of
|
|
@@ -753,7 +760,7 @@ module Mysigner
|
|
|
753
760
|
Note: You need to be the same user in all organizations. Tokens
|
|
754
761
|
from different user accounts will be rejected.
|
|
755
762
|
DESC
|
|
756
|
-
def switch
|
|
763
|
+
def switch(target_org_id = nil)
|
|
757
764
|
config = load_config
|
|
758
765
|
client = create_client(config)
|
|
759
766
|
|
|
@@ -815,20 +822,31 @@ module Mysigner
|
|
|
815
822
|
say 'Legend: ✓ = Has token | ⚠️ = Needs token', :white
|
|
816
823
|
say ''
|
|
817
824
|
|
|
818
|
-
#
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
825
|
+
# Non-interactive selection via positional arg (`mysigner switch 7`)
|
|
826
|
+
selected_org = if target_org_id
|
|
827
|
+
match = organizations_list.find { |o| o[:id].to_s == target_org_id.to_s }
|
|
828
|
+
unless match
|
|
829
|
+
error "Organization #{target_org_id} not found among your memberships"
|
|
830
|
+
say ''
|
|
831
|
+
say " Available IDs: #{organizations_list.map { |o| o[:id] }.join(', ')}", :yellow
|
|
832
|
+
exit 1
|
|
833
|
+
end
|
|
834
|
+
match
|
|
835
|
+
else
|
|
836
|
+
org_index = ask("Select organization (1-#{organizations_list.length}, or 'q' to cancel):")
|
|
837
|
+
|
|
838
|
+
if org_index.downcase == 'q'
|
|
839
|
+
say 'Cancelled', :yellow
|
|
840
|
+
return
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
unless org_index.match(/^\d+$/) && org_index.to_i.between?(1, organizations_list.length)
|
|
844
|
+
error 'Invalid selection'
|
|
845
|
+
return
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
organizations_list[org_index.to_i - 1]
|
|
849
|
+
end
|
|
832
850
|
|
|
833
851
|
if selected_org[:id] == config.current_organization_id
|
|
834
852
|
say ''
|
|
@@ -941,8 +959,14 @@ module Mysigner
|
|
|
941
959
|
end
|
|
942
960
|
|
|
943
961
|
no_commands do
|
|
944
|
-
# Helper method for yes/no prompts with Enter defaulting to yes
|
|
962
|
+
# Helper method for yes/no prompts with Enter defaulting to yes.
|
|
963
|
+
# Defaults to NO when stdin is not a TTY so automation (CI, pipes)
|
|
964
|
+
# never silently opts-in to mutating operations.
|
|
945
965
|
def yes_with_default?(statement, color = nil)
|
|
966
|
+
unless $stdin.tty?
|
|
967
|
+
say "#{statement} [Y/n] (non-interactive: assuming no)", color
|
|
968
|
+
return false
|
|
969
|
+
end
|
|
946
970
|
response = ask("#{statement} [Y/n]", color).to_s.strip.downcase
|
|
947
971
|
response.empty? || response == 'y' || response == 'yes'
|
|
948
972
|
end
|