Sfoglia il codice sorgente

use `dx run` for playwright tests, properly abort if error occurs, don't ddos github releases, split apt-cache by os/target in matrix (#4315)

* properly emit errors

* fix error propagate

* install glib

* fix playwright for dx run ssg

* add execute install scripts (for arm linux)

* use verbose playwright

* wip: unwrap playwright download error

* use alternative wasm-opt install

* log wasm-opt download

* use hardcoded endpoint for wasm-opt download

* disambiguate cache

* clean up impl
Jonathan Kelley 3 giorni fa
parent
commit
fd1b04eba1

+ 7 - 7
.github/workflows/main.yml

@@ -73,7 +73,7 @@ jobs:
         uses: ./.github/actions/free-disk-space
       - uses: awalsh128/cache-apt-pkgs-action@latest
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
           version: 1.0
       - uses: dtolnay/rust-toolchain@1.86.0
         with:
@@ -94,7 +94,7 @@ jobs:
         uses: ./.github/actions/free-disk-space
       - uses: awalsh128/cache-apt-pkgs-action@latest
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
           version: 1.0
       - uses: dtolnay/rust-toolchain@1.86.0
         with:
@@ -127,7 +127,7 @@ jobs:
       - uses: actions/checkout@v4
       - uses: awalsh128/cache-apt-pkgs-action@latest
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
           version: 1.0
       - name: Install Rust ${{ env.rust_nightly }}
         uses: dtolnay/rust-toolchain@nightly
@@ -150,7 +150,7 @@ jobs:
       - uses: actions/checkout@v4
       - uses: awalsh128/cache-apt-pkgs-action@latest
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
           version: 1.0
       - uses: dtolnay/rust-toolchain@1.86.0
       - uses: Swatinem/rust-cache@v2
@@ -166,7 +166,7 @@ jobs:
       - uses: actions/checkout@v4
       - uses: awalsh128/cache-apt-pkgs-action@latest
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
           version: 1.0
       - uses: dtolnay/rust-toolchain@1.86.0
         with:
@@ -309,8 +309,8 @@ jobs:
       - uses: awalsh128/cache-apt-pkgs-action@latest
         if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
-          version: 1.0
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
+          version: ${{ matrix.platform.target }}-${{ matrix.platform.os }} # disambiguate since we're in a matrix and this caching action doesn't factor in these variables
 
       - name: Install cross
         if: ${{ matrix.platform.cross == true }}

+ 1 - 1
.github/workflows/merge.yml

@@ -40,7 +40,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - run: sudo apt-get update
-      - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+      - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
       - uses: dtolnay/rust-toolchain@nightly
         with:
           toolchain: nightly-2024-02-01

+ 2 - 2
.github/workflows/publish.yml

@@ -80,7 +80,7 @@ jobs:
       - uses: awalsh128/cache-apt-pkgs-action@latest
         if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
         with:
-          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
           version: 1.0
 
       - name: Install stable
@@ -127,7 +127,7 @@ jobs:
   #     - uses: actions/checkout@v4
   #       ref: ${{ github.event.inputs.channel }}
   #     - run: sudo apt-get update
-  #     - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
+  #     - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
   #     - uses: dtolnay/rust-toolchain@nightly
   #       with:
   #         toolchain: nightly-2024-02-01

+ 44 - 10
packages/cli/src/cli/run.rs

@@ -1,13 +1,16 @@
 use super::*;
 use crate::{
     serve::{AppServer, ServeUpdate, WebServer},
-    BuilderUpdate, Platform, Result,
+    BuilderUpdate, Error, Platform, Result,
 };
 use dioxus_dx_wire_format::BuildStage;
 
 /// Run the project with the given arguments
 ///
 /// This is a shorthand for `dx serve` with interactive mode and hot-reload disabled.
