ruby_everywhere 0.13.0 → 0.14.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -30
  3. data/bridge/README.md +23 -23
  4. data/lib/everywhere/android_resources.rb +9 -9
  5. data/lib/everywhere/android_sdk.rb +9 -9
  6. data/lib/everywhere/asset_catalog.rb +7 -7
  7. data/lib/everywhere/blake2b.rb +1 -1
  8. data/lib/everywhere/boot.rb +1 -1
  9. data/lib/everywhere/builders/android.rb +25 -25
  10. data/lib/everywhere/builders/desktop.rb +6 -6
  11. data/lib/everywhere/builders/ios.rb +14 -14
  12. data/lib/everywhere/builders/native_sources.rb +1 -1
  13. data/lib/everywhere/child_supervision.rb +1 -1
  14. data/lib/everywhere/commands/build.rb +19 -19
  15. data/lib/everywhere/commands/clean.rb +1 -1
  16. data/lib/everywhere/commands/dev.rb +11 -11
  17. data/lib/everywhere/commands/doctor.rb +7 -7
  18. data/lib/everywhere/commands/icon.rb +2 -2
  19. data/lib/everywhere/commands/install.rb +21 -21
  20. data/lib/everywhere/commands/logs.rb +6 -6
  21. data/lib/everywhere/commands/platform/auth_status.rb +3 -3
  22. data/lib/everywhere/commands/platform/build.rb +7 -7
  23. data/lib/everywhere/commands/platform/login.rb +5 -5
  24. data/lib/everywhere/commands/platform/logout.rb +1 -1
  25. data/lib/everywhere/commands/platform/runner.rb +6 -6
  26. data/lib/everywhere/commands/preview.rb +14 -14
  27. data/lib/everywhere/commands/publish.rb +14 -14
  28. data/lib/everywhere/commands/release.rb +6 -6
  29. data/lib/everywhere/commands/updates_keygen.rb +4 -4
  30. data/lib/everywhere/config/app.rb +3 -3
  31. data/lib/everywhere/config/mobile.rb +2 -2
  32. data/lib/everywhere/config/native_desktop.rb +2 -2
  33. data/lib/everywhere/config/native_mobile.rb +3 -3
  34. data/lib/everywhere/config/updates.rb +2 -2
  35. data/lib/everywhere/database.rb +3 -3
  36. data/lib/everywhere/desktop_assets.rb +5 -5
  37. data/lib/everywhere/desktop_dev_app.rb +1 -1
  38. data/lib/everywhere/emulator.rb +5 -5
  39. data/lib/everywhere/log_filter.rb +1 -1
  40. data/lib/everywhere/omniauth.rb +1 -1
  41. data/lib/everywhere/paths.rb +1 -1
  42. data/lib/everywhere/platform/credentials.rb +1 -1
  43. data/lib/everywhere/request_context.rb +2 -2
  44. data/lib/everywhere/shellout.rb +2 -2
  45. data/lib/everywhere/simulator.rb +1 -1
  46. data/lib/everywhere/tab_filter.rb +2 -2
  47. data/lib/everywhere/version.rb +1 -1
  48. data/support/agents/AGENTS.md +7 -7
  49. data/support/agents/frameworks/hanami-views.md +8 -8
  50. data/support/agents/frameworks/hanami.md +2 -2
  51. data/support/agents/frameworks/rails-views.md +7 -7
  52. data/support/agents/frameworks/rails.md +8 -8
  53. data/support/agents/frameworks/sinatra-views.md +7 -7
  54. data/support/agents/frameworks/sinatra.md +2 -2
  55. data/support/agents/modes/local.md +2 -2
  56. data/support/agents/modes/remote.md +2 -2
  57. metadata +1 -1
@@ -123,7 +123,7 @@ module Everywhere
123
123
  config = Everywhere::Config.load(framework ? framework.root : Dir.pwd)
124
124
  unless framework || config.remote?
125
125
  UI.die!("no supported framework here (Rails, Hanami, Sinatra) and no remote.url in " \
126
- "config/everywhere.yml nothing to run")
126
+ "config/everywhere.yml, so nothing to run")
127
127
  end
128
128
 
129
129
  # The bundle id keys every per-app dir under ~/.rubyeverywhere that the
@@ -145,7 +145,7 @@ module Everywhere
145
145
  end
146
146
 
147
147
  if port_open?(port)
