[bfops/translate-script]: WIP

This commit is contained in:
Zeke Foppa
2026-05-06 12:09:26 -07:00
parent 4397c9e1a6
commit 2861ba3e0d
5 changed files with 625 additions and 205 deletions
-205
View File
@@ -91,123 +91,6 @@ fn check_global_json_policy() -> Result<()> {
Ok(())
}
fn overlay_unity_meta_skeleton(pkg_id: &str) -> Result<()> {
let skeleton_base = Path::new("sdks/csharp/unity-meta-skeleton~");
let skeleton_root = skeleton_base.join(pkg_id);
if !skeleton_root.exists() {
return Ok(());
}
let pkg_root = Path::new("sdks/csharp/packages").join(pkg_id);
if !pkg_root.exists() {
return Ok(());
}
// Copy spacetimedb.<pkg>.meta
let pkg_root_meta = skeleton_base.join(format!("{pkg_id}.meta"));
if pkg_root_meta.exists()
&& let Some(parent) = pkg_root.parent()
{
let pkg_meta_dst = parent.join(format!("{pkg_id}.meta"));
fs::copy(&pkg_root_meta, &pkg_meta_dst)?;
}
let versioned_dir = match find_only_subdir(&pkg_root) {
Ok(dir) => dir,
Err(err) => {
log::info!("Skipping Unity meta overlay for {pkg_id}: could not locate restored version dir: {err}");
return Ok(());
}
};
// If version.meta exists under the skeleton package, rename it to match the restored version dir.
let version_meta_template = skeleton_root.join("version.meta");
if version_meta_template.exists()
&& let Some(parent) = versioned_dir.parent()
{
let version_name = versioned_dir
.file_name()
.expect("versioned directory should have a file name");
let version_meta_dst = parent.join(format!("{}.meta", version_name.to_string_lossy()));
fs::copy(&version_meta_template, &version_meta_dst)?;
}
copy_overlay_dir(&skeleton_root, &versioned_dir)
}
fn clear_restored_package_dirs(pkg_id: &str) -> Result<()> {
let pkg_root = Path::new("sdks/csharp/packages").join(pkg_id);
if !pkg_root.exists() {
return Ok(());
}
fs::remove_dir_all(&pkg_root)?;
Ok(())
}
fn find_only_subdir(dir: &Path) -> Result<PathBuf> {
let mut subdirs: Vec<PathBuf> = vec![];
for entry in fs::read_dir(dir)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
subdirs.push(entry.path());
}
}
match subdirs.as_slice() {
[] => Err(anyhow::anyhow!(
"Could not find a restored versioned directory under {}",
dir.display()
)),
[only] => Ok(only.clone()),
_ => Err(anyhow::anyhow!(
"Expected exactly one restored versioned directory under {}, found {}",
dir.display(),
subdirs.len()
)),
}
}
fn copy_overlay_dir(src: &Path, dst: &Path) -> Result<()> {
if !src.exists() {
bail!("Skeleton directory does not exist: {}", src.display());
}
if !dst.exists() {
bail!("Destination directory does not exist: {}", dst.display());
}
for entry in fs::read_dir(src)? {
let entry = entry?;
let src_path = entry.path();
let dst_path = dst.join(entry.file_name());
if entry.file_type()?.is_dir() {
if dst_path.exists() {
copy_overlay_dir(&src_path, &dst_path)?;
}
} else {
if src_path.extension() == Some(OsStr::new("meta")) {
let asset_path = dst_path
.parent()
.expect("dst_path should have a parent")
.join(dst_path.file_stem().expect(".meta file should have a file stem"));
if asset_path.exists() {
fs::copy(&src_path, &dst_path)?;
} else if dst_path.exists() {
fs::remove_file(&dst_path)?;
}
continue;
}
fs::copy(&src_path, &dst_path)?;
}
}
Ok(())
}
#[derive(Subcommand)]
enum CiCmd {
/// Runs tests
@@ -225,12 +108,6 @@ enum CiCmd {
///
/// Runs tests for the codegen crate and builds a test module with the wasm bindings.
WasmBindings,
/// Builds and packs C# DLLs and NuGet packages for local Unity workflows
///
/// Packs the in-repo C# NuGet packages and restores the C# SDK to populate `sdks/csharp/packages/**`.
/// Then overlays Unity `.meta` skeleton files from `sdks/csharp/unity-meta-skeleton~/**` onto the restored
/// versioned package directory, so Unity can associate stable meta files with the most recently built package.
Dlls,
/// Runs smoketests
///
/// Executes the smoketests suite with some default exclusions.
@@ -308,84 +185,6 @@ fn tracked_rs_files_under(path: &str) -> Result<Vec<PathBuf>> {
.collect())
}
fn run_dlls() -> Result<()> {
ensure_repo_root()?;
cmd!(
"dotnet",
"pack",
"crates/bindings-csharp/BSATN.Runtime",
"-c",
"Release"
)
.run()?;
cmd!("dotnet", "pack", "crates/bindings-csharp/Runtime", "-c", "Release").run()?;
let repo_root = env::current_dir()?;
let bsatn_source = repo_root.join("crates/bindings-csharp/BSATN.Runtime/bin/Release");
let runtime_source = repo_root.join("crates/bindings-csharp/Runtime/bin/Release");
let nuget_config_dir = tempfile::tempdir()?;
let nuget_config_path = nuget_config_dir.path().join("nuget.config");
let nuget_config_contents = format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="Local SpacetimeDB.BSATN.Runtime" value="{}" />
<add key="Local SpacetimeDB.Runtime" value="{}" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="Local SpacetimeDB.BSATN.Runtime">
<package pattern="SpacetimeDB.BSATN.Runtime" />
</packageSource>
<packageSource key="Local SpacetimeDB.Runtime">
<package pattern="SpacetimeDB.Runtime" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
"#,
bsatn_source.display(),
runtime_source.display(),
);
fs::write(&nuget_config_path, nuget_config_contents)?;
let nuget_config_path_str = nuget_config_path.to_string_lossy().to_string();
clear_restored_package_dirs("spacetimedb.bsatn.runtime")?;
clear_restored_package_dirs("spacetimedb.runtime")?;
cmd!(
"dotnet",
"restore",
"SpacetimeDB.ClientSDK.csproj",
"--configfile",
&nuget_config_path_str,
)
.dir("sdks/csharp")
.run()?;
overlay_unity_meta_skeleton("spacetimedb.bsatn.runtime")?;
overlay_unity_meta_skeleton("spacetimedb.runtime")?;
cmd!(
"dotnet",
"pack",
"SpacetimeDB.ClientSDK.csproj",
"-c",
"Release",
"--no-restore"
)
.dir("sdks/csharp")
.run()?;
Ok(())
}
fn run_publish_checks() -> Result<()> {
cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?;
cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?;
@@ -622,10 +421,6 @@ fn main() -> Result<()> {
cmd!(cli_path, "build", "--module-path", "modules/module-test",).run()?;
}
Some(CiCmd::Dlls) => {
run_dlls()?;
}
Some(CiCmd::Smoketests(args)) => {
ensure_repo_root()?;
smoketest::run(args)?;
+245
View File
@@ -0,0 +1,245 @@
#![allow(clippy::disallowed_macros)]
use anyhow::{Context, Result};
use duct::cmd;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
pub fn workspace_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.expect("tools/csharp-tools should be two levels below the workspace root")
.to_path_buf()
}
pub fn sdk_dir() -> PathBuf {
workspace_dir().join("sdks/csharp")
}
fn cli_manifest() -> PathBuf {
workspace_dir().join("crates/cli/Cargo.toml")
}
fn standalone_manifest() -> PathBuf {
workspace_dir().join("crates/standalone/Cargo.toml")
}
fn path_arg(path: &Path) -> String {
path.to_string_lossy().into_owned()
}
fn canonicalize_existing(path: &Path) -> Result<PathBuf> {
path.canonicalize()
.with_context(|| format!("failed to canonicalize {}", path.display()))
}
fn render_nuget_config(bsatn_source: &Path, runtime_source: &Path) -> String {
format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<!-- Experimental NuGet feed for Microsoft.DotNet.ILCompiler.LLVM packages -->
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<!-- Local NuGet repositories -->
<add key="Local SpacetimeDB.BSATN.Runtime" value="{}" />
<!-- We need to override the module runtime as well because the examples use it -->
<add key="Local SpacetimeDB.Runtime" value="{}" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<!-- Ensure that SpacetimeDB.BSATN.Runtime is used from the local folder. -->
<!-- Otherwise we risk an outdated version being quietly pulled from NuGet for testing. -->
<packageSource key="Local SpacetimeDB.BSATN.Runtime">
<package pattern="SpacetimeDB.BSATN.Runtime" />
</packageSource>
<packageSource key="Local SpacetimeDB.Runtime">
<package pattern="SpacetimeDB.Runtime" />
</packageSource>
<!-- Experimental packages for NativeAOT-LLVM compilation -->
<packageSource key="dotnet-experimental">
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
<package pattern="runtime.*" />
</packageSource>
<!-- Fallback for other packages (e.g. test deps). -->
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
"#,
bsatn_source.display(),
runtime_source.display(),
)
}
pub fn write_persistent_nuget_configs(spacetimedb_repo_path: Option<&Path>) -> Result<()> {
let spacetimedb_repo_path = match spacetimedb_repo_path {
Some(path) => canonicalize_existing(path)?,
None => workspace_dir(),
};
let sdk_config = sdk_dir().join("NuGet.Config");
let sdk_config_contents = render_nuget_config(
&spacetimedb_repo_path.join("crates/bindings-csharp/BSATN.Runtime/bin/Release"),
&spacetimedb_repo_path.join("crates/bindings-csharp/Runtime/bin/Release"),
);
fs::write(&sdk_config, sdk_config_contents).with_context(|| format!("failed to write {}", sdk_config.display()))?;
let repo_config = spacetimedb_repo_path.join("NuGet.Config");
let repo_config_contents = render_nuget_config(
Path::new("crates/bindings-csharp/BSATN.Runtime/bin/Release"),
Path::new("crates/bindings-csharp/Runtime/bin/Release"),
);
fs::write(&repo_config, repo_config_contents)
.with_context(|| format!("failed to write {}", repo_config.display()))?;
println!("Wrote {} contents:", sdk_config.display());
print!("{}", fs::read_to_string(&sdk_config)?);
Ok(())
}
fn remove_obj_tilde_children(parent: &Path) -> Result<()> {
if !parent.exists() {
return Ok(());
}
for entry in fs::read_dir(parent)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
let obj_tilde = entry.path().join("obj~");
if obj_tilde.exists() {
fs::remove_dir_all(&obj_tilde).with_context(|| format!("failed to remove {}", obj_tilde.display()))?;
}
}
}
Ok(())
}
fn clean_procedure_obj_tilde_dirs() -> Result<()> {
let procedure_client = sdk_dir().join("examples~/regression-tests/procedure-client");
println!("Cleanup obj~ folders generated in {}", procedure_client.display());
remove_obj_tilde_children(&procedure_client)?;
remove_obj_tilde_children(&procedure_client.join("module_bindings"))?;
Ok(())
}
pub fn run_regression_tests() -> Result<()> {
let sdk = sdk_dir();
let workspace = workspace_dir();
let server_url = env::var("SPACETIMEDB_SERVER_URL").unwrap_or_else(|_| "local".to_string());
cmd!("cargo", "regen", "csharp", "regression-tests").run()?;
cmd!("cargo", "build", "--manifest-path", path_arg(&standalone_manifest())).run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"publish",
"-c",
"-y",
"--server",
&server_url,
"-p",
path_arg(&sdk.join("examples~/regression-tests/server")),
"btree-repro",
)
.run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"publish",
"-c",
"-y",
"--server",
&server_url,
"-p",
path_arg(&sdk.join("examples~/regression-tests/republishing/server-initial")),
"republish-test",
)
.run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"call",
"--server",
&server_url,
"republish-test",
"insert",
"1",
)
.run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"publish",
"--server",
&server_url,
"-p",
path_arg(&sdk.join("examples~/regression-tests/republishing/server-republish")),
"--break-clients",
"republish-test",
)
.run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"call",
"--server",
&server_url,
"republish-test",
"insert",
"2",
)
.run()?;
clean_procedure_obj_tilde_dirs()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"publish",
"-c",
"-y",
"--server",
&server_url,
"-p",
path_arg(&workspace.join("modules/sdk-test-procedure")),
"procedure-tests",
)
.run()?;
cmd!("dotnet", "run", "-c", "Debug")
.dir(sdk.join("examples~/regression-tests/client"))
.run()?;
cmd!("dotnet", "run", "-c", "Debug")
.dir(sdk.join("examples~/regression-tests/republishing/client"))
.run()?;
cmd!("dotnet", "run", "-c", "Debug")
.dir(sdk.join("examples~/regression-tests/procedure-client"))
.run()?;
Ok(())
}
+36
View File
@@ -0,0 +1,36 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "csharp-tools", bin_name = "cargo csharp", about = "C# SDK maintenance tasks")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// Write NuGet.Config files that point at local SpacetimeDB C# packages.
WriteNugetConfig {
/// Path to the SpacetimeDB repository whose C# packages should be used.
spacetimedb_repo_path: Option<PathBuf>,
},
/// Run the C# regression test workflow against a running local SpacetimeDB instance.
RunRegressionTests,
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Command::WriteNugetConfig { spacetimedb_repo_path } => {
csharp_tools::write_persistent_nuget_configs(spacetimedb_repo_path.as_deref())?;
}
Command::RunRegressionTests => {
csharp_tools::run_regression_tests()?;
}
}
Ok(())
}
+303
View File
@@ -0,0 +1,303 @@
#![allow(clippy::disallowed_macros)]
use anyhow::{bail, Result};
use duct::cmd;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
const BSATN_PACKAGE_ID: &str = "spacetimedb.bsatn.runtime";
const RUNTIME_PACKAGE_ID: &str = "spacetimedb.runtime";
fn workspace_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.expect("tools/regen should be two levels below the workspace root")
.to_path_buf()
}
fn sdk_dir() -> PathBuf {
workspace_dir().join("sdks/csharp")
}
fn cli_manifest() -> PathBuf {
workspace_dir().join("crates/cli/Cargo.toml")
}
fn standalone_manifest() -> PathBuf {
workspace_dir().join("crates/standalone/Cargo.toml")
}
fn path_arg(path: &Path) -> String {
path.to_string_lossy().into_owned()
}
fn render_nuget_config(bsatn_source: &Path, runtime_source: &Path) -> String {
format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<!-- Experimental NuGet feed for Microsoft.DotNet.ILCompiler.LLVM packages -->
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<!-- Local NuGet repositories -->
<add key="Local SpacetimeDB.BSATN.Runtime" value="{}" />
<!-- We need to override the module runtime as well because the examples use it -->
<add key="Local SpacetimeDB.Runtime" value="{}" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<!-- Ensure that SpacetimeDB.BSATN.Runtime is used from the local folder. -->
<!-- Otherwise we risk an outdated version being quietly pulled from NuGet for testing. -->
<packageSource key="Local SpacetimeDB.BSATN.Runtime">
<package pattern="SpacetimeDB.BSATN.Runtime" />
</packageSource>
<packageSource key="Local SpacetimeDB.Runtime">
<package pattern="SpacetimeDB.Runtime" />
</packageSource>
<!-- Experimental packages for NativeAOT-LLVM compilation -->
<packageSource key="dotnet-experimental">
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
<package pattern="runtime.*" />
</packageSource>
<!-- Fallback for other packages (e.g. test deps). -->
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
"#,
bsatn_source.display(),
runtime_source.display(),
)
}
pub fn regen_regression_tests() -> Result<()> {
let sdk = sdk_dir();
let workspace = workspace_dir();
cmd!("cargo", "build", "--manifest-path", path_arg(&standalone_manifest())).run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"generate",
"-y",
"-l",
"csharp",
"-o",
path_arg(&sdk.join("examples~/regression-tests/client/module_bindings")),
"--module-path",
path_arg(&sdk.join("examples~/regression-tests/server")),
)
.run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"generate",
"-y",
"-l",
"csharp",
"-o",
path_arg(&sdk.join("examples~/regression-tests/republishing/client/module_bindings")),
"--module-path",
path_arg(&sdk.join("examples~/regression-tests/republishing/server-republish")),
)
.run()?;
cmd!(
"cargo",
"run",
"--manifest-path",
path_arg(&cli_manifest()),
"--",
"generate",
"-y",
"-l",
"csharp",
"-o",
path_arg(&sdk.join("examples~/regression-tests/procedure-client/module_bindings")),
"--module-path",
path_arg(&workspace.join("modules/sdk-test-procedure")),
)
.run()?;
Ok(())
}
fn overlay_unity_meta_skeleton(pkg_id: &str) -> Result<()> {
let sdk = sdk_dir();
let skeleton_base = sdk.join("unity-meta-skeleton~");
let skeleton_root = skeleton_base.join(pkg_id);
if !skeleton_root.exists() {
return Ok(());
}
let pkg_root = sdk.join("packages").join(pkg_id);
if !pkg_root.exists() {
return Ok(());
}
let pkg_root_meta = skeleton_base.join(format!("{pkg_id}.meta"));
if pkg_root_meta.exists()
&& let Some(parent) = pkg_root.parent()
{
let pkg_meta_dst = parent.join(format!("{pkg_id}.meta"));
fs::copy(&pkg_root_meta, &pkg_meta_dst)?;
}
let versioned_dir = match find_only_subdir(&pkg_root) {
Ok(dir) => dir,
Err(err) => {
eprintln!("Skipping Unity meta overlay for {pkg_id}: could not locate restored version dir: {err}");
return Ok(());
}
};
let version_meta_template = skeleton_root.join("version.meta");
if version_meta_template.exists()
&& let Some(parent) = versioned_dir.parent()
{
let version_name = versioned_dir
.file_name()
.expect("versioned directory should have a file name");
let version_meta_dst = parent.join(format!("{}.meta", version_name.to_string_lossy()));
fs::copy(&version_meta_template, &version_meta_dst)?;
}
copy_overlay_dir(&skeleton_root, &versioned_dir)
}
fn clear_restored_package_dirs(pkg_id: &str) -> Result<()> {
let pkg_root = sdk_dir().join("packages").join(pkg_id);
if pkg_root.exists() {
fs::remove_dir_all(&pkg_root)?;
}
Ok(())
}
fn find_only_subdir(dir: &Path) -> Result<PathBuf> {
let mut subdirs = vec![];
for entry in fs::read_dir(dir)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
subdirs.push(entry.path());
}
}
match subdirs.as_slice() {
[] => bail!("Could not find a restored versioned directory under {}", dir.display()),
[only] => Ok(only.clone()),
_ => bail!(
"Expected exactly one restored versioned directory under {}, found {}",
dir.display(),
subdirs.len()
),
}
}
fn copy_overlay_dir(src: &Path, dst: &Path) -> Result<()> {
if !src.exists() {
bail!("Skeleton directory does not exist: {}", src.display());
}
if !dst.exists() {
bail!("Destination directory does not exist: {}", dst.display());
}
for entry in fs::read_dir(src)? {
let entry = entry?;
let src_path = entry.path();
let dst_path = dst.join(entry.file_name());
if entry.file_type()?.is_dir() {
if dst_path.exists() {
copy_overlay_dir(&src_path, &dst_path)?;
}
} else {
if src_path.extension().is_some_and(|ext| ext == "meta") {
let asset_path = dst_path
.parent()
.expect("dst_path should have a parent")
.join(dst_path.file_stem().expect(".meta file should have a file stem"));
if asset_path.exists() {
fs::copy(&src_path, &dst_path)?;
} else if dst_path.exists() {
fs::remove_file(&dst_path)?;
}
continue;
}
fs::copy(&src_path, &dst_path)?;
}
}
Ok(())
}
pub fn regen_dlls() -> Result<()> {
let workspace = workspace_dir();
let sdk = sdk_dir();
cmd!(
"dotnet",
"pack",
workspace.join("crates/bindings-csharp/BSATN.Runtime"),
"-c",
"Release"
)
.run()?;
cmd!(
"dotnet",
"pack",
workspace.join("crates/bindings-csharp/Runtime"),
"-c",
"Release"
)
.run()?;
let nuget_config_dir = tempfile::tempdir()?;
let nuget_config_path = nuget_config_dir.path().join("nuget.config");
fs::write(
&nuget_config_path,
render_nuget_config(
&workspace.join("crates/bindings-csharp/BSATN.Runtime/bin/Release"),
&workspace.join("crates/bindings-csharp/Runtime/bin/Release"),
),
)?;
clear_restored_package_dirs(BSATN_PACKAGE_ID)?;
clear_restored_package_dirs(RUNTIME_PACKAGE_ID)?;
cmd!(
"dotnet",
"restore",
"SpacetimeDB.ClientSDK.csproj",
"--configfile",
path_arg(&nuget_config_path),
)
.dir(&sdk)
.run()?;
overlay_unity_meta_skeleton(BSATN_PACKAGE_ID)?;
overlay_unity_meta_skeleton(RUNTIME_PACKAGE_ID)?;
cmd!(
"dotnet",
"pack",
"SpacetimeDB.ClientSDK.csproj",
"-c",
"Release",
"--no-restore"
)
.dir(&sdk)
.run()?;
Ok(())
}
+41
View File
@@ -0,0 +1,41 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
mod csharp;
#[derive(Parser)]
#[command(name = "regen", bin_name = "cargo regen", about = "Regenerate checked-in artifacts")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// Regenerate C# SDK artifacts.
Csharp {
#[command(subcommand)]
command: CsharpCommand,
},
}
#[derive(Subcommand)]
enum CsharpCommand {
/// Regenerate C# regression test bindings.
RegressionTests,
/// Regenerate C# DLL and NuGet package artifacts for Unity workflows.
Dlls,
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Csharp { command } => match command {
CsharpCommand::RegressionTests => csharp::regen_regression_tests()?,
CsharpCommand::Dlls => csharp::regen_dlls()?,
},
}
Ok(())
}