Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load examples manifest via HTTP #4391

Merged
merged 29 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4c5c3a4
load example manifest via http
jprochazk Nov 29, 2023
afee69a
override `EXAMPLES_MANIFEST_URL` at runtime in CLI
jprochazk Nov 29, 2023
7dfb188
move examples_manifest build script
jprochazk Nov 29, 2023
52e89ab
support `manifest_url` query param
jprochazk Nov 29, 2023
2b0ea81
support `--base-url` arg
jprochazk Nov 30, 2023
99b68d2
remove unused deps
jprochazk Nov 30, 2023
beed08f
add `build-examples-manifest` task to pixi
jprochazk Nov 30, 2023
09848dc
set default manifest to real values
jprochazk Nov 30, 2023
22daf12
build manifest on CI
jprochazk Nov 30, 2023
46921d0
fix clippy lints
jprochazk Nov 30, 2023
aee399f
get rid of `UPLOAD_COMMIT_OVERRIDE` dance
jprochazk Nov 30, 2023
1b9f878
fix trailing commas
jprochazk Nov 30, 2023
eb11397
ignore generated js file
jprochazk Nov 30, 2023
fc0807b
remove log about using default manifest
jprochazk Nov 30, 2023
43248c0
Merge branch 'main' into jan/online-manifest
jprochazk Nov 30, 2023
f9b14c6
update pr template with nightly app rerun io
jprochazk Nov 30, 2023
d3375b9
Merge branch 'main' into jan/online-manifest
jprochazk Nov 30, 2023
d8fa1ba
sort
jprochazk Nov 30, 2023
815bd94
move code around for readability
jprochazk Nov 30, 2023
c27c570
remove unnecessary `pub(crate)`
jprochazk Nov 30, 2023
9936fc7
use `ReUi::error_text` instead of plain `label` for error message
jprochazk Nov 30, 2023
7cd767e
remove unnecessary handle.start args
jprochazk Nov 30, 2023
1a39da1
downgrade prettier
jprochazk Nov 30, 2023
1b109a7
uppercase doctype
jprochazk Nov 30, 2023
252b6be
update pr template
jprochazk Nov 30, 2023
811b020
update pr template again
jprochazk Nov 30, 2023
50638c2
fix usage of `error_text`
jprochazk Nov 30, 2023
c6c2960
Merge branch 'main' into jan/online-manifest
jprochazk Nov 30, 2023
cf3b24b
Merge branch 'main' into jan/online-manifest
jprochazk Dec 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move examples_manifest build script
  • Loading branch information
