mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
build examples script in rust with wasm-opt checks
This commit is contained in:
parent
72b4c0ed9d
commit
a0d4ab0fc8
3
.github/workflows/publish-examples.yml
vendored
3
.github/workflows/publish-examples.yml
vendored
@ -4,6 +4,7 @@ on:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'ci/**'
|
||||
- 'tools/build-examples/**'
|
||||
- 'examples/**'
|
||||
|
||||
jobs:
|
||||
@ -33,7 +34,7 @@ jobs:
|
||||
version: 'latest'
|
||||
|
||||
- name: Build examples
|
||||
run: ./ci/build-examples.sh
|
||||
run: cargo run -p build-examples -b build-examples
|
||||
|
||||
- name: Deploy to Firebase
|
||||
uses: siku2/action-hosting-deploy@v1
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -302,6 +302,14 @@ dependencies = [
|
||||
"yew",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build-examples"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"reqwest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Must be run from root of the repo:
|
||||
# yew $ ./ci/build-examples.sh
|
||||
|
||||
output="$(pwd)/dist"
|
||||
mkdir -p "$output"
|
||||
|
||||
failure=false
|
||||
for path in examples/*; do
|
||||
if [[ ! -d $path ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
example=$(basename "$path")
|
||||
|
||||
# ssr does not need trunk
|
||||
if [[ "$example" == "simple_ssr" || "$example" == "ssr_router" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "::group::Building $example"
|
||||
if ! (
|
||||
set -e
|
||||
# we are sure that $path exists
|
||||
# shellcheck disable=SC2164
|
||||
cd "$path"
|
||||
dist_dir="$output/$example"
|
||||
export RUSTFLAGS="--cfg nightly_yew --cfg getrandom_backend=\"wasm_js\""
|
||||
|
||||
trunk build --release --dist "$dist_dir" --public-url "$PUBLIC_URL_PREFIX/$example" --no-sri
|
||||
|
||||
# check that there are no undefined symbols. Those generate an import .. from 'env',
|
||||
# which isn't available in the browser.
|
||||
{ cat "$dist_dir"/*.js | grep -q -e "from 'env'" ; } && exit 1 || true
|
||||
) ; then
|
||||
echo "::error ::$example failed to build"
|
||||
failure=true
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
done
|
||||
if [ "$failure" = true ] ; then
|
||||
exit 1
|
||||
fi
|
||||
@ -2,7 +2,7 @@
|
||||
#![doc(html_logo_url = "https://yew.rs/img/logo.png")]
|
||||
#![cfg_attr(documenting, feature(doc_cfg))]
|
||||
#![cfg_attr(documenting, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(nightly_yew, feature(fn_traits, async_closure, unboxed_closures))]
|
||||
#![cfg_attr(nightly_yew, feature(fn_traits, unboxed_closures))]
|
||||
|
||||
//! # Yew Framework - API Documentation
|
||||
//!
|
||||
|
||||
10
tools/build-examples/Cargo.toml
Normal file
10
tools/build-examples/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "build-examples"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12.12", features = ["blocking"] }
|
||||
regex = "1.11.1"
|
||||
|
||||
124
tools/build-examples/src/bin/update-wasm-opt.rs
Normal file
124
tools/build-examples/src/bin/update-wasm-opt.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
|
||||
use build_examples::{get_latest_wasm_opt_version, is_wasm_opt_outdated, NO_TRUNK_EXAMPLES};
|
||||
use regex::Regex;
|
||||
|
||||
|
||||
fn main() -> ExitCode {
|
||||
// Must be run from root of the repo
|
||||
let examples_dir = Path::new("examples");
|
||||
if !examples_dir.exists() {
|
||||
eprintln!(
|
||||
"examples directory not found. Make sure you're running from the root of the repo."
|
||||
);
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
|
||||
let latest_wasm_opt = get_latest_wasm_opt_version();
|
||||
let mut outdated_example_paths = Vec::new();
|
||||
let mut outdated_examples = Vec::new();
|
||||
|
||||
// Get all entries in the examples directory
|
||||
let entries = fs::read_dir(examples_dir).expect("Failed to read examples directory");
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry.expect("Failed to read directory entry");
|
||||
let path = entry.path();
|
||||
|
||||
// Skip if not a directory
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let example = path
|
||||
.file_name()
|
||||
.expect("Failed to get directory name")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
// Skip ssr examples as they don't need trunk
|
||||
if NO_TRUNK_EXAMPLES.contains(&example.as_str()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check Trunk.toml for wasm_opt version and collect outdated examples
|
||||
if is_wasm_opt_outdated(&path, &latest_wasm_opt) {
|
||||
outdated_examples.push(example);
|
||||
outdated_example_paths.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
if outdated_examples.is_empty() {
|
||||
println!(
|
||||
"All examples are up-to-date with the latest wasm_opt version: {}",
|
||||
latest_wasm_opt
|
||||
);
|
||||
return ExitCode::from(0);
|
||||
}
|
||||
|
||||
println!(
|
||||
"Found {} examples with outdated or missing wasm_opt configuration:",
|
||||
outdated_examples.len()
|
||||
);
|
||||
for example in &outdated_examples {
|
||||
println!(" - {}", example);
|
||||
}
|
||||
println!("Latest wasm_opt version is: {}", latest_wasm_opt);
|
||||
println!("Updating all examples...");
|
||||
|
||||
let updated_count = update_all_examples(&outdated_example_paths, &latest_wasm_opt);
|
||||
println!(
|
||||
"Updated {} example configurations to use {}",
|
||||
updated_count, latest_wasm_opt
|
||||
);
|
||||
|
||||
ExitCode::from(0)
|
||||
}
|
||||
|
||||
pub fn update_all_examples(outdated_paths: &[PathBuf], latest_version: &str) -> usize {
|
||||
let mut updated_count = 0;
|
||||
|
||||
let re = Regex::new(r#"(?m)^\[tools\]\s*\nwasm_opt\s*=\s*"(version_\d+)""#).unwrap();
|
||||
for path in outdated_paths {
|
||||
let trunk_toml_path = path.join("Trunk.toml");
|
||||
|
||||
let content = fs::read_to_string(&trunk_toml_path).unwrap_or_default();
|
||||
|
||||
let updated_content = if re.is_match(&content) {
|
||||
// Replace existing wasm_opt version
|
||||
re.replace(&content, |_: ®ex::Captures| {
|
||||
format!(
|
||||
r#"[tools]
|
||||
wasm_opt = "{}""#,
|
||||
latest_version
|
||||
)
|
||||
})
|
||||
.to_string()
|
||||
} else {
|
||||
// Add wasm_opt configuration
|
||||
if content.is_empty() {
|
||||
format!(
|
||||
r#"[tools]
|
||||
wasm_opt = "{}""#,
|
||||
latest_version
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}\n\n[tools]\nwasm_opt = \"{}\"",
|
||||
content.trim(),
|
||||
latest_version
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = fs::write(&trunk_toml_path, updated_content) {
|
||||
println!("Failed to update {}: {}", trunk_toml_path.display(), e);
|
||||
} else {
|
||||
updated_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
updated_count
|
||||
}
|
||||
44
tools/build-examples/src/lib.rs
Normal file
44
tools/build-examples/src/lib.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
/// Examples that don't use Trunk for building
|
||||
pub const NO_TRUNK_EXAMPLES: [&str; 3] = ["simple_ssr", "ssr_router", "wasi_ssr_module"];
|
||||
|
||||
pub fn get_latest_wasm_opt_version() -> String {
|
||||
let url = "https://github.com/WebAssembly/binaryen/releases";
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let res = client.get(url).send().unwrap();
|
||||
let body = res.text().unwrap();
|
||||
let re = Regex::new(r#"version_(\d+)"#).unwrap();
|
||||
let captures = re.captures_iter(&body);
|
||||
let mut versions: Vec<u32> = captures
|
||||
.map(|c| c.get(1).unwrap().as_str().parse().unwrap())
|
||||
.collect();
|
||||
versions.sort();
|
||||
format!("version_{}", versions.last().unwrap())
|
||||
}
|
||||
|
||||
pub fn is_wasm_opt_outdated(path: &Path, latest_version: &str) -> bool {
|
||||
let trunk_toml_path = path.join("Trunk.toml");
|
||||
|
||||
if !trunk_toml_path.exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let content = match fs::read_to_string(&trunk_toml_path) {
|
||||
Ok(content) => content,
|
||||
Err(_) => return true,
|
||||
};
|
||||
|
||||
// Check if wasm_opt is configured and up-to-date
|
||||
let re = Regex::new(r#"(?m)^\[tools\]\s*\nwasm_opt\s*=\s*"(version_\d+)""#).unwrap();
|
||||
match re.captures(&content) {
|
||||
Some(captures) => {
|
||||
let current_version = captures.get(1).unwrap().as_str();
|
||||
current_version != latest_version
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
132
tools/build-examples/src/main.rs
Normal file
132
tools/build-examples/src/main.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use std::path::Path;
|
||||
use std::process::{Command, ExitCode};
|
||||
use std::{env, fs};
|
||||
|
||||
use build_examples::{get_latest_wasm_opt_version, is_wasm_opt_outdated, NO_TRUNK_EXAMPLES};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
// Must be run from root of the repo:
|
||||
// yew $ cargo r -p build-examples -b build-examples
|
||||
let output_dir = env::current_dir().expect("Failed to get current directory");
|
||||
let output_dir = output_dir.join("dist");
|
||||
|
||||
fs::create_dir_all(&output_dir).expect("Failed to create output directory");
|
||||
|
||||
let examples_dir = Path::new("examples");
|
||||
if !examples_dir.exists() {
|
||||
eprintln!(
|
||||
"examples directory not found. Make sure you're running from the root of the repo."
|
||||
);
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
|
||||
let mut failure = false;
|
||||
let latest_wasm_opt = get_latest_wasm_opt_version();
|
||||
let mut outdated_examples = Vec::new();
|
||||
let mut outdated_example_paths = Vec::new();
|
||||
|
||||
// Get all entries in the examples directory
|
||||
let entries = fs::read_dir(examples_dir).expect("Failed to read examples directory");
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry.expect("Failed to read directory entry");
|
||||
let path = entry.path();
|
||||
|
||||
// Skip if not a directory
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let example = path
|
||||
.file_name()
|
||||
.expect("Failed to get directory name")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
// Skip ssr examples as they don't need trunk
|
||||
if NO_TRUNK_EXAMPLES.contains(&example.as_str()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check Trunk.toml for wasm_opt version and collect outdated examples
|
||||
if is_wasm_opt_outdated(&path, &latest_wasm_opt) {
|
||||
outdated_examples.push(example.clone());
|
||||
outdated_example_paths.push(path.clone());
|
||||
}
|
||||
|
||||
println!("::group::Building {}", example);
|
||||
|
||||
if !build_example(&path, &output_dir, &example) {
|
||||
eprintln!("::error ::{} failed to build", example);
|
||||
failure = true;
|
||||
}
|
||||
|
||||
println!("::endgroup::");
|
||||
}
|
||||
|
||||
// Emit warning if any examples have outdated wasm_opt
|
||||
if !outdated_examples.is_empty() {
|
||||
println!(
|
||||
"::warning ::{} example crates do not have up-to-date wasm_opt: {}",
|
||||
outdated_examples.len(),
|
||||
outdated_examples.join(", ")
|
||||
);
|
||||
}
|
||||
|
||||
if failure {
|
||||
ExitCode::from(1)
|
||||
} else {
|
||||
ExitCode::from(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn build_example(path: &Path, output_dir: &Path, example: &str) -> bool {
|
||||
// Get the public URL prefix from environment or use default
|
||||
let public_url_prefix = env::var("PUBLIC_URL_PREFIX").unwrap_or_default();
|
||||
|
||||
// Set up the dist directory for this example
|
||||
let dist_dir = output_dir.join(example);
|
||||
|
||||
// Run trunk build command
|
||||
let status = Command::new("trunk")
|
||||
.current_dir(path)
|
||||
.arg("build")
|
||||
.env(
|
||||
"RUSTFLAGS",
|
||||
"--cfg nightly_yew --cfg getrandom_backend=\"wasm_js\"",
|
||||
)
|
||||
.arg("--release")
|
||||
.arg("--dist")
|
||||
.arg(&dist_dir)
|
||||
.arg("--public-url")
|
||||
.arg(format!("{}/{}", public_url_prefix, example))
|
||||
.arg("--no-sri")
|
||||
.status();
|
||||
|
||||
match status {
|
||||
Ok(status) if status.success() => {
|
||||
// Check for undefined symbols (imports from 'env')
|
||||
let js_files = match fs::read_dir(&dist_dir) {
|
||||
Ok(entries) => entries
|
||||
.filter_map(Result::ok)
|
||||
.filter(|e| e.path().extension().is_some_and(|ext| ext == "js"))
|
||||
.collect::<Vec<_>>(),
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
for js_file in js_files {
|
||||
let content = match fs::read_to_string(js_file.path()) {
|
||||
Ok(content) => content,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
if content.contains("from 'env'") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user