ruby_everywhere 0.12.0 → 0.13.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: 2d7635a3c08d751d6941f39975c28a1582a80b2190fb7da09403bfe6e4537136
4
- data.tar.gz: 03d515b70b0ced051c159ec1010c0a20addbe61024c1fb23694beab7e8bd1ca6
3
+ metadata.gz: 0afe4b448a6f964a0b013d31762aac332b71682717c04d25194271d9d1c92b67
4
+ data.tar.gz: '039d951d08c4d3be2d8a22c4de5a862d5dc1ca9635b978f989d4ba56807da148'
5
5
  SHA512:
6
- metadata.gz: 8f5683963a2de224a6810f0489b768c0160077c8e70fc2edb3bb9bdf3df8049262d5d4ed882d764f48fc033d690a6d61c0a16639f81d42dc961a1eae62e7ffb8
7
- data.tar.gz: ab10fdb4f6d80662325f942ae1b53f8bad702cd324db71fca7421094cc9bfaf3625398abda78f4b03ee2790d04056d447a5b5fe364b73ee233e4414fa8986f85
6
+ metadata.gz: 2656b49706209ee27c7aeb0410ea9ebff73ee833f58df43b9d51ceb2049b2e7540436b561e4a301fa89e6afb87c48056d1b0556598982cfaafb4367be55f8266
7
+ data.tar.gz: aca89fa867ba1aa8a1053e0d2324e358a02d9dd94e19129d984ca593e9598d3f031c91d95077b037fcb86a3a11d04535a0b70695f4eb3443ea7548f825617d83
data/README.md CHANGED
@@ -324,11 +324,13 @@ the middle:
324
324
  ```sh
325
325
  every updates keygen # once, per app
326
326
  every release # build, sign, notarize, staple
327
- every publish --notes-file NOTES.md # sign the manifest, upload, flip latest.json
327
+ every publish --notes-file NOTES.md # sign the artifact, upload, flip latest.json
328
328
  ```
329
329
 
330
- The public key ships in the app; the secret key never leaves your machine. Apps
331
- can check, install, and switch channels at runtime via `Everywhere.updates`.
330
+ `every publish` signs the artifact and publishes that signature in the update
331
+ manifest, which the app checks the download against before installing. The
332
+ public key ships in the app; the secret key never leaves your machine. Apps can
333
+ check, install, and switch channels at runtime via `Everywhere.updates`.
332
334
 
333
335
  ## Hosted builds
334
336
 
@@ -41,6 +41,28 @@ module Everywhere
41
41
 
42
42
  ANDROID_FORMATS = %w[apk aab].freeze
43
43
 
44
+ # Valid --channel per target os — the pairing the Platform accepts, minus
45
+ # nothing: macOS has no store channel (every macOS release signs with
46
+ # Developer ID and notarizes regardless), iOS `direct` exports an .ipa
47
+ # without the App Store Connect upload, and Android's `play` selects the
48
+ # .aab path even though the Play upload itself happens platform-side.
49
+ CHANNELS = {
50
+ "macos" => %w[direct],
51
+ "ios" => %w[direct testflight app_store],
52
+ "android" => %w[direct play]
53
+ }.freeze
54
+
55
+ # An os/channel pair nothing implements has to be refused before the
56
+ # build runs — it used to land in the receipt, where the only thing
57
+ # reading it (a store upload, a Platform distributor) has no idea what
58
+ # it means. Unknown os falls through: builder dispatch owns that error.
59
+ def self.check_channel!(os, channel)
60
+ allowed = CHANNELS[os] || return
61
+ return if allowed.include?(channel)
62
+
63
+ UI.die!("unsupported --channel #{channel} for #{os} — one of #{allowed.join(", ")}")
64
+ end
65
+
44
66
  desc "Package the app natively — desktop (tebako press + shell + .app), iOS (--ios) or Android (--android)"
45
67
 
46
68
  option :root, default: ".", desc: "App root directory"
@@ -52,7 +74,7 @@ module Everywhere
52
74
  option :shell_dir, default: nil, desc: "Path to the shell's src-tauri directory"
53
75
  option :template_dir, default: nil, desc: "Path to the mobile shell template (iOS/Android)"
54
76
  option :format, default: nil, desc: "Android artifact format (#{ANDROID_FORMATS.join(" | ")}), default: apk"
55
- option :channel, default: "direct", desc: "Distribution channel (direct | testflight | app_store | ...)"
77
+ option :channel, default: "direct", desc: "Distribution channel (#{CHANNELS.map { |os, chs| "#{os}: #{chs.join("|")}" }.join(" · ")})"
56
78
  option :ios, type: :boolean, default: false, desc: "Build the iOS shell (shorthand for --target #{IOS_TARGET})"
57
79
  option :android, type: :boolean, default: false, desc: "Build the Android shell (shorthand for --target #{ANDROID_TARGET})"
58
80
  option :device, type: :boolean, default: false, desc: "iOS: archive and export a device .ipa instead of a simulator .app"
@@ -62,6 +84,7 @@ module Everywhere
62
84
  app_name: nil, target: nil, shell_dir: nil, template_dir: nil, format: nil,
63
85
  channel: "direct", ios: false, android: false, device: false, skip_press: false, **)
64
86
  target = resolve_target(target, ios: ios, android: android)
87
+ self.class.check_channel!(Everywhere::Config.os_of(target), channel)
65
88
  root_path = File.expand_path(root)
66
89
  config = Everywhere::Config.load(root_path)
67
90
 
@@ -70,11 +93,7 @@ module Everywhere
70
93
  identity_errors = config.identity_errors