+///
+/// Unlike `dx serve`, errors during build and run will cascade out as an error, rather than being
+/// handled by the TUI, making it more suitable for scripting, automation, or CI/CD pipelines.
 #[derive(Clone, Debug, Parser)]
 pub(crate) struct RunArgs {
     /// Information about the target to build
@@ -36,15 +39,15 @@ impl RunArgs {
             };
 
             match msg {
-                // Wait for logs from the build engine
-                // These will cause us to update the screen
-                // We also can check the status of the builds here in case we have multiple ongoing builds
                 ServeUpdate::BuilderUpdate { id, update } => {
                     let platform = builder.get_build(id).unwrap().build.platform;
 
                     // And then update the websocketed clients with the new build status in case they want it
                     devserver.new_build_update(&update).await;
 
+                    // Finally, we also want to update the builder with the new update
+                    builder.new_build_update(&update, &devserver).await;
+
                     // And then open the app if it's ready
                     match update {
                         BuilderUpdate::BuildReady { bundle } => {
@@ -72,7 +75,9 @@ impl RunArgs {
                                 total,
                                 krate,
                             } => {
-                                tracing::info!("[{platform}] Compiling {krate} ({current}/{total})",)
+                                tracing::debug!(
+                                    "[{platform}] ({current}/{total}) Compiling {krate} ",
+                                )
                             }
                             BuildStage::RunningBindgen => {
                                 tracing::info!("[{platform}] Running WASM bindgen")
@@ -82,7 +87,7 @@ impl RunArgs {
                                 tracing::info!("[{platform}] Optimizing WASM with `wasm-opt`")
                             }
                             BuildStage::Linking => tracing::info!("Linking app"),
-                            BuildStage::Hotpatching => todo!(),
+                            BuildStage::Hotpatching => {}
                             BuildStage::CopyingAssets {
                                 current,
                                 total,
@@ -96,10 +101,24 @@ impl RunArgs {
                                 tracing::info!("[{platform}] Running Gradle")
                             }
                             BuildStage::Success => {}
-                            BuildStage::Failed => {}
-                            BuildStage::Aborted => {}
                             BuildStage::Restarting => {}
                             BuildStage::CompressingAssets => {}
+                            BuildStage::ExtractingAssets => {}
+                            BuildStage::Prerendering => {
+                                tracing::info!("[{platform}] Prerendering app")
+                            }
+                            BuildStage::Failed => {
+                                tracing::error!("[{platform}] Build failed");
+                                return Err(Error::Cargo(format!(
+                                    "Build failed for platform: {platform}"
+                                )));
+                            }
+                            BuildStage::Aborted => {
+                                tracing::error!("[{platform}] Build aborted");
+                                return Err(Error::Cargo(format!(
+                                    "Build aborted for platform: {platform}"
+                                )));
+                            }
                             _ => {}
                         },
                         BuilderUpdate::CompilerMessage { message } => {
@@ -107,6 +126,7 @@ impl RunArgs {
                         }
                         BuilderUpdate::BuildFailed { err } => {
                             tracing::error!("Build failed: {}", err);
+                            return Err(err);
                         }
                         BuilderUpdate::StdoutReceived { msg } => {
                             tracing::info!("[{platform}] {msg}");
@@ -119,14 +139,28 @@ impl RunArgs {
                                 tracing::error!(
                                     "Application [{platform}] exited with error: {status}"
                                 );
+                                return Err(Error::Runtime(format!(
+                                    "Application [{platform}] exited with error: {status}"
+                                )));
                             }
 
                             break;
                         }
-                        BuilderUpdate::ProcessWaitFailed { .. } => {}
+                        BuilderUpdate::ProcessWaitFailed { err } => {
+                            return Err(err.into());
+                        }
                     }
                 }
-                _ => {}
+                ServeUpdate::Exit { .. } => break,
+                ServeUpdate::NewConnection { .. } => {}
+                ServeUpdate::WsMessage { .. } => {}
+                ServeUpdate::FilesChanged { .. } => {}
+                ServeUpdate::OpenApp => {}
+                ServeUpdate::RequestRebuild => {}
+                ServeUpdate::ToggleShouldRebuild => {}
+                ServeUpdate::OpenDebugger { .. } => {}
+                ServeUpdate::Redraw => {}
+                ServeUpdate::TracingLog { .. } => {}
             }
         }
 

+ 47 - 30
packages/cli/src/wasm_opt.rs

@@ -1,12 +1,10 @@
+use crate::config::WasmOptLevel;
+use crate::{CliSettings, Result, WasmOptConfig, Workspace};
 use anyhow::{anyhow, Context};
 use flate2::read::GzDecoder;
+use std::path::{Path, PathBuf};
 use tar::Archive;
 use tempfile::NamedTempFile;
-use tokio::fs;
-
-use crate::config::WasmOptLevel;
-use crate::{CliSettings, Result, WasmOptConfig, Workspace};
-use std::path::{Path, PathBuf};
 
 /// Write these wasm bytes with a particular set of optimizations
 pub async fn write_wasm(bytes: &[u8], output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
@@ -16,7 +14,9 @@ pub async fn write_wasm(bytes: &[u8], output_path: &Path, cfg: &WasmOptConfig) -
 }
 
 pub async fn optimize(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
-    let wasm_opt = WasmOpt::new(input_path, output_path, cfg).await?;
+    let wasm_opt = WasmOpt::new(input_path, output_path, cfg)
+        .await
+        .inspect_err(|err| tracing::error!("Failed to create wasm-opt instance: {}", err))?;
     wasm_opt.optimize().await?;
 
     Ok(())
@@ -127,6 +127,20 @@ impl WasmOpt {
 
 // Find the URL for the latest binaryen release that contains wasm-opt
 async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
+    // Find the platform identifier based on the current OS and architecture
+    // hardcoded for now to get around github api rate limits
+    if cfg!(all(target_os = "windows", target_arch = "x86_64")) {
+        return Ok("https://github.com/WebAssembly/binaryen/releases/download/version_123/binaryen-version_123-x86_64-windows.tar.gz".to_string());
+    } else if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
+        return Ok("https://github.com/WebAssembly/binaryen/releases/download/version_123/binaryen-version_123-x86_64-linux.tar.gz".to_string());
+    } else if cfg!(all(target_os = "linux", target_arch = "aarch64")) {
+        return Ok("https://github.com/WebAssembly/binaryen/releases/download/version_123/binaryen-version_123-aarch64-linux.tar.gz".to_string());
+    } else if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
+        return Ok("https://github.com/WebAssembly/binaryen/releases/download/version_123/binaryen-version_123-x86_64-macos.tar.gz".to_string());
+    } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
+        return Ok("https://github.com/WebAssembly/binaryen/releases/download/version_123/binaryen-version_123-arm64-macos.tar.gz".to_string());
+    };
+
     let url = "https://api.github.com/repos/WebAssembly/binaryen/releases/latest";
     let client = reqwest::Client::new();
     let response = client
@@ -136,6 +150,9 @@ async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
         .await?
         .json::<serde_json::Value>()
         .await?;
+
+    tracing::trace!("Response from GitHub: {:#?}", response);
+
     let assets = response
         .get("assets")
         .and_then(|assets| assets.as_array())
@@ -165,7 +182,7 @@ async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
             asset
                 .get("name")
                 .and_then(|name| name.as_str())
-                .is_some_and(|name| name.contains(platform))
+                .is_some_and(|name| name.contains(platform) && !name.ends_with("sha256"))
         })
         .ok_or_else(|| {
             anyhow::anyhow!(
@@ -185,31 +202,30 @@ async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
 
 /// Get the path to the wasm-opt binary, downloading it if necessary
 async fn get_binary_path() -> anyhow::Result<PathBuf> {
-    let existing_path = which::which("wasm-opt");
-
-    match existing_path {
-        // If wasm-opt is already in the PATH, return its path
-        Ok(path) => Ok(path),
-        // If wasm-opt is not found in the path and we prefer no downloads, return an error
-        Err(_) if CliSettings::prefer_no_downloads() => Err(anyhow!("Missing wasm-opt")),
-        // Otherwise, try to install it
-        Err(_) => {
-            let install_dir = install_dir().await?;
-            let install_path = installed_bin_path(&install_dir);
-            if !install_path.exists() {
-                tracing::info!("Installing wasm-opt");
-                install_github(&install_dir).await?;
-                tracing::info!("wasm-opt installed from Github");
-            }
-            Ok(install_path)
+    let install_dir = install_dir();
+    let install_path = installed_bin_path(&install_dir);
+
+    if install_path.exists() {
+        return Ok(install_path);
+    }
+
+    if CliSettings::prefer_no_downloads() {
+        if let Ok(existing) = which::which("wasm-opt") {
+            return Ok(existing);
+        } else {
+            return Err(anyhow!("Missing wasm-opt"));
         }
     }
+
+    tracing::info!("Installing wasm-opt");
+    install_github(&install_dir).await?;
+    tracing::info!("wasm-opt installed from Github");
+
+    Ok(install_path)
 }
 
-async fn install_dir() -> anyhow::Result<PathBuf> {
-    let bindgen_dir = Workspace::dioxus_home_dir().join("binaryen");
-    fs::create_dir_all(&bindgen_dir).await?;
-    Ok(bindgen_dir)
+fn install_dir() -> PathBuf {
+    Workspace::dioxus_home_dir().join("binaryen")
 }
 
 fn installed_bin_name() -> &'static str {
@@ -221,14 +237,15 @@ fn installed_bin_name() -> &'static str {
 }
 
 fn installed_bin_path(install_dir: &Path) -> PathBuf {
-    let bin_name = installed_bin_name();
-    install_dir.join("bin").join(bin_name)
+    install_dir.join("bin").join(installed_bin_name())
 }
 
 /// Install wasm-opt from GitHub releases into the specified directory
 async fn install_github(install_dir: &Path) -> anyhow::Result<()> {
     tracing::trace!("Attempting to install wasm-opt from GitHub");
 
+    std::fs::create_dir_all(install_dir)?;
+
     let url = find_latest_wasm_opt_download_url()
         .await
         .context("Failed to find latest wasm-opt download URL")?;

+ 12 - 12
packages/playwright-tests/playwright.config.js

@@ -87,7 +87,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "web"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 9990',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 9990',
       port: 9990,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -96,7 +96,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "web-routing"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 2020',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 2020',
       port: 2020,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -105,7 +105,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "fullstack"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 3333',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 3333',
       port: 3333,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -114,7 +114,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "fullstack-mounted"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 7777',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 7777',
       port: 7777,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -123,7 +123,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "fullstack-routing"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 8888',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 8888',
       port: 8888,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -132,7 +132,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "suspense-carousel"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 4040',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 4040',
       port: 4040,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -141,7 +141,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "nested-suspense"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 5050',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --platform web --addr "127.0.0.1" --port 5050',
       port: 5050,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -150,7 +150,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "nested-suspense"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --bin nested-suspense-ssg --force-sequential --platform web --ssg --addr "127.0.0.1" --port 6060',
+        'cargo run --package dioxus-cli --release -- run --verbose --bin nested-suspense-ssg --force-sequential --platform web --ssg --addr "127.0.0.1" --port 6060',
       port: 6060,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -160,7 +160,7 @@ module.exports = defineConfig({
       cwd: path.join(process.cwd(), "cli-optimization"),
       // Remove the cache folder for the cli-optimization build to force a full cache reset
       command:
-        'cargo run --package dioxus-cli --release -- serve --addr "127.0.0.1" --port 8989',
+        'cargo run --package dioxus-cli --release -- run --verbose --addr "127.0.0.1" --port 8989',
       port: 8989,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -169,7 +169,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "wasm-split-harness"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --bin wasm-split-harness --platform web --addr "127.0.0.1" --port 8001 --wasm-split --profile wasm-split-release',
+        'cargo run --package dioxus-cli --release -- run --verbose --bin wasm-split-harness --platform web --addr "127.0.0.1" --port 8001 --wasm-split --profile wasm-split-release',
       port: 8001,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -178,7 +178,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "default-features-disabled"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --addr "127.0.0.1" --port 8002',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --addr "127.0.0.1" --port 8002',
       port: 8002,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,
@@ -187,7 +187,7 @@ module.exports = defineConfig({
     {
       cwd: path.join(process.cwd(), "barebones-template"),
       command:
-        'cargo run --package dioxus-cli --release -- serve --force-sequential --addr "127.0.0.1" --port 8123',
+        'cargo run --package dioxus-cli --release -- run --verbose --force-sequential --addr "127.0.0.1" --port 8123',
       port: 8123,
       timeout: 50 * 60 * 1000,
       reuseExistingServer: !process.env.CI,