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.
- checksums.yaml +4 -4
- data/README.md +30 -30
- data/bridge/README.md +23 -23
- data/lib/everywhere/android_resources.rb +9 -9
- data/lib/everywhere/android_sdk.rb +9 -9
- data/lib/everywhere/asset_catalog.rb +7 -7
- data/lib/everywhere/blake2b.rb +1 -1
- data/lib/everywhere/boot.rb +1 -1
- data/lib/everywhere/builders/android.rb +25 -25
- data/lib/everywhere/builders/desktop.rb +6 -6
- data/lib/everywhere/builders/ios.rb +14 -14
- data/lib/everywhere/builders/native_sources.rb +1 -1
- data/lib/everywhere/child_supervision.rb +1 -1
- data/lib/everywhere/commands/build.rb +19 -19
- data/lib/everywhere/commands/clean.rb +1 -1
- data/lib/everywhere/commands/dev.rb +11 -11
- data/lib/everywhere/commands/doctor.rb +7 -7
- data/lib/everywhere/commands/icon.rb +2 -2
- data/lib/everywhere/commands/install.rb +21 -21
- data/lib/everywhere/commands/logs.rb +6 -6
- data/lib/everywhere/commands/platform/auth_status.rb +3 -3
- data/lib/everywhere/commands/platform/build.rb +7 -7
- data/lib/everywhere/commands/platform/login.rb +5 -5
- data/lib/everywhere/commands/platform/logout.rb +1 -1
- data/lib/everywhere/commands/platform/runner.rb +6 -6
- data/lib/everywhere/commands/preview.rb +14 -14
- data/lib/everywhere/commands/publish.rb +14 -14
- data/lib/everywhere/commands/release.rb +6 -6
- data/lib/everywhere/commands/updates_keygen.rb +4 -4
- data/lib/everywhere/config/app.rb +3 -3
- data/lib/everywhere/config/mobile.rb +2 -2
- data/lib/everywhere/config/native_desktop.rb +2 -2
- data/lib/everywhere/config/native_mobile.rb +3 -3
- data/lib/everywhere/config/updates.rb +2 -2
- data/lib/everywhere/database.rb +3 -3
- data/lib/everywhere/desktop_assets.rb +5 -5
- data/lib/everywhere/desktop_dev_app.rb +1 -1
- data/lib/everywhere/emulator.rb +5 -5
- data/lib/everywhere/log_filter.rb +1 -1
- data/lib/everywhere/omniauth.rb +1 -1
- data/lib/everywhere/paths.rb +1 -1
- data/lib/everywhere/platform/credentials.rb +1 -1
- data/lib/everywhere/request_context.rb +2 -2
- data/lib/everywhere/shellout.rb +2 -2
- data/lib/everywhere/simulator.rb +1 -1
- data/lib/everywhere/tab_filter.rb +2 -2
- data/lib/everywhere/version.rb +1 -1
- data/support/agents/AGENTS.md +7 -7
- data/support/agents/frameworks/hanami-views.md +8 -8
- data/support/agents/frameworks/hanami.md +2 -2
- data/support/agents/frameworks/rails-views.md +7 -7
- data/support/agents/frameworks/rails.md +8 -8
- data/support/agents/frameworks/sinatra-views.md +7 -7
- data/support/agents/frameworks/sinatra.md +2 -2
- data/support/agents/modes/local.md +2 -2
- data/support/agents/modes/remote.md +2 -2
- metadata +1 -1
|
@@ -173,7 +173,7 @@ module Everywhere
|
|
|
173
173
|
@version_code = version_code
|
|
174
174
|
|
|
175
175
|
unless dev || @config.remote?
|
|
176
|
-
UI.die!("Android apps are remote mode only for now
|
|
176
|
+
UI.die!("Android apps are remote mode only for now. Set remote.url in config/everywhere.yml " \
|
|
177
177
|
"(the shell wraps your deployed app; nothing is packaged locally)")
|
|
178
178
|
end
|
|
179
179
|
|
|
@@ -184,7 +184,7 @@ module Everywhere
|
|
|
184
184
|
# package name. Caught here so the answer is a config key, not a
|
|
185
185
|
# Gradle stacktrace.
|
|
186
186
|
unless application_id.match?(APPLICATION_ID)
|
|
187
|
-
UI.die!("#{application_id.inspect} can't be an Android applicationId
|
|
187
|
+
UI.die!("#{application_id.inspect} can't be an Android applicationId. It needs two or " \
|
|
188
188
|
"more dot-separated segments of letters, digits and _, each starting with a " \
|
|
189
189
|
"letter. Set platforms.android.bundle_id in config/everywhere.yml")
|
|
190
190
|
end
|
|
@@ -208,12 +208,12 @@ module Everywhere
|
|
|
208
208
|
|
|
209
209
|
def check_format!(dev:, format:)
|
|
210
210
|
unless FORMATS.key?(format)
|
|
211
|
-
UI.die!("#{format.inspect} isn't an Android package format
|
|
211
|
+
UI.die!("#{format.inspect} isn't an Android package format. Use :apk (installable) or :aab " \
|
|
212
212
|
"(what Google Play accepts)")
|
|
213
213
|
end
|
|
214
214
|
return unless format == :aab && dev
|
|
215
215
|
|
|
216
|
-
UI.die!("an .aab can't be installed on a device or an emulator
|
|
216
|
+
UI.die!("an .aab can't be installed on a device or an emulator. It's an upload format Play " \
|
|
217
217
|
"re-signs into per-device APKs. `every dev` builds an APK; ask for the bundle when " \
|
|
218
218
|
"you're publishing")
|
|
219
219
|
end
|
|
@@ -231,7 +231,7 @@ module Everywhere
|
|
|
231
231
|
missing = REQUIRED_SIGNING_ENV.reject { |name| env_value(name) }
|
|
232
232
|
unless missing.empty?
|
|
233
233
|
UI.die!("EVERY_ANDROID_KEYSTORE is set but #{missing.join(" and ")} " \
|
|
234
|
-
"#{missing.one? ? "is" : "are"} not
|
|
234
|
+
"#{missing.one? ? "is" : "are"} not. Signing needs the keystore, its password " \
|
|
235
235
|
"and the alias of the key inside it (EVERY_ANDROID_KEY_PASSWORD falls back to " \
|
|
236
236
|
"the keystore password)")
|
|
237
237
|
end
|
|
@@ -316,7 +316,7 @@ module Everywhere
|
|
|
316
316
|
xml = <<~XML
|
|
317
317
|
<?xml version="1.0" encoding="utf-8"?>
|
|
318
318
|
<!-- Written by `every build` because config/everywhere.yml sets
|
|
319
|
-
remote.instances: true
|
|
319
|
+
remote.instances: true, and previewed apps arrive as plain http on
|
|
320
320
|
LAN addresses that cannot be enumerated at build time. Do not
|
|
321
321
|
edit; the template's strict default returns if instances is
|
|
322
322
|
turned off. -->
|
|
@@ -344,7 +344,7 @@ module Everywhere
|
|
|
344
344
|
# opted in, so the shell's own tested level is the default.
|
|
345
345
|
def write_properties(work)
|
|
346
346
|
File.write(File.join(work, "app", "everywhere.properties"), <<~PROPERTIES)
|
|
347
|
-
# Written by `every build --target #{@target}
|
|
347
|
+
# Written by `every build --target #{@target}`. Do not edit; changes
|
|
348
348
|
# belong in config/everywhere.yml.
|
|
349
349
|
everywhere.applicationId=#{properties_value(application_id)}
|
|
350
350
|
everywhere.versionName=#{properties_value(@config.version(target: @target))}
|
|
@@ -365,7 +365,7 @@ module Everywhere
|
|
|
365
365
|
override = env_value(VERSION_CODE_ENV) or return derived_version_code
|
|
366
366
|
|
|
367
367
|
unless override.match?(/\A[1-9]\d*\z/)
|
|
368
|
-
UI.die!("#{VERSION_CODE_ENV}=#{override.inspect}
|
|
368
|
+
UI.die!("#{VERSION_CODE_ENV}=#{override.inspect}: a versionCode is a positive integer, " \
|
|
369
369
|
"and Play only accepts an upload whose code is higher than the last one's")
|
|
370
370
|
end
|
|
371
371
|
override.to_i
|
|
@@ -431,7 +431,7 @@ module Everywhere
|
|
|
431
431
|
def write_launcher_icons(work)
|
|
432
432
|
source = @config.icon
|
|
433
433
|
unless source && File.exist?(source)
|
|
434
|
-
UI.warn "no app icon (set app.icon or add icon.png)
|
|
434
|
+
UI.warn "no app icon (set app.icon or add icon.png). Shipping the placeholder"
|
|
435
435
|
return
|
|
436
436
|
end
|
|
437
437
|
|
|
@@ -439,7 +439,7 @@ module Everywhere
|
|
|
439
439
|
res = File.join(work, "app", "src", "main", "res")
|
|
440
440
|
return if Icon.write_android_mipmaps(source, res, background: background)
|
|
441
441
|
|
|
442
|
-
UI.warn "couldn't read #{File.basename(source)}
|
|
442
|
+
UI.warn "couldn't read #{File.basename(source)}. Shipping the placeholder icon"
|
|
443
443
|
end
|
|
444
444
|
|
|
445
445
|
# App-supplied images: native/android/assets/** → drawable resources, so
|
|
@@ -495,12 +495,12 @@ module Everywhere
|
|
|
495
495
|
return if tabs.length <= BAR_SLOTS
|
|
496
496
|
|
|
497
497
|
overflow = tabs.drop(BAR_TABS_WITH_MORE).map { |tab| tab["title"] }
|
|
498
|
-
UI.detail "#{tabs.length} tabs: Android's bar holds #{BAR_TABS_WITH_MORE} plus a \"More\" tab
|
|
498
|
+
UI.detail "#{tabs.length} tabs: Android's bar holds #{BAR_TABS_WITH_MORE} plus a \"More\" tab. " \
|
|
499
499
|
"#{overflow.map(&:inspect).join(", ")} open from there (iOS does the same past five)"
|
|
500
500
|
|
|
501
501
|
return if tabs.length <= CROWDED_TABS
|
|
502
502
|
|
|
503
|
-
UI.warn "#{overflow.length} tabs behind \"More\" is a long list to scroll on a phone
|
|
503
|
+
UI.warn "#{overflow.length} tabs behind \"More\" is a long list to scroll on a phone. " \
|
|
504
504
|
"consider moving the rarer ones into the app's own navigation"
|
|
505
505
|
end
|
|
506
506
|
|
|
@@ -514,7 +514,7 @@ module Everywhere
|
|
|
514
514
|
def resolve_icon_font!(template)
|
|
515
515
|
file = @config.native_android_icon_font_file
|
|
516
516
|
@icon_font = nil
|
|
517
|
-
return unless file # icon_font: none
|
|
517
|
+
return unless file # icon_font: none, so the app brings its own drawables
|
|
518
518
|
|
|
519
519
|
@icon_font = if @config.native_android_icon_font_fetched?
|
|
520
520
|
fetch_icon_font!(file)
|
|
@@ -529,7 +529,7 @@ module Everywhere
|
|
|
529
529
|
dir = BUNDLED_FONT_DIRS.map { |parts| File.join(template, *parts) }
|
|
530
530
|
.find { |candidate| File.file?(File.join(candidate, file)) }
|
|
531
531
|
unless dir
|
|
532
|
-
UI.die!("the Android template has no #{file}
|
|
532
|
+
UI.die!("the Android template has no #{file}. Reinstall ruby_everywhere, or set " \
|
|
533
533
|
"native.android.icon_font: symbols in config/everywhere.yml to fetch one instead")
|
|
534
534
|
end
|
|
535
535
|
|
|
@@ -557,7 +557,7 @@ module Everywhere
|
|
|
557
557
|
self.class.fetch!(icon_font_url(pin["path"], ".codepoints"), codepoints, sha256: pin["codepoints"])
|
|
558
558
|
resolved
|
|
559
559
|
rescue StandardError => e
|
|
560
|
-
UI.die!("couldn't fetch the #{@config.native_android_icon_font} icon font (#{e.message})
|
|
560
|
+
UI.die!("couldn't fetch the #{@config.native_android_icon_font} icon font (#{e.message}). " \
|
|
561
561
|
"set native.android.icon_font: classic in config/everywhere.yml to use the 2,235-icon " \
|
|
562
562
|
"set bundled in the gem, which needs no download and works offline")
|
|
563
563
|
end
|
|
@@ -630,8 +630,8 @@ module Everywhere
|
|
|
630
630
|
icon = tab["icon"].to_s
|
|
631
631
|
base = icon.sub(/\.fill\z/, "")
|
|
632
632
|
if base != icon && !fillable
|
|
633
|
-
"tab #{tab["title"].inspect}: #{icon.inspect}
|
|
634
|
-
"
|
|
633
|
+
"tab #{tab["title"].inspect}: #{icon.inspect} needs a FILL axis, which icon_font: " \
|
|
634
|
+
"#{@config.native_android_icon_font} lacks; use #{base.inspect}, or switch to icon_font: symbols"
|
|
635
635
|
elsif !names.include?(base)
|
|
636
636
|
"tab #{tab["title"].inspect}: #{icon.inspect} isn't an icon in #{font["file"]}"
|
|
637
637
|
end
|
|
@@ -639,7 +639,7 @@ module Everywhere
|
|
|
639
639
|
return if problems.empty?
|
|
640
640
|
|
|
641
641
|
UI.die!("icons.android in config/everywhere.yml (#{names.size} icons in " \
|
|
642
|
-
"#{font["file"]}
|
|
642
|
+
"#{font["file"]}, browse them at https://fonts.google.com/icons):\n#{problems.join("\n")}")
|
|
643
643
|
end
|
|
644
644
|
|
|
645
645
|
# "name<space>hex" per line. Parsed in Ruby rather than shelled out to awk
|
|
@@ -690,7 +690,7 @@ module Everywhere
|
|
|
690
690
|
Do not edit; this source set is merged into app/src/main/AndroidManifest.xml.
|
|
691
691
|
Note for whoever edits this header next: XML forbids a double hyphen
|
|
692
692
|
anywhere inside a comment, so the flag spelling of the target must not
|
|
693
|
-
appear here. aapt2 does not warn
|
|
693
|
+
appear here. aapt2 does not warn: the manifest merger just fails to
|
|
694
694
|
parse the file, minutes into a Gradle run. -->
|
|
695
695
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
696
696
|
#{body.empty? ? " <!-- nothing declared: no permissions, deep links or sign-in scheme -->" : body}
|
|
@@ -731,7 +731,7 @@ module Everywhere
|
|
|
731
731
|
UI.step("stamping deep links #{UI.dim("(#{domains.join(", ")})")}")
|
|
732
732
|
unless @config.deep_linking_android?
|
|
733
733
|
UI.warn "deep links won't auto-verify until deep_linking.android.package and " \
|
|
734
|
-
".sha256_cert_fingerprints are set
|
|
734
|
+
".sha256_cert_fingerprints are set. Until then they open in the browser"
|
|
735
735
|
end
|
|
736
736
|
|
|
737
737
|
domains.map do |host|
|
|
@@ -804,7 +804,7 @@ module Everywhere
|
|
|
804
804
|
UI.step("bundling native extensions #{UI.dim("(#{sources.length} Kotlin files from native/android)")}")
|
|
805
805
|
elsif sources.any?
|
|
806
806
|
UI.warn "native/android has Kotlin files but everywhere.yml declares nothing under " \
|
|
807
|
-
"native.android
|
|
807
|
+
"native.android. They compile in, but no components/screens/splash are hooked up"
|
|
808
808
|
end
|
|
809
809
|
File.write(File.join(dest, "EverywhereExtensions.kt"), extensions_registry)
|
|
810
810
|
end
|
|
@@ -826,7 +826,7 @@ module Everywhere
|
|
|
826
826
|
return if declared == EXTENSIONS_PACKAGE
|
|
827
827
|
|
|
828
828
|
found = declared ? "declares package #{declared}" : "declares no package"
|
|
829
|
-
UI.die!("native/android/#{relative} #{found}
|
|
829
|
+
UI.die!("native/android/#{relative} #{found}. Every Kotlin file here compiles into the " \
|
|
830
830
|
"shell's extension package, so it has to start with:\n\n package #{EXTENSIONS_PACKAGE}\n")
|
|
831
831
|
end
|
|
832
832
|
|
|
@@ -846,7 +846,7 @@ module Everywhere
|
|
|
846
846
|
splash = @config.native_android_splash
|
|
847
847
|
|
|
848
848
|
<<~KOTLIN
|
|
849
|
-
// Written by `every build` from config/everywhere.yml (native.android)
|
|
849
|
+
// Written by `every build` from config/everywhere.yml (native.android). Do
|
|
850
850
|
// not edit; changes belong in native/android/ and everywhere.yml.
|
|
851
851
|
//
|
|
852
852
|
// A declared component is registered under the name its own class states:
|
|
@@ -897,7 +897,7 @@ module Everywhere
|
|
|
897
897
|
lines = packages.map { |coordinate| " add(\"implementation\", \"#{coordinate}\")" }
|
|
898
898
|
File.write(File.join(work, "app", "native-packages.gradle.kts"), <<~KOTLIN)
|
|
899
899
|
// Written by `every build` from config/everywhere.yml (native.android.packages)
|
|
900
|
-
//
|
|
900
|
+
// Do not edit; changes belong in config/everywhere.yml.
|
|
901
901
|
dependencies {
|
|
902
902
|
#{lines.join("\n")}
|
|
903
903
|
}
|
|
@@ -966,7 +966,7 @@ module Everywhere
|
|
|
966
966
|
UI.die!("Gradle reported success but no #{kind} in #{UI.short_path(dir)}") unless built
|
|
967
967
|
|
|
968
968
|
if @signing && built == unsigned
|
|
969
|
-
UI.die!("Gradle wrote #{File.basename(built)}
|
|
969
|
+
UI.die!("Gradle wrote #{File.basename(built)}, but EVERY_ANDROID_KEYSTORE is set, so the " \
|
|
970
970
|
"#{kind} should have been signed. Check the keystore's alias and passwords")
|
|
971
971
|
end
|
|
972
972
|
|
|
@@ -119,7 +119,7 @@ module Everywhere
|
|
|
119
119
|
path = File.join(work, "Cargo.toml")
|
|
120
120
|
source = File.read(path)
|
|
121
121
|
unless source.include?(CRATES_MARKER)
|
|
122
|
-
UI.die!("the shell's Cargo.toml has no #{CRATES_MARKER} marker
|
|
122
|
+
UI.die!("the shell's Cargo.toml has no #{CRATES_MARKER} marker. " \
|
|
123
123
|
"reinstall ruby_everywhere or pass --shell-dir at your own risk")
|
|
124
124
|
end
|
|
125
125
|
|
|
@@ -186,7 +186,7 @@ module Everywhere
|
|
|
186
186
|
# declare the file in everywhere.yml, it's to delete it.
|
|
187
187
|
sources = NativeSources.copy!(from: source, into: dest, ext: "rs") do |_path, relative|
|
|
188
188
|
if File.basename(relative) == MODULE_FILE
|
|
189
|
-
UI.die!("#{File.join(SOURCE_DIR, relative)} is generated by `every build
|
|
189
|
+
UI.die!("#{File.join(SOURCE_DIR, relative)} is generated by `every build`. " \
|
|
190
190
|
"remove it and let the CLI write the module declarations")
|
|
191
191
|
end
|
|
192
192
|
end
|
|
@@ -222,7 +222,7 @@ module Everywhere
|
|
|
222
222
|
|
|
223
223
|
def child_module(children)
|
|
224
224
|
<<~RUST
|
|
225
|
-
// GENERATED by `every build
|
|
225
|
+
// GENERATED by `every build`. Edits are overwritten.
|
|
226
226
|
// Declares the .rs files this directory holds in native/desktop/.
|
|
227
227
|
#{children.map { |name| "pub mod #{name};" }.join("\n")}
|
|
228
228
|
RUST
|
|
@@ -244,7 +244,7 @@ module Everywhere
|
|
|
244
244
|
<<~RUST
|
|
245
245
|
// GENERATED by `every build` / `every dev` from `native.desktop` in
|
|
246
246
|
// config/everywhere.yml, alongside the app's own .rs files copied from
|
|
247
|
-
// native/desktop/. Edits are overwritten
|
|
247
|
+
// native/desktop/. Edits are overwritten; see support/desktop/README.md.
|
|
248
248
|
|
|
249
249
|
use crate::commands;
|
|
250
250
|
|
|
@@ -256,7 +256,7 @@ module Everywhere
|
|
|
256
256
|
// is the right way to find out.
|
|
257
257
|
#{glob_imports(paths)}
|
|
258
258
|
|
|
259
|
-
// The shell's own commands. App extensions answer over events instead
|
|
259
|
+
// The shell's own commands. App extensions answer over events instead,
|
|
260
260
|
// see extension_host.rs for why invoke isn't available to them.
|
|
261
261
|
pub fn handler() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
|
|
262
262
|
tauri::generate_handler![commands::notify_command]
|
|
@@ -274,7 +274,7 @@ module Everywhere
|
|
|
274
274
|
) -> Result<serde_json::Value, String> {
|
|
275
275
|
match name {
|
|
276
276
|
#{dispatch_arms(commands)} _ => Err(format!(
|
|
277
|
-
"no desktop command named {name:?}
|
|
277
|
+
"no desktop command named {name:?}. Declare it under native.desktop.commands"
|
|
278
278
|
)),
|
|
279
279
|
}
|
|
280
280
|
}
|
|
@@ -90,14 +90,14 @@ module Everywhere
|
|
|
90
90
|
|
|
91
91
|
def preflight!(dev:, device:)
|
|
92
92
|
unless dev || @config.remote?
|
|
93
|
-
UI.die!("iOS apps are remote mode only for now
|
|
93
|
+
UI.die!("iOS apps are remote mode only for now. Set remote.url in config/everywhere.yml " \
|
|
94
94
|
"(the shell wraps your deployed app; nothing is packaged locally)")
|
|
95
95
|
end
|
|
96
96
|
UI.die!("iOS builds need macOS with Xcode installed") unless Host.darwin?
|
|
97
97
|
return device_preflight! if device
|
|
98
98
|
return if Shellout.run?("xcrun", "--sdk", "iphonesimulator", "--show-sdk-path", quiet: true)
|
|
99
99
|
|
|
100
|
-
UI.die!("no iOS Simulator SDK
|
|
100
|
+
UI.die!("no iOS Simulator SDK. Install full Xcode (not just the Command Line Tools) " \
|
|
101
101
|
"and run: xcode-select --switch /Applications/Xcode.app")
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -106,17 +106,17 @@ module Everywhere
|
|
|
106
106
|
# its own vocabulary.
|
|
107
107
|
def device_preflight!
|
|
108
108
|
unless Shellout.run?("xcrun", "--sdk", "iphoneos", "--show-sdk-path", quiet: true)
|
|
109
|
-
UI.die!("no iOS device SDK
|
|
109
|
+
UI.die!("no iOS device SDK. Install full Xcode (not just the Command Line Tools) " \
|
|
110
110
|
"and run: xcode-select --switch /Applications/Xcode.app")
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
missing = REQUIRED_SIGNING_ENV.reject { |name, _| env_value(name) }
|
|
114
114
|
unless missing.empty?
|
|
115
115
|
UI.die!("device builds need signing credentials in the environment:\n" \
|
|
116
|
-
"#{missing.map { |name, hint| " #{name}
|
|
116
|
+
"#{missing.map { |name, hint| " #{name}: #{hint}" }.join("\n")}")
|
|
117
117
|
end
|
|
118
118
|
unless EXPORT_METHODS.include?(export_method)
|
|
119
|
-
UI.die!("EVERY_IOS_EXPORT_METHOD=#{export_method} isn't an Xcode export method
|
|
119
|
+
UI.die!("EVERY_IOS_EXPORT_METHOD=#{export_method} isn't an Xcode export method. " \
|
|
120
120
|
"use one of: #{EXPORT_METHODS.join(", ")}")
|
|
121
121
|
end
|
|
122
122
|
asc_preflight! if store_channel?
|
|
@@ -129,12 +129,12 @@ module Everywhere
|
|
|
129
129
|
.map { |_field, names| names.first }
|
|
130
130
|
unless missing.empty?
|
|
131
131
|
UI.die!("--channel #{@channel} uploads to App Store Connect, which needs an API key " \
|
|
132
|
-
"in the environment
|
|
132
|
+
"in the environment. Missing: #{missing.join(", ")}")
|
|
133
133
|
end
|
|
134
134
|
return if export_method == DEFAULT_EXPORT_METHOD
|
|
135
135
|
|
|
136
136
|
UI.die!("--channel #{@channel} uploads to App Store Connect, which only accepts " \
|
|
137
|
-
"#{DEFAULT_EXPORT_METHOD} builds
|
|
137
|
+
"#{DEFAULT_EXPORT_METHOD} builds. Unset EVERY_IOS_EXPORT_METHOD=#{export_method}")
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
def asc_env(field) = ASC_ENV.fetch(field).filter_map { |name| env_value(name) }.first
|
|
@@ -284,7 +284,7 @@ module Everywhere
|
|
|
284
284
|
FileUtils.mkdir_p(dest)
|
|
285
285
|
File.write(File.join(dest, "EverywhereExtensions.swift"), extensions_registry)
|
|
286
286
|
elsif sources.any?
|
|
287
|
-
UI.warn "native/ios has Swift files but everywhere.yml declares nothing under native.ios
|
|
287
|
+
UI.warn "native/ios has Swift files but everywhere.yml declares nothing under native.ios. " \
|
|
288
288
|
"they compile in, but no components/screens/splash are hooked up"
|
|
289
289
|
end
|
|
290
290
|
end
|
|
@@ -309,7 +309,7 @@ module Everywhere
|
|
|
309
309
|
end
|
|
310
310
|
|
|
311
311
|
<<~SWIFT
|
|
312
|
-
// Written by `every build` from config/everywhere.yml (native.ios)
|
|
312
|
+
// Written by `every build` from config/everywhere.yml (native.ios). Do
|
|
313
313
|
// not edit; changes belong in native/ios/ and everywhere.yml.
|
|
314
314
|
import HotwireNative
|
|
315
315
|
import SwiftUI
|
|
@@ -368,7 +368,7 @@ module Everywhere
|
|
|
368
368
|
<<~SWIFT
|
|
369
369
|
// swift-tools-version:5.9
|
|
370
370
|
// Written by `every build` from config/everywhere.yml (native.ios.packages)
|
|
371
|
-
//
|
|
371
|
+
// Do not edit; changes belong in config/everywhere.yml.
|
|
372
372
|
import PackageDescription
|
|
373
373
|
|
|
374
374
|
let package = Package(
|
|
@@ -390,7 +390,7 @@ module Everywhere
|
|
|
390
390
|
def native_package_exports(packages)
|
|
391
391
|
header = <<~SWIFT
|
|
392
392
|
// Written by `every build` from config/everywhere.yml (native.ios.packages)
|
|
393
|
-
//
|
|
393
|
+
// Re-exports each pinned product so native/ios code reaches it with a
|
|
394
394
|
// single `import NativeExtensions`. Do not edit.
|
|
395
395
|
SWIFT
|
|
396
396
|
modules = packages.flat_map { |pkg| pkg["products"] }.uniq.sort
|
|
@@ -489,7 +489,7 @@ module Everywhere
|
|
|
489
489
|
# and no deep linking gets the setting just the same.
|
|
490
490
|
entitlements = "CODE_SIGN_ENTITLEMENTS = App/App.entitlements\n" if entitlement_keys.any?
|
|
491
491
|
File.write(File.join(work, "App", "App.xcconfig"), <<~XCCONFIG)
|
|
492
|
-
// Written by `every build --target #{@target}
|
|
492
|
+
// Written by `every build --target #{@target}`. Do not edit; changes
|
|
493
493
|
// belong in config/everywhere.yml.
|
|
494
494
|
PRODUCT_BUNDLE_IDENTIFIER = #{xcconfig_value(bundle_id)}
|
|
495
495
|
MARKETING_VERSION = #{xcconfig_value(@config.version(target: @target))}
|
|
@@ -526,7 +526,7 @@ module Everywhere
|
|
|
526
526
|
def write_app_icon(work)
|
|
527
527
|
source = @config.icon
|
|
528
528
|
unless source && File.exist?(source)
|
|
529
|
-
UI.warn "no app icon (set app.icon or add icon.png)
|
|
529
|
+
UI.warn "no app icon (set app.icon or add icon.png). Shipping the placeholder"
|
|
530
530
|
return
|
|
531
531
|
end
|
|
532
532
|
|
|
@@ -534,7 +534,7 @@ module Everywhere
|
|
|
534
534
|
dest = File.join(work, "App", "Assets.xcassets", "AppIcon.appiconset")
|
|
535
535
|
return if Icon.write_ios_appiconset(source, dest, background: background)
|
|
536
536
|
|
|
537
|
-
UI.warn "couldn't read #{File.basename(source)}
|
|
537
|
+
UI.warn "couldn't read #{File.basename(source)}. Shipping the placeholder icon"
|
|
538
538
|
end
|
|
539
539
|
|
|
540
540
|
# App-supplied images: native/ios/assets/** → imagesets in the template's
|
|
@@ -22,7 +22,7 @@ module Everywhere
|
|
|
22
22
|
relative = file.delete_prefix("#{from}/")
|
|
23
23
|
if reserved && File.basename(file) == reserved
|
|
24
24
|
UI.die!("#{label}/#{relative}: #{reserved} is generated by " \
|
|
25
|
-
"`every build
|
|
25
|
+
"`every build`. Declare your types under #{label.tr("/", ".")} " \
|
|
26
26
|
"in config/everywhere.yml instead")
|
|
27
27
|
end
|
|
28
28
|
yield(file, relative) if block_given?
|
|
@@ -160,7 +160,7 @@ module Everywhere
|
|
|
160
160
|
deadline = Clock.monotonic + timeout
|
|
161
161
|
until port_open?(port)
|
|
162
162
|
if pid && !ChildProcesses.alive?(pid)
|
|
163
|
-
UI.die!("the dev server exited before it finished booting
|
|
163
|
+
UI.die!("the dev server exited before it finished booting. Scroll up for its output#{dead_hint}")
|
|
164
164
|
end
|
|
165
165
|
if Clock.monotonic > deadline
|
|
166
166
|
UI.die!("dev server never came up on port #{port} after #{timeout}s#{slow_hint}")
|
|
@@ -60,17 +60,17 @@ module Everywhere
|
|
|
60
60
|
allowed = CHANNELS[os] || return
|
|
61
61
|
return if allowed.include?(channel)
|
|
62
62
|
|
|
63
|
-
UI.die!("unsupported --channel #{channel} for #{os}
|
|
63
|
+
UI.die!("unsupported --channel #{channel} for #{os}: use one of #{allowed.join(", ")}")
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
desc "Package the app natively
|
|
66
|
+
desc "Package the app natively: desktop (tebako press + shell + .app), iOS (--ios) or Android (--android)"
|
|
67
67
|
|
|
68
68
|
option :root, default: ".", desc: "App root directory"
|
|
69
69
|
option :output, default: nil, desc: "Output binary path (default: dist/<app>)"
|
|
70
70
|
option :ruby, default: "4.0.6", desc: "Ruby version to package"
|
|
71
71
|
option :entry, default: "native_boot.rb", desc: "Entry point script relative to root"
|
|
72
72
|
option :app_name, default: nil, desc: "Bundle name (default: capitalized app dir)"
|
|
73
|
-
option :target, default: nil, desc: "Build target (os-arch)
|
|
73
|
+
option :target, default: nil, desc: "Build target (os-arch); selects any platforms: overrides, default: \"#{DEFAULT_TARGET}\""
|
|
74
74
|
option :shell_dir, default: nil, desc: "Path to the shell's src-tauri directory"
|
|
75
75
|
option :template_dir, default: nil, desc: "Path to the mobile shell template (iOS/Android)"
|
|
76
76
|
option :format, default: nil, desc: "Android artifact format (#{ANDROID_FORMATS.join(" | ")}), default: apk"
|
|
@@ -115,7 +115,7 @@ module Everywhere
|
|
|
115
115
|
# thirty minutes in, at the first codesign.
|
|
116
116
|
UI.die!("desktop builds are macOS-only for now") unless Host.darwin?
|
|
117
117
|
else
|
|
118
|
-
UI.die!("no builder for target #{target}
|
|
118
|
+
UI.die!("no builder for target #{target}: macos-*, ios-* and android-* are supported")
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# Desktop chrome is validated here rather than in the shell: a typo in
|
|
@@ -150,7 +150,7 @@ module Everywhere
|
|
|
150
150
|
entry_path = File.join(framework.root, entry)
|
|
151
151
|
|
|
152
152
|
unless File.exist?(entry_path)
|
|
153
|
-
UI.die!("#{entry} not found in #{framework.root}
|
|
153
|
+
UI.die!("#{entry} not found in #{framework.root}: the app needs a packaged-boot entry point")
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
UI.step("#{UI.bold(framework.name)} app at #{framework.root}")
|
|
@@ -198,10 +198,10 @@ module Everywhere
|
|
|
198
198
|
# `--format aab` on an iOS build is a mistake we name rather than drop.
|
|
199
199
|
def mobile_options(target, device:, format:)
|
|
200
200
|
if Everywhere::Config.os_of(target) == "android"
|
|
201
|
-
UI.die!("--device is an iOS flag
|
|
201
|
+
UI.die!("--device is an iOS flag; Android always builds an installable artifact") if device
|
|
202
202
|
{ format: android_format(format) }
|
|
203
203
|
else
|
|
204
|
-
UI.die!("--format is an Android flag (#{ANDROID_FORMATS.join("/")})
|
|
204
|
+
UI.die!("--format is an Android flag (#{ANDROID_FORMATS.join("/")}); iOS builds a .app or .ipa") if format
|
|
205
205
|
{ device: device }
|
|
206
206
|
end
|
|
207
207
|
end
|
|
@@ -222,7 +222,7 @@ module Everywhere
|
|
|
222
222
|
def resolve_target(target, ios:, android:)
|
|
223
223
|
asked = { ios: ios, android: android }.select { |_flag, given| given }.keys
|
|
224
224
|
if asked.length > 1
|
|
225
|
-
UI.die!("#{asked.map { |f| "--#{f}" }.join(" and ")} are separate builds
|
|
225
|
+
UI.die!("#{asked.map { |f| "--#{f}" }.join(" and ")} are separate builds; run one at a time")
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
flag = asked.first or return target || DEFAULT_TARGET
|
|
@@ -230,7 +230,7 @@ module Everywhere
|
|
|
230
230
|
return shorthand if target.nil?
|
|
231
231
|
|
|
232
232
|
unless Everywhere::Config.os_of(target) == flag.to_s
|
|
233
|
-
UI.die!("--#{flag} and --target #{target} disagree
|
|
233
|
+
UI.die!("--#{flag} and --target #{target} disagree; drop one " \
|
|
234
234
|
"(--#{flag} means --target #{shorthand})")
|
|
235
235
|
end
|
|
236
236
|
target
|
|
@@ -243,7 +243,7 @@ module Everywhere
|
|
|
243
243
|
def ensure_gems!(framework)
|
|
244
244
|
return if Shellout.run?(bundle_env, "bundle", "check", chdir: framework.root, quiet: true)
|
|
245
245
|
|
|
246
|
-
UI.step("installing gems #{UI.dim("(bundle install
|
|
246
|
+
UI.step("installing gems #{UI.dim("(bundle install, first build on this machine)")}")
|
|
247
247
|
Shellout.run!(bundle_env, "bundle", "install", chdir: framework.root)
|
|
248
248
|
end
|
|
249
249
|
|
|
@@ -272,7 +272,7 @@ module Everywhere
|
|
|
272
272
|
FileUtils.mkdir_p(out_dir)
|
|
273
273
|
log = "#{output_path}.build.log"
|
|
274
274
|
|
|
275
|
-
UI.step("rbe-tebako press #{UI.dim("(Ruby #{ruby}
|
|
275
|
+
UI.step("rbe-tebako press #{UI.dim("(Ruby #{ruby}, grab a coffee on first run)")}")
|
|
276
276
|
UI.detail("full build log: #{UI.short_path(log)}")
|
|
277
277
|
# Press into a staging dir OUTSIDE the app root for the same reason
|
|
278
278
|
# (tebako prints a big warning about exactly this). Block form: the
|
|
@@ -305,7 +305,7 @@ module Everywhere
|
|
|
305
305
|
# Rails binary (as "app-server") + Info.plist + icon. An .app bundle is
|
|
306
306
|
# just a directory layout, so we build it by hand — no tauri-cli needed.
|
|
307
307
|
def bundle_app(server_binary, app_name, shell_dir, config, target: DEFAULT_TARGET, dist_dir: nil)
|
|
308
|
-
Shellout.ensure_tool!("cargo", "the desktop shell is built with Rust
|
|
308
|
+
Shellout.ensure_tool!("cargo", "the desktop shell is built with Rust; install it from https://rustup.rs")
|
|
309
309
|
|
|
310
310
|
# cargo always runs in a STAGED copy of the shell under
|
|
311
311
|
# ~/.rubyeverywhere, never in the gem (or in whatever --shell-dir
|
|
@@ -320,7 +320,7 @@ module Everywhere
|
|
|
320
320
|
|
|
321
321
|
icon_source = config.icon
|
|
322
322
|
if icon_source && !File.exist?(icon_source)
|
|
323
|
-
UI.warn "app icon #{icon_source} not found
|
|
323
|
+
UI.warn "app icon #{icon_source} not found, using placeholder"
|
|
324
324
|
icon_source = nil
|
|
325
325
|
end
|
|
326
326
|
UI.step("app icon: #{icon_source ? icon_source.sub("#{config.root}/", "") : "placeholder"}")
|
|
@@ -337,7 +337,7 @@ module Everywhere
|
|
|
337
337
|
if Everywhere::Icon.macos_master(icon_source, shaped)
|
|
338
338
|
macos_icon = shaped
|
|
339
339
|
else
|
|
340
|
-
UI.warn "couldn't read #{File.basename(icon_source)} (unusual PNG flavor)
|
|
340
|
+
UI.warn "couldn't read #{File.basename(icon_source)} (unusual PNG flavor), using it unshaped"
|
|
341
341
|
end
|
|
342
342
|
end
|
|
343
343
|
|
|
@@ -359,7 +359,7 @@ module Everywhere
|
|
|
359
359
|
true
|
|
360
360
|
end
|
|
361
361
|
unless dock_ok
|
|
362
|
-
UI.warn "couldn't convert #{File.basename(icon_source)} to RGBA (unusual PNG flavor)
|
|
362
|
+
UI.warn "couldn't convert #{File.basename(icon_source)} to RGBA (unusual PNG flavor); the Dock icon will use the placeholder after launch"
|
|
363
363
|
end
|
|
364
364
|
FileUtils.touch(File.join(shell_dir, "build.rs"))
|
|
365
365
|
end
|
|
@@ -389,7 +389,7 @@ module Everywhere
|
|
|
389
389
|
FileUtils.cp(splash, File.join(resources, "splash.html"))
|
|
390
390
|
UI.step("custom splash: #{splash.sub("#{config.root}/", "")}")
|
|
391
391
|
else
|
|
392
|
-
UI.warn "splash #{splash} not found
|
|
392
|
+
UI.warn "splash #{splash} not found, using the default splash"
|
|
393
393
|
end
|
|
394
394
|
end
|
|
395
395
|
icns = make_icns(macos_icon || shell_icon, resources)
|
|
@@ -433,7 +433,7 @@ module Everywhere
|
|
|
433
433
|
out = File.join(resources_dir, "AppIcon.icns")
|
|
434
434
|
Everywhere::Icon.write_icns(png, out) ? "AppIcon" : nil
|
|
435
435
|
rescue StandardError => e
|
|
436
|
-
UI.warn "icon generation failed (#{e.message})
|
|
436
|
+
UI.warn "icon generation failed (#{e.message}), bundling without a custom icon"
|
|
437
437
|
nil
|
|
438
438
|
end
|
|
439
439
|
|
|
@@ -480,7 +480,7 @@ module Everywhere
|
|
|
480
480
|
end
|
|
481
481
|
|
|
482
482
|
press_warnings = text.scan(/^.*(?:WARNING|Warning):(?!.*bitcode).*$/).size
|
|
483
|
-
UI.warn "tebako emitted #{press_warnings} warning(s)
|
|
483
|
+
UI.warn "tebako emitted #{press_warnings} warning(s), details in the build log" if press_warnings.positive?
|
|
484
484
|
end
|
|
485
485
|
|
|
486
486
|
def press_env
|
|
@@ -497,7 +497,7 @@ module Everywhere
|
|
|
497
497
|
# a silently dropped PATH entry.
|
|
498
498
|
def bison_prefix!
|
|
499
499
|
@bison_prefix ||= brew_bison_prefix ||
|
|
500
|
-
UI.die!("tebako needs Homebrew bison
|
|
500
|
+
UI.die!("tebako needs Homebrew bison: brew install bison")
|
|
501
501
|
end
|
|
502
502
|
|
|
503
503
|
def brew_bison_prefix
|
|
@@ -36,7 +36,7 @@ module Everywhere
|
|
|
36
36
|
FileUtils.rm_rf(dir)
|
|
37
37
|
UI.ok("removed #{dir} #{UI.dim("(#{human_size(bytes)})")}")
|
|
38
38
|
end
|
|
39
|
-
UI.success("#{human_size(freed)} freed
|
|
39
|
+
UI.success("#{human_size(freed)} freed; the next build recompiles the shells cold")
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
private
|