jprochazk committed Nov 29, 2023
commit 7dfb1883d9bd3608542ab7385f744e8895a2e6e2
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ re_analytics = { path = "crates/re_analytics", version = "=0.12.0-alpha.1", defa
re_arrow_store = { path = "crates/re_arrow_store", version = "=0.12.0-alpha.1", default-features = false }
re_build_info = { path = "crates/re_build_info", version = "=0.12.0-alpha.1", default-features = false }
re_build_tools = { path = "crates/re_build_tools", version = "=0.12.0-alpha.1", default-features = false }
re_build_examples_manifest = { path = "crates/re_build_examples_manifest", version = "=0.12.0-alpha.1", default-features = false }
jprochazk marked this conversation as resolved.
Show resolved Hide resolved
re_build_web_viewer = { path = "crates/re_build_web_viewer", version = "=0.12.0-alpha.1", default-features = false }
re_crash_handler = { path = "crates/re_crash_handler", version = "=0.12.0-alpha.1", default-features = false }
re_data_source = { path = "crates/re_data_source", version = "=0.12.0-alpha.1", default-features = false }
Expand Down
26 changes: 26 additions & 0 deletions crates/re_build_examples_manifest/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "re_build_examples_manifest"
authors.workspace = true
description = "Build the rerun web-viewer Wasm from source"
edition.workspace = true
homepage.workspace = true
include.workspace = true
license.workspace = true
publish = true
readme = "README.md"
repository.workspace = true
rust-version.workspace = true
version.workspace = true

[package.metadata.docs.rs]
all-features = true


[dependencies]
re_build_tools.workspace = true

# External
anyhow.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_yaml.workspace = true
259 changes: 259 additions & 0 deletions crates/re_build_examples_manifest/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
//! This build script generates the `examples_manifest.json` file.
//! It looks at all examples in the workspace (under `examples/python`),
//! and only includes those with `demo` set to `true` in their `README.md`
//! frontmatter.
//!
//! The URLs embedded in the `example_manifest.json` file point to a specific version.
//! This version is resolved according to the current environment:
//!
//! If the `CI` env var is set + the branch name is not `main`, then:
//! - On any `release-x.y.z` branch, the version is `version/x.y.z`
//! - On any other branch, the version is `commit/$COMMIT_SHORT_HASH`
//!
//! Otherwise, the version is `version/nightly`. This means local builds,
//! and builds on `main` point to `version/nightly`.

use std::path::Path;
use std::path::PathBuf;

use re_build_tools::Environment;

fn parse_release_version(branch: &str) -> Option<&str> {
// release-\d+.\d+.\d+(-alpha.\d+)?

let version = branch.strip_prefix("release-")?;

let (major, rest) = version.split_once('.')?;
major.parse::<u8>().ok()?;
let (minor, rest) = rest.split_once('.')?;
minor.parse::<u8>().ok()?;
let (patch, meta) = rest
.split_once('-')
.map_or((rest, None), |(p, m)| (p, Some(m)));
patch.parse::<u8>().ok()?;

if let Some(meta) = meta {
let (kind, n) = meta.split_once('.')?;
if kind != "alpha" && kind != "rc" {
return None;
}
n.parse::<u8>().ok()?;
}

Some(version)
}

#[derive(serde::Deserialize)]
struct Frontmatter {
#[serde(default)]
title: String,
#[serde(default)]
tags: Vec<String>,
#[serde(default)]
description: String,
#[serde(default)]
thumbnail: String,
#[serde(default)]
thumbnail_dimensions: [u64; 2],
#[serde(default)]
demo: bool,
}

#[derive(serde::Serialize)]
struct ManifestEntry {
name: String,
title: String,
description: String,
tags: Vec<String>,
demo_url: String,
rrd_url: String,
thumbnail: Thumbnail,
}

impl ManifestEntry {
fn new(example: Example, base_url: &str) -> Self {
let Example { name, readme } = example;
Self {
title: readme.title,
description: readme.description,
tags: readme.tags,
demo_url: format!("{base_url}/examples/{name}/"),
rrd_url: format!("{base_url}/examples/{name}/data.rrd"),
thumbnail: Thumbnail {
url: readme.thumbnail,
width: readme.thumbnail_dimensions[0],
height: readme.thumbnail_dimensions[1],
},
name,
}
}
}

#[derive(serde::Serialize)]
struct Thumbnail {
url: String,
width: u64,
height: u64,
}

struct Example {
name: String,
readme: Frontmatter,
}

fn examples() -> anyhow::Result<Vec<Example>> {
let mut examples = vec![];
let dir = Path::new("examples/python");
if !dir.exists() {
anyhow::bail!("Failed to find {}", dir.display())
}
if !dir.is_dir() {
anyhow::bail!("{} is not a directory", dir.display())
}

for folder in std::fs::read_dir(dir)? {
let folder = folder?;
let metadata = folder.metadata()?;
let name = folder.file_name().to_string_lossy().to_string();
let readme = folder.path().join("README.md");
if metadata.is_dir() && readme.exists() {
let readme = parse_frontmatter(readme)?;
if let Some(readme) = readme {
if readme.demo {
eprintln!("Adding example {name:?}");
examples.push(Example { name, readme });
} else {
eprintln!("Skipping example {name:?} because 'demo' is set to 'false'");
}
} else {
eprintln!("Skipping example {name:?} because it has no frontmatter");
}
}
}

if examples.is_empty() {
anyhow::bail!("No examples found in {}", dir.display())
}

examples.sort_unstable_by(|a, b| a.name.cmp(&b.name));
Ok(examples)
}

fn parse_frontmatter<P: AsRef<Path>>(path: P) -> anyhow::Result<Option<Frontmatter>> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)?;
let content = content.replace('\r', ""); // Windows, god damn you
re_build_tools::rerun_if_changed(path);
let Some(content) = content.strip_prefix("---\n") else {
return Ok(None);
};
let Some(end) = content.find("---") else {
anyhow::bail!("{:?} has invalid frontmatter", path);
};
Ok(Some(serde_yaml::from_str(&content[..end]).map_err(
|e| {
anyhow::anyhow!(
"failed to read {:?}: {e}",
path.parent().unwrap().file_name().unwrap()
)
},
)?))
}

