fastlane-plugin-pharen 0.2.0 → 0.3.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: 8e730459c397f4e9585f12a2c687f6216c8fb2c1679c5a45a5e85e8649b1398a
4
- data.tar.gz: 465ba4d2a4551dd462333f6e70d8e1e361d6642aafcb67d0505b84f7cb7f9e54
3
+ metadata.gz: dadecec8055f7967c935f9a8679dfd48f489f848f7ad7477bff1e1d6b87a4158
4
+ data.tar.gz: 4b0a0db3a15232e961f545a7264b94c89bc329c8ea25db487c0f575cffd93323
5
5
  SHA512:
6
- metadata.gz: e6a0a84cfaa13a65a383e60cfe3cc0d55bc4996e66053fac349f7c5eb98fc4634dbde73395d8a1c417e994fde64b7e050e0fbf59870431f9a3767afe218c0eec
7
- data.tar.gz: ba90879161e346e4b626a198de1824851a129407411bd03bd597b405847d337b37553393a30078fb6f67a5de50fd70f698ad28cc05a669906dc6e7ae9a0b960b
6
+ metadata.gz: b818317f1e4a81ea82160b707888e66db4ff15ae6f5882710112c4699460dee62e18d4b229916b37d307d6c56bf5b1f7434981fb6637b3c19ecffca157593042
7
+ data.tar.gz: b04d8bfc3b3e651938c00b5ad1dd4f0ddc9006de0f8b78f713b3f16c445d7af3c6f4b4f9a0fd93fed1da1614bbf4727db0cb7a95d9efd324bfaf3062e7046251
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to `fastlane-plugin-pharen` are documented here.
4
4
 
5
+ ## 0.3.0
6
+
7
+ - Requires `@pharen-ai/cli` 0.4.0 or later. The plugin requests JSON explicitly and
8
+ works with CLI releases that print readable text by default.
9
+
5
10
  ## 0.2.0
6
11
 
7
12
  - **Requires `@pharen-ai/cli` 0.1.0 or later** (`npm install -g @pharen-ai/cli`). The
@@ -13,14 +18,27 @@ All notable changes to `fastlane-plugin-pharen` are documented here.
13
18
  `strict: false` (or `PHAREN_STRICT=false`) to warn and continue as 0.1.0 did. A build
14
19
  shipped without its symbols produces crashes that stay unreadable until they arrive,
15
20
  which a warning in a long lane log too easily hides.
21
+ - **In strict mode, `pharen_release` checks its dSYMs before it registers the release.**
22
+ When none are found, a dSYM path does not exist, or a zip holds no `.dSYM`, the lane
23
+ fails before anything is sent; with `strict: true`, 0.1.0 registered the release first and
24
+ then failed at the symbols step, leaving a release with no build. A dSYM the CLI or the
25
+ server rejects at upload still fails the lane after the release is registered. With `strict: false` it warns and uploads the build, as before.
16
26
  - **`PHAREN_STRICT` takes `true` or `false`** (also `yes`/`no`, `on`/`off`, each in
17
27
  lowercase or all caps). Any other value, `1`, `0` and an empty one included, now fails
18
28
  the lane instead of being read as `true`; unset it to take the default.
19
29
  - **`pharen_release`'s closing line says what happened** — release registered or not,
20
30
  symbols uploaded or not — rather than claiming all three steps after one was skipped.
31
+ - **GitLab CI signs in once per command.** A GitLab ID token signs in once, so a job
32
+ declares `PHAREN_ID_TOKEN_1`, `_2`, ... in its `id_tokens:` block, one per `pharen`
33
+ command, and the plugin hands each command the next in that command's own environment.
34
+ The plugin takes the tokens out of the process environment when fastlane loads it, so no
35
+ other action or build step inherits one, a lone `PHAREN_ID_TOKEN` included. `pharen_release`
36
+ (three commands, two with no dSYMs and `strict: false`) checks the count before it runs anything and fails the lane when the job is short
37
+ or declares a single `PHAREN_ID_TOKEN`. An exported `PHAREN_AUTH_TOKEN` turns this off.
21
38
  - **Credentials are the CLI's.** The CLI uses `PHAREN_AUTH_TOKEN` when it is exported, a