148
- UI.warn "something is ALREADY RUNNING on port #{port} attaching to it instead of starting"
148
+ UI.warn "something is ALREADY RUNNING on port #{port}, attaching to it instead of starting"
149
149
  UI.warn "#{UI.dim("your Procfile.dev/watchers are NOT running; stop that server or use --port if this isn't yours")}"
150
150
  @dock.set(:web, :running, detail: "port #{port} (attached)")
151
151
  else
@@ -161,7 +161,7 @@ module Everywhere
161
161
  # change, where a full restart of the session would mean rebuilding the
162
162
  # native shells for nothing.
163
163
  def restart_server
164
- return UI.warn("nothing to restart this app runs in remote mode") unless @framework
164
+ return UI.warn("nothing to restart: this app runs in remote mode") unless @framework
165
165
 
166
166
  @dock.set(:web, :booting, detail: "restarting")
167
167
  UI.step("restarting the dev server")
@@ -186,7 +186,7 @@ module Everywhere
186
186
  def background(name, &block)
187
187
  case @pool.start(name, &block)
188
188
  when :busy
189
- UI.warn "#{Dock::State::LABELS[name]} is already working #{UI.dim("give it a moment")}"
189
+ UI.warn "#{Dock::State::LABELS[name]} is already working, #{UI.dim("give it a moment")}"
190
190
  end
191
191
  end
192
192
 
@@ -210,7 +210,7 @@ module Everywhere
210
210
  # Checked before anything is staged: without cargo the build dies deep
211
211
  # in a relayed `sh: cargo: command not found`, after a dock entry and a
212
212
  # half-written dev bundle say a shell is on its way.
213
- Shellout.ensure_tool!("cargo", "the desktop shell is built with Rust install it from " \
213
+ Shellout.ensure_tool!("cargo", "the desktop shell is built with Rust; install it from " \
214
214
  "https://rustup.rs, then press d again")
215
215
 
216
216
  # A bad `window:` or a broken assets/ tree marks the desktop red and
@@ -285,7 +285,7 @@ module Everywhere
285
285
  # produce a long build that fails at the first tool. Android stays
286
286
  # available everywhere — its toolchain genuinely is cross-platform.
287
287
  unless Host.darwin?
288
- UI.warn("iOS builds need macOS and Xcode #{UI.dim("nothing to do on this platform")}")
288
+ UI.warn("iOS builds need macOS and Xcode, #{UI.dim("nothing to do on this platform")}")
289
289
  @dock.set(:ios, :idle, detail: "needs macOS")
290
290
  return
291
291
  end
@@ -343,7 +343,7 @@ module Everywhere
343
343
  # A headless Linux box (or WSL) has no xdg-open, and run? would just
344
344
  # return nil — the key would look broken. Print the URL instead.
345
345
  unless Shellout.tool?(opener)
346
- UI.warn("couldn't find #{opener} open #{UI.cyan(@dev_url)} yourself")
346
+ UI.warn("couldn't find #{opener}; open #{UI.cyan(@dev_url)} yourself")
347
347
  return
348
348
  end
349
349
 
@@ -362,7 +362,7 @@ module Everywhere
362
362
 
363
363
  udid = Simulator.booted_udid
364
364
  unless udid
365
- UI.warn "no booted iOS Simulator press #{UI.bold("i")} first"
365
+ UI.warn "no booted iOS Simulator; press #{UI.bold("i")} first"
366
366
  return
367
367
  end
368
368
  UI.step("watching iOS Simulator logs #{UI.dim("(press l again to stop)")}")
@@ -384,7 +384,7 @@ module Everywhere
384
384
  # are worth naming.
385
385
  def wait_for_port(port, pid: nil, timeout: 60,
386
386
  dead_hint: " (did `bundle install` run? does bin/dev's foreman exist?)",
387
- slow_hint: " it may just be slow to boot; check its output above")
387
+ slow_hint: ", though it may just be slow to boot; check its output above")
388
388
  super
389
389
  end
390
390
 
@@ -425,7 +425,7 @@ module Everywhere
425
425
  def run_headless
426
426
  @pool.wait_all
427
427
  failed = @pool.failed
428
- UI.die!("#{failed.join(", ")} failed see the log above") if failed.any?
428
+ UI.die!("#{failed.join(", ")} failed, see the log above") if failed.any?
429
429
 
430
430
  watcher = @children.synchronize { @relays[:web] || @relays[:desktop] }
431
431
  return watcher.join if watcher
