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 +4 -4
- data/CHANGELOG.md +19 -1
- data/README.md +53 -4
- data/lib/fastlane/plugin/pharen/actions/pharen_release_action.rb +50 -26
- data/lib/fastlane/plugin/pharen/helper/pharen_helper.rb +87 -8
- data/lib/fastlane/plugin/pharen/version.rb +1 -1
- data/lib/fastlane/plugin/pharen.rb +4 -0
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dadecec8055f7967c935f9a8679dfd48f489f848f7ad7477bff1e1d6b87a4158
|
|
4
|
+
data.tar.gz: 4b0a0db3a15232e961f545a7264b94c89bc329c8ea25db487c0f575cffd93323
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
98
|
-
|
|
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.
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -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.
|
|
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:
|
|
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:
|
|
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'
|