22
39
  CI job's own identity once its pipeline is bound, or the session `pharen auth login`
23
- stored; the plugin still never reads, prints, or passes any of them.
40
+ stored; the plugin never reads, prints, or passes any of them, beyond handing a GitLab
41
+ job's ID tokens on one per command.
24
42
 
25
43
  ## 0.1.0 — initial release
26
44
 
data/README.md CHANGED
@@ -5,6 +5,10 @@ Ship an iOS build to Pharen from your Fastfile. One action, `pharen_release`, ru
5
5
  uploads the build, and returns an OTA install link — with nothing to configure in the
6
6
  lane.
7
7
 
8
+ [Continue with organization approval](https://console.pharen.ai/setup/application) to move beyond evaluation.
9
+ Approved organizations can use test and live keys and upload matching dSYMs for readable
10
+ crash stacks. Your paid plan determines which features and usage limits apply.
11
+
8
12
  The plugin is a thin wrapper over the `pharen` CLI. Authentication, `.pharen.yml`
9
13
  resolution, debug-id parsing, and server-side dedupe all live in the CLI; the plugin only
10
14
  derives what the CLI needs from the lane context and the built artifacts. It adds no
@@ -18,7 +22,7 @@ fastlane add_plugin pharen
18
22
 
19
23
  Once per repository, you also need:
20
24
 
21
- - the `pharen` CLI on `PATH` — `npm install -g @pharen-ai/cli` (or point
25
+ - the `pharen` CLI (0.4.0 or later) on `PATH` — `npm install -g @pharen-ai/cli` (or point
22
26
  `pharen_binary:` / `PHAREN_BINARY` at it),
23
27
  - a committed `.pharen.yml` at the repo root (`org:` and `app:` — no secrets),
24
28
  - a credential the CLI can use — see [Configuration](#configuration).
@@ -38,6 +42,17 @@ from git. It registers the release, uploads every dSYM the archive produced, upl
38
42
  build, prints the OTA install link, and returns it (also available in
39
43
  `lane_context[SharedValues::PHAREN_INSTALL_URL]`).
40
44
 
45
+ `pharen_release` is for tester builds: it always uploads the build for an install link. In
46
+ a TestFlight or App Store lane, upload the symbols before the build leaves:
47
+
48
+ ```ruby
49
+ lane :release do
50
+ build_app(scheme: "MyApp", export_method: "app-store")
51
+ pharen_upload_symbols
52
+ upload_to_testflight
53
+ end
54
+ ```
55
+
41
56
  ## Actions
42
57
 
43
58
  ### `pharen_release`
@@ -94,10 +109,42 @@ Two things live outside the lane:
94
109
  - **A credential** — the CLI finds its own, in this order: `PHAREN_AUTH_TOKEN` when it is
95
110
  exported (a CI secret), then a CI job's own identity once an organization owner has
96
111
  bound that pipeline (no secret at all), then the session `pharen auth login` stored on
97
- your machine. The plugin never reads, prints, or passes a credential, so none lands in a
98
- log line or a process argument list. The [CLI reference](https://www.pharen.ai/docs/cli)
112
+ your machine. The plugin never prints a credential or puts one in a process argument
113
+ list, and passes none on except a GitLab job's ID tokens, one per command (below). The [CLI reference](https://www.pharen.ai/docs/cli)
99
114
  covers each.
100
115
 
116
+ ### GitLab CI
117
+
118
+ On GitHub Actions, each `pharen` command asks GitHub for a fresh ID token, so a bound job
119
+ needs nothing more. A GitLab ID token signs in once, and `pharen_release` runs three `pharen`
120
+ commands, so a GitLab job declares one token per command and the plugin hands each command
121
+ the next:
122
+
123
+ ```yaml
124
+ beta:
125
+ id_tokens:
126
+ PHAREN_ID_TOKEN_1: { aud: 'https://api.pharen.ai' }
127
+ PHAREN_ID_TOKEN_2: { aud: 'https://api.pharen.ai' }
128
+ PHAREN_ID_TOKEN_3: { aud: 'https://api.pharen.ai' }
129
+ script:
130
+ - bundle exec fastlane beta
131
+ ```
132
+
133
+ - **Count every action.** `pharen_release` spends three tokens (two when there are no dSYMs
134
+ to upload, or none could be prepared, and `strict` is false); `pharen_upload_build` and `pharen_upload_symbols` spend
135
+ one each. A job that runs a single one-command action may declare one `PHAREN_ID_TOKEN`
136
+ instead.
137
+ - **The tokens stay out of the lane.** The plugin reads them when fastlane loads it and
138
+ takes them out of the environment, a lone `PHAREN_ID_TOKEN` included, so no other action
139
+ or build step inherits one. Each
140
+ command gets its token in its own environment, and none is printed.
141
+ - **A short job fails up front.** `pharen_release` counts its tokens before it runs
142
+ anything, and fails the lane when the job holds too few or declares a single
143
+ `PHAREN_ID_TOKEN` instead, rather than registering the release and then being refused at
144
+ the upload. With `strict: false` it warns and skips all three steps.
145
+ - **`PHAREN_AUTH_TOKEN` wins.** When it is exported, the CLI uses it and the tokens go
146
+ unspent.
147
+
101
148
  ## Failure posture
102
149
 
103
150
  The default is **strict**: if a step errors, the lane fails. A build that ships without
@@ -113,7 +160,9 @@ the lane rather than being guessed at. Unset it to take the default.
113
160
  Two things hold regardless of mode:
114
161
 
115
162
  - **Missing dSYMs are never quiet.** A build shipped without its symbols produces
116
- crashes that stay unreadable until the symbols arrive. Uploading them later
163
+ crashes that stay unreadable until the symbols arrive. In strict mode, when no dSYM is
164
+ found, a dSYM path does not exist, or a zip holds no `.dSYM`, `pharen_release` stops
165
+ before it registers or uploads anything. Uploading them later
117
166
  re-symbolicates and re-groups recent crashes, at most the last seven days of them;
118
167
  older ones may stay as they first grouped. Archive with `DEBUG_INFORMATION_FORMAT=dwarf-with-dsym`
119
168
  so the dSYMs exist to upload with the build.
@@ -47,6 +47,47 @@ module Fastlane
47
47
  return nil
48
48
  end
49
49
 
50
+ dsym_inputs = params[:dsym_paths]
51
+ if dsym_inputs.nil? || Array(dsym_inputs).empty?
52
+ candidates = []
53
+ candidates << Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH]
54
+ candidates.concat(Array(Actions.lane_context[SharedValues::DSYM_PATHS]))
55
+ dsym_inputs = candidates.compact.uniq
56
+ end
57
+
58
+ # The dSYMs are found and prepared before anything runs, so that in strict mode a
59
+ # lane with none, or with one that cannot be read, stops before the release is
60
+ # registered rather than leaving a release with no build. With strict: false the
61
+ # problem is a loud warning at the symbols step, and the build still uploads.
62
+ dsyms = nil
63
+ dsym_problem = nil
64
+ if Array(dsym_inputs).empty?
65
+ dsym_problem =
66
+ "pharen_release: no dSYMs in the lane context — crashes from this build " \
67
+ "stay unreadable until its dSYMs are uploaded. Archive with " \
68
+ "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym, or pass dsym_paths:"
69
+ else
70
+ begin
71
+ dsyms = helper.expand_dsym_inputs(dsym_inputs)
72
+ rescue Helper::PharenHelper::Failure => e
73
+ dsym_problem = "pharen_release: #{e.message}"
74
+ end
75
+ end
76
+ if dsym_problem && params[:strict]
77
+ helper.fail_or_warn(params[:strict], dsym_problem)
78
+ return nil
79
+ end
80
+
81
+ # A GitLab job needs one ID token per command, counted before the first one runs:
82
+ # a job short of tokens would otherwise register the release and then be refused
83
+ # at the upload.
84
+ commands = dsyms ? 3 : 2
85
+ shortfall = helper.id_token_shortfall("pharen_release", commands)
86
+ if shortfall
87
+ helper.fail_or_warn(params[:strict], shortfall)
88
+ return nil
89
+ end
90
+
50
91
  # -- step 1: register the release (idempotent server-side; commit = provenance)
51
92
  release_args = ["releases", "create", "--platform", "ios", "--version", info[:version], "--build", info[:build]]
52
93
  sha = helper.git_commit_sha
@@ -62,33 +103,16 @@ module Fastlane
62
103
 
63
104
  # -- step 2: symbols (every dSYM the lane produced) ---------------------------
64
105
  symbols_ok = false
65
- dsym_inputs = params[:dsym_paths]
66
- if dsym_inputs.nil? || Array(dsym_inputs).empty?
67
- candidates = []
68
- candidates << Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH]
69
- candidates.concat(Array(Actions.lane_context[SharedValues::DSYM_PATHS]))
70
- dsym_inputs = candidates.compact.uniq
71
- end
72
- if Array(dsym_inputs).empty?
73
- helper.fail_or_warn(
74
- params[:strict],
75
- "pharen_release: no dSYMs in the lane context — crashes from this build " \
76
- "stay unreadable until its dSYMs are uploaded. Archive with " \
77
- "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym, or pass dsym_paths:"
78
- )
106
+ if dsym_problem
107
+ helper.fail_or_warn(params[:strict], dsym_problem)
79
108
  else
80
- begin
81
- dsyms = helper.expand_dsym_inputs(dsym_inputs)
82
- symbols = helper.run_pharen(params[:pharen_binary], ["symbols", "upload", *dsyms])
83
- if symbols[:ok]
84
- symbols_ok = true
85
- summary = symbols[:json].is_a?(Hash) ? symbols[:json]["summary"] : nil
86
- UI.success("pharen_release: symbols — #{summary || "uploaded #{dsyms.length} dSYM(s)"}")
87
- else
88
- helper.fail_or_warn(params[:strict], helper.cli_failure_message("pharen symbols upload", symbols))
89
- end
90
- rescue Helper::PharenHelper::Failure => e
91
- helper.fail_or_warn(params[:strict], "pharen_release: #{e.message}")
109
+ symbols = helper.run_pharen(params[:pharen_binary], ["symbols", "upload", *dsyms])
110
+ if symbols[:ok]
111
+ symbols_ok = true
112
+ summary = symbols[:json].is_a?(Hash) ? symbols[:json]["summary"] : nil
113
+ UI.success("pharen_release: symbols — #{summary || "uploaded #{dsyms.length} dSYM(s)"}")
114
+ else
115
+ helper.fail_or_warn(params[:strict], helper.cli_failure_message("pharen symbols upload", symbols))
92
116
  end
93
117
  end
94
118
 
@@ -11,7 +11,12 @@
11
11
  #
12
12
  # SECURITY: the CLI resolves its own credential — the session `pharen auth login` stored, a
13
13
  # CI job's identity, or PHAREN_AUTH_TOKEN from the environment. This helper never reads,
14
- # prints, or passes one, so no command line ever contains it.
14
+ # prints, or passes one, so no command line ever contains it. The one thing it hands on is
15
+ # a GitLab job's ID tokens (PHAREN_ID_TOKEN_1, _2, ...): a GitLab ID token signs in once,
16
+ # and a lane runs several pharen commands, so each command is given the next token in its
17
+ # own environment only. The tokens are read when the plugin loads and taken out of the
18
+ # process environment, so no other action or build step can spend one first, and none is
19
+ # ever printed.
15
20
 
16
21
  require "fastlane_core/ui/ui"
17
22
  require "json"
@@ -33,6 +38,9 @@ module Fastlane
33
38
  # frameworks) never match.
34
39
  INFO_PLIST_ENTRY = %r{\APayload/[^/]+\.app/Info\.plist\z}
35
40
 
41
+ # A GitLab job's numbered ID tokens, one per pharen command (see SECURITY above).
42
+ ID_TOKEN_ENV = /\APHAREN_ID_TOKEN_(\d+)\z/
43
+
36
44
  class << self
37
45
  # -- IPA introspection ---------------------------------------------------------
38
46
 
@@ -97,17 +105,81 @@ module Fastlane
97
105
  found
98
106
  end
99
107
 
108
+ # -- GitLab ID tokens ------------------------------------------------------------
109
+
110
+ # Reads PHAREN_ID_TOKEN_1, _2, ... once, in numeric order, and removes every one of
111
+ # them from the process environment, so actions and build steps that run later
112
+ # inherit none. A job that declares a single PHAREN_ID_TOKEN instead holds a pool of
113
+ # one, taken out the same way: it signs in one command. Called when the plugin loads;
114
+ # later calls return the same pool.
115
+ def load_id_tokens
116
+ return if @id_tokens
117
+
118
+ names = ENV.keys.grep(ID_TOKEN_ENV).sort_by { |name| name[ID_TOKEN_ENV, 1].to_i }
119
+ @id_tokens = names.map { |name| ENV.delete(name) }.reject { |value| value.to_s.empty? }
120
+ @id_tokens_spent = 0
121
+ lone = ENV.delete("PHAREN_ID_TOKEN")
122
+ @lone_id_token = @id_tokens.empty? && !lone.to_s.empty?
123
+ @id_tokens = [lone] if @lone_id_token
124
+ end
125
+
126
+ # Forgets the loaded tokens so the next load_id_tokens reads the environment again.
127
+ def reset_id_tokens
128
+ @id_tokens = nil
129
+ @id_tokens_spent = 0
130
+ @lone_id_token = false
131
+ end
132
+
133
+ # True when each pharen command signs in with its own GitLab ID token. An exported
134
+ # PHAREN_AUTH_TOKEN turns this off: the CLI prefers it over a CI identity.
135
+ def per_command_id_tokens?
136
+ load_id_tokens
137
+ return false if @id_tokens_spent.zero? && @id_tokens.empty?
138
+ ENV["PHAREN_AUTH_TOKEN"].to_s.empty?
139
+ end
140
+
141
+ # Checks, before an action runs anything, that a GitLab job holds a token for each
142
+ # of the `count` pharen commands it is about to run. Returns nil when it does, or
143
+ # when the job is not signing in with GitLab ID tokens; otherwise the message to
144
+ # refuse with.
145
+ def id_token_shortfall(action, count)
146
+ return nil unless per_command_id_tokens?
147
+ return nil if @id_tokens.length >= count
148
+
149
+ if @lone_id_token
150
+ "#{action}: PHAREN_ID_TOKEN is one GitLab ID token, and one token signs in once. This " \
151
+ "action runs #{count} pharen commands: declare PHAREN_ID_TOKEN_1 through " \
152
+ "PHAREN_ID_TOKEN_#{count} in the job's id_tokens: block instead"
153
+ else
154
+ "#{action}: this action runs #{count} pharen command(s) and a GitLab ID token signs in " \
155
+ "once, but #{@id_tokens.length} unspent token(s) remain. Declare PHAREN_ID_TOKEN_1 " \
156
+ "through PHAREN_ID_TOKEN_#{@id_tokens_spent + count} in the job's id_tokens: block"
157
+ end
158
+ end
159
+
100
160
  # -- Pharen CLI invocation -----------------------------------------------------
101
161
 
102
- # Run `<binary> <args...>` and interpret the CLI's contract: JSON on stdout,
162
+ # Run `<binary> --json <args...>` and interpret the CLI's contract: JSON on stdout,
103
163
  # exit 0 on success. `binary` is Shellwords-split so `pharen_binary` may carry a
104
164
  # command with arguments (e.g. "npx --yes @pharen-ai/cli"). Never raises on CLI
105
- # failure — callers decide strict-vs-warn from the returned hash.
165
+ # failure — callers decide strict-vs-warn from the returned hash. On a GitLab job
166
+ # signing in with per-command ID tokens, the command is given the next one as
167
+ # PHAREN_ID_TOKEN in its own environment.
106
168
  def run_pharen(binary, args, chdir: nil)
107
- argv = Shellwords.split(binary.to_s) + args.map(&:to_s)
169
+ argv = Shellwords.split(binary.to_s) + ["--json"] + args.map(&:to_s)
108
170
  dir = chdir || project_dir
171
+ env = {}
172
+ if per_command_id_tokens?
173
+ if @id_tokens.empty?
174
+ return { ok: false, status: nil, stdout: "", json: nil, no_id_token: true,
175
+ stderr: "no GitLab ID token left for this command — declare PHAREN_ID_TOKEN_1 " \
176
+ "through PHAREN_ID_TOKEN_#{@id_tokens_spent + 1} in the job's id_tokens: block" }
177
+ end
178
+ env["PHAREN_ID_TOKEN"] = @id_tokens.shift
179
+ @id_tokens_spent += 1
180
+ end
109
181
  UI.command(Shellwords.join(argv)) # never contains a credential (the CLI resolves its own)
110
- out, err, status = capture(argv, chdir: dir)
182
+ out, err, status = capture(argv, chdir: dir, env: env)
111
183
  json = parse_json(out)
112
184
  { ok: status == 0, status: status, stdout: out, stderr: err, json: json }
113
185
  rescue Errno::ENOENT => e
@@ -120,7 +192,13 @@ module Fastlane
120
192
  def cli_failure_message(step, result)
121
193
  error = result[:json].is_a?(Hash) ? result[:json]["error"] : nil
122
194
  if error.is_a?(Hash) && error["message"]
195
+ if error["code"] == "command.unknown" && error["message"].to_s.start_with?("unknown command '--json ")
196
+ return "#{step} failed: this plugin needs Pharen CLI 0.4.0 or later. Update the CLI on PATH or point pharen_binary at a newer command."
197
+ end
198
+
123
199
  "#{step} failed: #{error['message']} (#{error['code']})"
200
+ elsif result[:no_id_token]
201
+ "#{step} failed: #{result[:stderr]}"
124
202
  elsif result[:not_found]
125
203
  "#{step} failed: #{result[:stderr]} — install the Pharen CLI with " \
126
204
  "`npm install -g @pharen-ai/cli` so `pharen` is on PATH, or set the pharen_binary option"
@@ -171,12 +249,13 @@ module Fastlane
171
249
 
172
250
  # -- plumbing (the single shell-out seam; specs stub exactly this) ---------------
173
251
 
174
- # argv -> [stdout, stderr, exitstatus]; binmode because plists are binary.
175
- def capture(argv, stdin: nil, chdir: nil)
252
+ # argv -> [stdout, stderr, exitstatus]; binmode because plists are binary. `env` is
253
+ # added to the child's environment only.
254
+ def capture(argv, stdin: nil, chdir: nil, env: {})
176
255
  options = { binmode: true }
177
256
  options[:stdin_data] = stdin unless stdin.nil?
178
257
  options[:chdir] = chdir unless chdir.nil?
179
- out, err, status = Open3.capture3(*argv, **options)
258
+ out, err, status = Open3.capture3(env, *argv, **options)
180
259
  [out, err, status.exitstatus]
181
260
  end
182
261
 
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Pharen
3
- VERSION = "0.2.0".freeze
3
+ VERSION = "0.3.0".freeze
4
4
  end
5
5
  end
@@ -16,3 +16,7 @@ end
16
16
  Fastlane::Pharen.all_classes.each do |current|
17
17
  require current
18
18
  end
19
+
20
+ # A GitLab job's ID tokens leave the environment before any lane runs, so no other action
21
+ # or build step inherits one it could spend first.
22
+ Fastlane::Helper::PharenHelper.load_id_tokens
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-pharen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pharen
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-10-24 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: fastlane
@@ -78,7 +77,6 @@ licenses:
78
77
  metadata:
79
78
  homepage_uri: https://www.pharen.ai/docs/fastlane
80
79
  rubygems_mfa_required: 'true'
81
- post_install_message:
82
80
  rdoc_options: []
83
81
  require_paths:
84
82
  - lib
@@ -93,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
91
  - !ruby/object:Gem::Version
94
92
  version: '0'
95
93
  requirements: []
96
- rubygems_version: 3.0.3.1
97
- signing_key:
94
+ rubygems_version: 4.0.20
98
95
  specification_version: 4
99
96
  summary: 'Pharen build distribution + crash symbols from your lane: pharen_release
100
97
  / pharen_upload_build / pharen_upload_symbols'