fn get_base_url(build_env: Environment) -> anyhow::Result<String> {
if let Ok(base_url) = re_build_tools::get_and_track_env_var("EXAMPLES_MANIFEST_BASE_URL") {
// override via env var
return Ok(base_url);
}

// In the CondaBuild environment we can't trust the git_branch name -- if it exists
// at all it's going to be the feedstock branch-name, not our Rerun branch. However
// conda should ONLY be building released versions, so we want to version the manifest.
let versioned_manifest = matches!(build_env, Environment::CondaBuild) || {
let branch = re_build_tools::git_branch()?;
if branch == "main" || !re_build_tools::is_on_ci() {
// on `main` and local builds, use `version/nightly`
// this will point to data uploaded by `.github/workflows/reusable_upload_web_demo.yml`
// on every commit to the `main` branch
return Ok("https://demo.rerun.io/version/nightly".into());
}
parse_release_version(&branch).is_some()
};

if versioned_manifest {
let metadata = re_build_tools::cargo_metadata()?;
let workspace_root = metadata
.root_package()
.ok_or_else(|| anyhow::anyhow!("failed to find workspace root"))?;

// on `release-x.y.z` builds, use `version/{crate_version}`
// this will point to data uploaded by `.github/workflows/reusable_build_and_publish_web.yml`
return Ok(format!(
"https://demo.rerun.io/version/{}",
workspace_root.version
));
}

// any other branch that is not `main`, use `commit/{sha}`
// this will point to data uploaded by `.github/workflows/reusable_upload_web_demo.yml`
let sha = re_build_tools::git_commit_short_hash()?;
Ok(format!("https://demo.rerun.io/commit/{sha}"))
}

fn build_examples_manifest(build_env: Environment) -> anyhow::Result<String> {
let base_url = get_base_url(build_env)?;

let mut manifest = vec![];
for example in examples()? {
manifest.push(ManifestEntry::new(example, &base_url));
}

if manifest.is_empty() {
anyhow::bail!("No examples found!");
}

Ok(serde_json::to_string_pretty(&manifest)?)
}

const USAGE: &str = "\
Usage: [options] [output_path]

Options:
-h, --help Print help
";

fn main() {
re_build_tools::set_output_cargo_build_instructions(false);

let mut output_path = None;
for arg in std::env::args().skip(1) {
match arg.as_str() {
"--help" | "-h" => {
println!("{USAGE}");
return;
}
_ if arg.starts_with('-') => {
eprintln!("Unknown argument: {arg:?}");
println!("\n{USAGE}");
return;
}
_ if output_path.is_some() => {
eprintln!("Too many arguments");
println!("\n{USAGE}");
return;
}
_ => output_path = Some(PathBuf::from(arg)),
}
}

let Some(output_path) = output_path else {
eprintln!("Missing argument \"output_path\"");
return;
};

if let Err(err) = build_examples_manifest(Environment::detect())
.and_then(|manifest| std::fs::write(output_path, manifest).map_err(anyhow::Error::from))
{
eprintln!("{err}");
}
}
Loading