71
94
  UI.die!("app: in config/everywhere.yml:\n#{identity_errors.join("\n")}") unless identity_errors.empty?
72
95
 
73
- # Baked into the shell here, so this is the last moment an unusable
74
- # feed URL can still be a message instead of a shipped app that never
75
- # updates.
76
- updates_errors = config.updates_errors
77
- UI.die!("updates: in config/everywhere.yml:\n#{updates_errors.join("\n")}") unless updates_errors.empty?
96
+ check_updates!(config)
78
97
 
79
98
  # Builder dispatch on the target's os (build-engine.md §5): macOS is
80
99
  # the tebako+tauri path below; the mobile targets are Hotwire Native
@@ -150,6 +169,17 @@ module Everywhere
150
169
 
151
170
  private
152
171
 
172
+ # The `updates:` section is baked into the shell during this build, so
173
+ # this is the last moment an unusable feed URL — or a missing signing key,
174
+ # which turns the updater off without failing anything — can still be a
175
+ # message instead of a shipped app that never updates. Once per build:
176
+ # every target stamps everywhere.json from the same config.
177
+ def check_updates!(config)
178
+ errors = config.updates_errors
179
+ UI.die!("updates: in config/everywhere.yml:\n#{errors.join("\n")}") unless errors.empty?
180
+ config.updates_warnings.each { |warning| UI.warn(warning) }
181
+ end
182
+
153
183
  # `--ios` is the shorthand `every dev` already has; --target stays the
154
184
  # general escape hatch. Both given is only an error when they disagree —
155
185
  # `--ios --target ios-arm64` is redundant, not wrong.
@@ -33,7 +33,7 @@ module Everywhere
33
33
 
34
34
  option :root, default: ".", desc: "App root directory"
35
35
  option :target, default: Build::DEFAULT_TARGET, desc: "Build target (os-arch)"
36
- option :channel, default: "direct", desc: "Distribution channel (direct | mac_app_store | ...)"
36
+ option :channel, default: "direct", desc: "Distribution channel (#{Build::CHANNELS.map { |os, chs| "#{os}: #{chs.join("|")}" }.join(" · ")})"
37
37
  option :ruby, default: nil, desc: "Ruby version to package (default: build.ruby or 4.0.6)"
38
38
  option :entry, default: "native_boot.rb", desc: "Entry point script relative to root"
39
39
  option :app_name, default: nil, desc: "Bundle name (default from config)"
@@ -50,7 +50,7 @@ module Everywhere
50
50
  ruby ||= config.build_ruby || "4.0.6"
51
51
  os = Everywhere::Config.os_of(target)
52
52
 
53
- preflight!(target, os)
53
+ preflight!(target, os, channel)
54
54
 
55
55
  framework = config.remote? ? nil : Framework.detect(root_path)
56
56
  dist_dir = File.expand_path("dist", root_path)
@@ -82,7 +82,7 @@ module Everywhere
82
82
 
83
83
  private
84
84
 
85
- def preflight!(target, os)
85
+ def preflight!(target, os, channel)
86
86
  case os
87
87
  when "macos"
88
88
  UI.die!("`every release --target #{target}` runs on macOS only") unless Host.darwin?
@@ -94,6 +94,10 @@ module Everywhere
94
94
  else
95
95
  UI.die!("no release builder for #{target} — macos-*, ios-* and android-* are supported")
96
96
  end
97
+
98
+ # After the os check: the channel table is keyed by os, so an unknown
99
+ # target has to die on the missing builder, not a nil channel list.
100
+ Build.check_channel!(os, channel)
97
101
  end
98
102
 
99
103
 
@@ -44,6 +44,16 @@ module Everywhere
44
44
  ["updates.url #{url.inspect} is not a URL (#{e.message})"]
45
45
  end
46
46
 
47
+ # Not an error — no `updates:` at all is a perfectly good app — but a feed
48
+ # URL without a key drops #updates_shell_hash to nil, so the app ships
49
+ # looking configured with the updater off.
50
+ def updates_warnings
51
+ return [] unless updates_url && updates_public_key.to_s.empty?
52
+
53
+ ["updates.url is set but updates.public_key is missing — the updater will be disabled " \
54
+ "in this build; run `every updates keygen`"]
55
+ end
56
+
47
57
  # The client subset for the shell; nil (and thus absent from
48
58
  # everywhere.json) until both url and public_key are configured — an
49
59
  # unsigned update feed is not a thing.
@@ -3,7 +3,7 @@
3
3
  require "json"
4
4
 
5
5
  module Everywhere
6
- VERSION = "0.12.0"
6
+ VERSION = "0.13.0"
7
7
 
8
8
  # Version of the @rubyeverywhere/bridge JS this gem ships. bridge/ in the
9
9
  # gem IS the npm package (served to Rails apps by Everywhere::Engine,
@@ -480,7 +480,7 @@ fn verify_signature(path: &Path, signature: &str, public_key: &str) -> Result<()
480
480
  let pk = minisign_verify::PublicKey::from_base64(public_key)
481
481
  .map_err(|e| format!("bad updates.public_key: {e}"))?;
482
482
  let sig = minisign_verify::Signature::decode(signature)
483
- .map_err(|e| format!("bad manifest signature: {e}"))?;
483
+ .map_err(|e| format!("unreadable artifact signature: {e}"))?;
484
484
  let data = std::fs::read(path).map_err(|e| e.to_string())?;
485
485
  pk.verify(&data, &sig, false)
486
486
  .map_err(|_| "signature verification FAILED — artifact does not match the app's signing key".to_string())
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_everywhere
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrea Fomera