@@ -448,7 +448,7 @@ module Everywhere
448
448
  UI.bad("#{Dock::State::LABELS[name]} failed: #{error.message}") unless error.is_a?(Everywhere::Fatal)
449
449
  return unless (key = KEY_FOR[name])
450
450
 
451
- UI.note("everything else is still running press #{UI.bold(key)} to try again",
451
+ UI.note("everything else is still running; press #{UI.bold(key)} to try again",
452
452
  marker: "⌨", color: :cyan)
453
453
  end
454
454
 
@@ -39,7 +39,7 @@ module Everywhere
39
39
 
40
40
  puts
41
41
  if ok
42
- note = @optional_gaps.any? ? " #{UI.dim("(#{@optional_gaps.join(" and ")} would need setup see above)")}" : ""
42
+ note = @optional_gaps.any? ? " #{UI.dim("(#{@optional_gaps.join(" and ")} would need setup, see above)")}" : ""
43
43
  UI.success("ready to build#{note}")
44
44
  else
45
45
  UI.bad("fix the items above (see https://rubyeverywhere.com/docs/diy/requirements)")
@@ -58,7 +58,7 @@ module Everywhere
58
58
  end
59
59
 
60
60
  def section_label(name, required)
61
- required ? name : "#{name} #{UI.dim("(not in build.targets informational)")}"
61
+ required ? name : "#{name} #{UI.dim("(not in build.targets, informational)")}"
62
62
  end
63
63
 
64
64
 
@@ -82,7 +82,7 @@ module Everywhere
82
82
  unless Host.darwin?
83
83
  return fix_check("macOS", "desktop builds are macOS-only for now") { false } if required
84
84
 
85
- UI.warn("desktop builds are macOS-only for now skipped")
85
+ UI.warn("desktop builds are macOS-only for now, so skipped")
86
86
  return false
87
87
  end
88
88
 
@@ -155,7 +155,7 @@ module Everywhere
155
155
  unless Host.darwin?
156
156
  return fix_check("macOS with Xcode", "iOS apps can only be built on a Mac") { false } if required
157
157
 
158
- UI.warn("needs macOS and Xcode skipped")
158
+ UI.warn("needs macOS and Xcode, so skipped")
159
159
  return false
160
160
  end
161
161
 
@@ -223,7 +223,7 @@ module Everywhere
223
223
  if sdk.cmdline_tools?
224
224
  UI.ok(labelled("Android SDK command-line tools", UI.short_path(sdk.sdkmanager)))
225
225
  else
226
- UI.warn("Android SDK command-line tools missing nothing below can be installed " \
226
+ UI.warn("Android SDK command-line tools missing, and nothing below can be installed " \
227
227
  "until they are: #{sdk.cmdline_tools_hint}")
228
228
  end
229
229
 
@@ -260,10 +260,10 @@ module Everywhere
260
260
  # turned out to be), so a failure appends the exact command to run.
261
261
  def fix_check(label, fix)
262
262
  passed = yield
263
- passed ? UI.ok(label) : UI.bad("#{label} #{fix}")
263
+ passed ? UI.ok(label) : UI.bad("#{label}: #{fix}")
264
264
  passed
265
265
  rescue StandardError
266
- UI.bad("#{label} #{fix}")
266
+ UI.bad("#{label}: #{fix}")
267
267
  false
268
268
  end
269
269
 
@@ -20,7 +20,7 @@ module Everywhere
20
20
  def call(source: nil, root: ".", output: nil, **)
21
21
  root_path = File.expand_path(root)
22
22
  source ||= default_source(root_path)
23
- UI.die!("no source PNG pass one or set app.icon in everywhere.yml") unless source
23
+ UI.die!("no source PNG. Pass one or set app.icon in everywhere.yml") unless source
24
24
  UI.die!("source PNG not found: #{source}") unless File.exist?(source)
25
25
 
26
26
  out = File.expand_path(output || File.join(root_path, "dist", "icons"))
@@ -29,7 +29,7 @@ module Everywhere
29
29
 
30
30
  master = File.join(out, "AppIcon-macos.png")
31
31
  unless Everywhere::Icon.macos_master(source, master)
32
- UI.die!("couldn't read #{File.basename(source)} expected an 8-bit RGB/RGBA PNG")
32
+ UI.die!("couldn't read #{File.basename(source)}: expected an 8-bit RGB/RGBA PNG")
33
33
  end
34
34
  UI.ok("macOS master #{rel(master, out)} #{UI.dim("(inset + squircle, 1024px)")}")
35
35
 
@@ -40,7 +40,7 @@ module Everywhere
40
40
  UI.step(UI.dim("tip: `every install --agents` writes an AGENTS.md guide for AI coding agents"))
41
41
  end
42
42
 
43
- UI.success("installed run `every dev` to open the app, `every build` to package it")
43
+ UI.success("installed. Run `every dev` to open the app, `every build` to package it")
44
44
  end
45
45
 
46
46
  private
@@ -107,14 +107,14 @@ module Everywhere
107
107
  # real app legitimately ships in (API-only, DATABASE_URL, no
108
108
  # config/puma.rb), so say what was skipped — never raise on it.
109
109
  def skip(label, reason)
110
- UI.step("#{label} #{UI.dim("(skipped #{reason})")}")
110
+ UI.step("#{label} #{UI.dim("(skipped: #{reason})")}")
111
111
  end
112
112
 
113
113
  def add_gem
114
- UI.die!("no Gemfile in #{@root} run `every install` inside your app") unless File.exist?(app_file("Gemfile"))
114
+ UI.die!("no Gemfile in #{@root}. Run `every install` inside your app") unless File.exist?(app_file("Gemfile"))
115
115
 
116
116
  add_gemfile_gem("ruby_everywhere",
117
- comment: "# Desktop apps from this #{@framework.name.capitalize} app https://rubyeverywhere.com")
117
+ comment: "# Desktop apps from this #{@framework.name.capitalize} app. https://rubyeverywhere.com")
118
118
  end
119
119
 
120
120
  # Packaged Rack apps serve through Puma at runtime (Framework#serve_rack
@@ -166,14 +166,14 @@ module Everywhere
166
166
  light: "#FFFFFF"
167
167
  dark: "#1C1B1A"
168
168
 
169
- # Native permissions the app may request only declared ones can prompt.
169
+ # Native permissions the app may request. Only declared ones can prompt.
170
170
  # camera/location values are the sentence iOS shows when asking (mandatory).
171
171
  # permissions:
172
172
  # notifications: true
173
173
  # camera: "Scan QR codes to pair devices."
174
174
  # location: "Find things near you."
175
175
 
176
- # Native menu-bar items each one visits a route in your app (no native code).
176
+ # Native menu-bar items. Each one visits a route in your app (no native code).
177
177
  # menu:
178
178
  # - label: Settings…
179
179
  # path: /settings
@@ -182,7 +182,7 @@ module Everywhere
182
182
  # - label: Documentation
183
183
  # path: /help
184
184
 
185
- # System tray / menu-bar-extra items same shape as menu, plus action: show | quit.
185
+ # System tray / menu-bar-extra items. Same shape as menu, plus action: show | quit.
186
186
  # tray:
187
187
  # - label: Open #{app_name}
188
188
  # action: show
@@ -204,10 +204,10 @@ module Everywhere
204
204
  # min_size: [600, 400]
205
205
 
206
206
  # Mobile tab bar (iOS today, Android soon). Served live from
207
- # /everywhere/ios_v1.json too, so tab changes deploy with the app
208
- # no app-store release. Icons: ios = SF Symbol name.
207
+ # /everywhere/ios_v1.json too, so tab changes deploy with the app.
208
+ # No app-store release. Icons: ios = SF Symbol name.
209
209
  #
210
- # Vary the bar per request with Everywhere.tabs in an initializer
210
+ # Vary the bar per request with Everywhere.tabs in an initializer:
211
211
  # signed-in only, feature flags, roles. visible: false declares a tab
212
212
  # without shipping it, for that block to add back (ctx.tab("Admin")).
213
213
  # tabs:
@@ -225,7 +225,7 @@ module Everywhere
225
225
  # ios: shield
226
226
  # visible: false
227
227
 
228
- # iOS-specific knobs (and native Swift you compile into the shell
228
+ # iOS-specific knobs (and native Swift you compile into the shell;
229
229
  # see /docs/diy/native-extensions).
230
230
  # native:
231
231
  # ios:
@@ -234,7 +234,7 @@ module Everywhere
234
234
  #
235
235
  # Rust you keep in native/desktop/ and compile into the desktop
236
236
  # shell. Declaring anything here switches the desktop build to a
237
- # per-app cargo compile the first one takes minutes.
237
+ # per-app cargo compile. The first one takes minutes.
238
238
  # desktop:
239
239
  # commands: [scan_ports] # reachable as Everywhere.desktop.invoke("scan_ports")
240
240
  # setup: true # run native/desktop/setup.rs at boot
@@ -244,7 +244,7 @@ module Everywhere
244
244
  # Durable build knobs (CLI flags still override per-run).
245
245
  # build:
246
246
  # ruby: "4.0.6"
247
- # targets: # the platforms you ship the CI matrix, expressed once
247
+ # targets: # the platforms you ship: the CI matrix, expressed once
248
248
  # - macos-arm64
249
249
  # # - ios-arm64 # Hotwire Native shell; remote mode only. Local builds
250
250
  # # # (`every build` / `every dev --ios`) and hosted
@@ -255,7 +255,7 @@ module Everywhere
255
255
 
256
256
  # Per-platform overrides. Keys are platform names (macos / ios / android /
257
257
  # windows / linux); anything here overrides the app: defaults above for that
258
- # platform handy when a store needs a distinct bundle id or version.
258
+ # platform. Handy when a store needs a distinct bundle id or version.
259
259
  # platforms:
260
260
  # ios:
261
261
  # bundle_id: com.example.#{slug}.ios
@@ -269,13 +269,13 @@ module Everywhere
269
269
  def remote_section
270
270
  if @mode == "remote"
271
271
  <<~YAML
272
- # Remote mode the deployed URL the native shell opens.
272
+ # Remote mode: the deployed URL the native shell opens.
273
273
  remote:
274
274
  url: "https://example.com"
275
275
  YAML
276
276
  else
277
277
  <<~YAML
278
- # Remote mode only the deployed URL the native shell opens.
278
+ # Remote mode only: the deployed URL the native shell opens.
279
279
  # Set mode: remote above, then uncomment:
280
280
  # remote:
281
281
  # url: "https://example.com"
@@ -327,7 +327,7 @@ module Everywhere
327
327
  if begins != 1 || ends != 1 ||
328
328
  existing.index(AgentsGuide::BEGIN_MARKER) > existing.index(AgentsGuide::END_MARKER)
329
329
  UI.warn("AGENTS.md has unbalanced #{AgentsGuide::BEGIN_MARKER} / #{AgentsGuide::END_MARKER} " \
330
- "markers leaving it alone; fix them by hand and re-run")
330
+ "markers, so leaving it alone; fix them by hand and re-run")
331
331
  return nil
332
332
  end
333
333
 
@@ -438,7 +438,7 @@ module Everywhere
438
438
  # RubyEverywhere desktop-runtime configuration (generated by `every install`).
439
439
  if ENV["NATIVE_PACKAGED"]
440
440
  # Large static files truncate when streamed from the packaged read-only
441
- # filesystem; Everywhere.boot! copies public/ to app-data serve from there.
441
+ # filesystem; Everywhere.boot! copies public/ to app-data, so serve from there.
442
442
  Rails.application.config.paths["public"] = ENV["NATIVE_PUBLIC_DIR"] if ENV["NATIVE_PUBLIC_DIR"]
443
443
 
444
444
  # Keep desktop users signed in across launches (session cookies are
@@ -458,7 +458,7 @@ module Everywhere
458
458
  puma = app_file("config", "puma.rb")
459
459
  unless File.exist?(puma)
460
460
  return skip("disable puma keep-alives when packaged",
461
- "no config/puma.rb add `enable_keep_alives false if ENV[\"NATIVE_PACKAGED\"]` to yours if you add one")
461
+ "no config/puma.rb. Add `enable_keep_alives false if ENV[\"NATIVE_PACKAGED\"]` to yours if you add one")
462
462
  end
463
463
 
464
464
  contents = File.read(puma)
@@ -478,7 +478,7 @@ module Everywhere
478
478
  db = app_file("config", "database.yml")
479
479
  unless File.exist?(db)
480
480
  return skip("point production SQLite at app-data dir",
481
- "no config/database.yml point the packaged database at ENV[\"NATIVE_STORAGE_DIR\"] yourself")
481
+ "no config/database.yml. Point the packaged database at ENV[\"NATIVE_STORAGE_DIR\"] yourself")
482
482
  end
483
483
 
484
484
  contents = File.read(db)
@@ -492,7 +492,7 @@ module Everywhere
492
492
  File.write(db, contents) if made
493
493
 
494
494
  unless made || contents.include?("NATIVE_STORAGE_DIR")
495
- UI.warn "couldn't rewrite config/database.yml automatically point production " \
495
+ UI.warn "couldn't rewrite config/database.yml automatically. Point production " \
496
496
  "sqlite paths at <%= ENV.fetch(\"NATIVE_STORAGE_DIR\", \"storage\") %>/..."
497
497
  return
498
498
  end
@@ -62,8 +62,8 @@ module Everywhere
62
62
  VERBOSE_FORMAT = "threadtime"
63
63
 
64
64
  def call(ios: false, android: false, last: nil, udid: nil, serial: nil, app_id: nil, verbose: false, **)
65
- UI.die!("pick a platform run: every logs --ios, or every logs --android") unless ios || android
66
- UI.die!("--ios and --android are two separate log streams pick one") if ios && android
65
+ UI.die!("pick a platform: run every logs --ios, or every logs --android") unless ios || android
66
+ UI.die!("--ios and --android are two separate log streams; pick one") if ios && android
67
67
 
68
68
  if android
69
69
  android_logs(serial: serial, app_id: app_id, last: last, verbose: verbose)
@@ -76,7 +76,7 @@ module Everywhere
76
76
 
77
77
  def ios_logs(udid: nil, last: nil, verbose: false)
78
78
  udid ||= Everywhere::Simulator.booted_udid
79
- UI.die!("no booted iOS Simulator start one with `every dev --ios`") unless udid
79
+ UI.die!("no booted iOS Simulator. Start one with `every dev --ios`") unless udid
80
80
 
81
81
  UI.step(last ? "iOS Simulator logs from the last #{UI.bold(last)}"
82
82
  : "watching iOS Simulator logs #{UI.dim("(Ctrl-C to stop)")}")
@@ -110,12 +110,12 @@ module Everywhere
110
110
  # template (it's the applicationId), so the config supplies it.
111
111
  def android_logs(serial: nil, app_id: nil, last: nil, verbose: false)
112
112
  serial ||= Everywhere::Emulator.booted_serial
113
- UI.die!("no Android emulator or device start one with `every dev --android`") unless serial
113
+ UI.die!("no Android emulator or device. Start one with `every dev --android`") unless serial
114
114
 
115
115
  application_id = app_id || Everywhere::Config.load(Dir.pwd).bundle_id(target: "android-arm64")
116
116
  pid = Everywhere::Emulator.pid_of(serial, application_id)
117
117
  unless pid
118
- UI.die!("#{application_id} isn't running on #{serial} start it with `every dev --android` " \
118
+ UI.die!("#{application_id} isn't running on #{serial}. Start it with `every dev --android` " \
119
119
  "(or pass --app-id if that isn't this app's applicationId)")
120
120
  end
121
121
 
@@ -155,7 +155,7 @@ module Everywhere
155
155
  return (Time.now - seconds).strftime(LOGCAT_TIME)
156
156
  end
157
157
 
158
- UI.die!(%(--last takes a line count ("200") or a duration ("30s", "5m", "2h") got #{last.inspect}))
158
+ UI.die!(%(--last takes a line count ("200") or a duration ("30s", "5m", "2h"); got #{last.inspect}))
159
159
  end
160
160
 
161
161
  # "200" reads as a count, everything else as a duration.
@@ -18,7 +18,7 @@ module Everywhere
18
18
  def call(url: nil, **)
19
19
  base = Everywhere::Platform::Client.base_url(url)
20
20
  creds = Everywhere::Platform::Credentials.load.for(base)
21
- return UI.warn("not logged in to #{base} run `every platform login`") unless creds
21
+ return UI.warn("not logged in to #{base}. Run `every platform login`") unless creds
22
22
 
23
23
  res = Everywhere::Platform::Client.new(base, token: creds["token"]).get("/cli/token")
24
24
  if res.ok?
@@ -31,9 +31,9 @@ module Everywhere
31
31
  last = tok["last_used_at"] ? " · last used #{tok["last_used_at"]}" : ""
32
32
  UI.step("token: #{tok["hint"]}#{UI.dim(last)}")
33
33
  elsif res.unauthorized?
34
- UI.bad("saved token is invalid or revoked run `every platform login`")
34
+ UI.bad("saved token is invalid or revoked. Run `every platform login`")
35
35
  else
36
- UI.bad("couldn't check status HTTP #{res.status}")
36
+ UI.bad("couldn't check status: HTTP #{res.status}")
37
37
  end
38
38
  rescue Everywhere::Error => e
39
39
  UI.bad(e.message)
@@ -35,7 +35,7 @@ module Everywhere
35
35
  config = Everywhere::Config.load(root_path)
36
36
  base = Everywhere::Platform::Client.base_url(url)
37
37
  creds = Everywhere::Platform::Credentials.load.for(base)
38
- UI.die!("not logged in to #{base} run `every platform login`") unless creds
38
+ UI.die!("not logged in to #{base}. Run `every platform login`") unless creds
39
39
 
40
40
  client = Everywhere::Platform::Client.new(base, token: creds["token"])
41
41
  framework = config.remote? ? nil : Everywhere::Framework.detect(root_path)
@@ -129,7 +129,7 @@ module Everywhere
129
129
 
130
130
  case body["error"]
131
131
  when "missing_credentials"
132
- UI.bad("can't build yet missing signing credentials:")
132
+ UI.bad("can't build yet, missing signing credentials:")
133
133
  Array(body["targets"]).each do |t|
134
134
  UI.step("#{server_target_label(t["target"])}: add #{Array(t["missing"]).join(", ")}")
135
135
  end
@@ -154,7 +154,7 @@ module Everywhere
154
154
  end
155
155
 
156
156
  def die_unauthorized!
157
- UI.die!("token expired or revoked run `every platform login`")
157
+ UI.die!("token expired or revoked. Run `every platform login`")
158
158
  end
159
159
 
160
160
  # How often we ask the Platform for new logs. A build spends most of its
@@ -185,7 +185,7 @@ module Everywhere
185
185
  errors += 1
186
186
  status.clear
187
187
  detach!(build_id, build_url, e) if errors >= MAX_POLL_ERRORS
188
- UI.warn("lost contact with the Platform (#{e.message}) retrying #{errors}/#{MAX_POLL_ERRORS}")
188
+ UI.warn("lost contact with the Platform (#{e.message}), retrying #{errors}/#{MAX_POLL_ERRORS}")
189
189
  spin(status, [], started, IDLE_POLL)
190
190
  next
191
191
  end
@@ -210,7 +210,7 @@ module Everywhere
210
210
  # We stopped watching; the build didn't stop. Leave the user everything
211
211
  # they need to re-attach or grab the artifacts later.
212
212
  def detach!(build_id, build_url, error)
213
- UI.bad("gave up tailing #{UI.bold(build_id)} after #{MAX_POLL_ERRORS} failed polls #{error.message}")
213
+ UI.bad("gave up tailing #{UI.bold(build_id)} after #{MAX_POLL_ERRORS} failed polls: #{error.message}")
214
214
  UI.step("the build is still running on the Platform")
215
215
  UI.step("watch it → #{UI.cyan(build_url)}") if build_url
216
216
  UI.die!("re-attach with `every platform build --wait` once you're back online, " \
@@ -276,7 +276,7 @@ module Everywhere
276
276
  if artifact["status"] == "succeeded"
277
277
  report_success(client, artifact, out_dir, download)
278
278
  else
279
- UI.bad("#{label(artifact["target"])} #{artifact["error"] || "failed"}")
279
+ UI.bad("#{label(artifact["target"])}: #{artifact["error"] || "failed"}")
280
280
  end
281
281
  end
282
282
 
@@ -313,7 +313,7 @@ module Everywhere
313
313
  def parse_target_spec(token, default_channel)
314
314
  os_arch, channel, extra = token.split(":", -1)
315
315
  if !extra.nil? || os_arch.to_s.empty? || (!channel.nil? && channel.empty?)
316
- UI.die!("invalid target #{token.inspect} expected os-arch[:channel], e.g. ios-arm64:testflight")
316
+ UI.die!("invalid target #{token.inspect}: expected os-arch[:channel], e.g. ios-arm64:testflight")
317
317
  end
318
318
 
319
319
  os, arch = os_arch.split("-", 2)
@@ -23,13 +23,13 @@ module Everywhere
23
23
  client = Everywhere::Platform::Client.new(base)
24
24
 
25
25
  start = client.post("/cli/device_authorizations", client_name: client_name)
26
- UI.die!("couldn't start login HTTP #{start.status}") unless start.ok?
26
+ UI.die!("couldn't start login: HTTP #{start.status}") unless start.ok?
27
27
 
28
28
  # A captive portal or a misconfigured proxy answers 200 with HTML, which
29
29
  # parses to {} — without this we'd print an empty code and poll nothing
30
30
  # for ten minutes.
31
31
  unless start.body["user_code"] && start.body["device_code"]
32
- UI.die!("#{base} answered without a device code check EVERYWHERE_PLATFORM_URL " \
32
+ UI.die!("#{base} answered without a device code. Check EVERYWHERE_PLATFORM_URL " \
33
33
  "(a captive portal or proxy may be intercepting the request)")
34
34
  end
35
35
 
@@ -56,7 +56,7 @@ module Everywhere
56
56
  deadline = Clock.monotonic + (expires_in.positive? ? expires_in : 600)
57
57
  loop do
58
58
  sleep(interval)
59
- UI.die!("login timed out run `every platform login` again") if Clock.monotonic > deadline
59
+ UI.die!("login timed out. Run `every platform login` again") if Clock.monotonic > deadline
60
60
 
61
61
  # The user is mid-approval in a browser; a dropped connection here must
62
62
  # not kill the login. Keep polling until the deadline instead.
@@ -65,14 +65,14 @@ module Everywhere
65
65
  rescue Everywhere::Error
66
66
  next
67
67
  end
68
- UI.die!("login failed #{res.body["error"] || "invalid request"}") if res.status == 400
68
+ UI.die!("login failed: #{res.body["error"] || "invalid request"}") if res.status == 400
69
69
  next unless res.ok? # transient 5xx: keep polling until the deadline
70
70
 
71
71
  case res.body["status"]
72
72
  when "approved" then return res.body
73
73
  when "pending" then interval = [res.body["interval"].to_i, interval].max
74
74
  when "denied" then UI.die!("request denied in the browser")
75
- when "expired" then UI.die!("the code expired run `every platform login` again")
75
+ when "expired" then UI.die!("the code expired. Run `every platform login` again")
76
76
  end
77
77
  end
78
78
  end
@@ -24,7 +24,7 @@ module Everywhere
24
24
  begin
25
25
  Everywhere::Platform::Client.new(base, token: creds["token"]).delete("/cli/token")
26
26
  rescue Everywhere::Error => e
27
- UI.warn("couldn't reach the Platform to revoke (#{e.message}) removing local credential anyway")
27
+ UI.warn("couldn't reach the Platform to revoke (#{e.message}), removing local credential anyway")
28
28
  end
29
29
 
30
30
  store.delete(base)
@@ -38,9 +38,9 @@ module Everywhere
38
38
  ".aab" => "application/octet-stream"
39
39
  }.freeze
40
40
  RELEASE_NARRATION = {
41
- "macos" => "every release press → sign → notarize → staple",
42
- "ios" => "every release build → archive → export .ipa",
43
- "android" => "every release assemble → sign → bundle"
41
+ "macos" => "every release: press → sign → notarize → staple",
42
+ "ios" => "every release: build → archive → export .ipa",
43
+ "android" => "every release: assemble → sign → bundle"
44
44
  }.freeze
45
45
 
46
46
  # A .mobileprovision has to be in both: Xcode reads the UserData copy,
@@ -82,7 +82,7 @@ module Everywhere
82
82
  if status == "succeeded" && built
83
83
  complete_success(built, release_json)
84
84
  else
85
- complete_failure("build failed see log")
85
+ complete_failure("build failed, see log")
86
86
  end
87
87
  ensure
88
88
  restore_keychains
@@ -162,7 +162,7 @@ module Everywhere
162
162
  if Host.darwin?
163
163
  env["APPLE_SIGNING_IDENTITY"] = install_certificate(path, meta, work)
164
164
  else
165
- remote_log("skipping #{cred["kind"]} keychain import needs macOS")
165
+ remote_log("skipping #{cred["kind"]}: keychain import needs macOS")
166
166
  end
167
167
  when "apple_asc_api_key"
168
168
  env["NOTARY_KEY"] = path
@@ -290,7 +290,7 @@ module Everywhere
290
290
  signed_id = upload_artifact(built)
291
291
  post!("/runner/artifacts/#{@artifact}/complete",
292
292
  status: "succeeded", file_signed_id: signed_id, release_json: release_json)
293
- UI.success("build succeeded #{File.basename(built)}")
293
+ UI.success("build succeeded: #{File.basename(built)}")
294
294
  end
295
295
 
296
296
  # Best-effort: if the thing that failed WAS connectivity, reporting the