Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 5 additions & 3 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,17 @@ jobs:
VERSION="${{ needs.release-please.outputs.cli_version }}"
echo "Bundling templates for version ${VERSION}"

# Create tarball with only the template directories (excluding node_modules, dist, .env)
# Create tarball with only the template directories (excluding build artifacts)
tar -czvf "hyperstack-templates-v${VERSION}.tar.gz" \
--exclude='node_modules' \
--exclude='dist' \
--exclude='.env' \
--exclude='package-lock.json' \
--exclude='target' \
--exclude='Cargo.lock' \
-C examples \
pumpfun-react \
ore-react
ore-react \
ore-rust

- name: Upload templates to CLI release
env:
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ exclude = [
"stacks/pumpfun",
"stacks/ore",
"examples/rust-client",
"examples/pumpfun-server",
"examples/ore-server",
]

Expand Down
48 changes: 35 additions & 13 deletions cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ pub fn create(

let selected_template = match template {
Some(t) => Template::from_str(&t).ok_or_else(|| {
anyhow::anyhow!(
"Unknown template: {}. Available: react-pumpfun, react-ore",
t
)
anyhow::anyhow!("Unknown template: {}. Available: react-ore, rust-ore", t)
})?,
None => {
let items: Vec<String> = Template::ALL
Expand Down Expand Up @@ -99,22 +96,29 @@ pub fn create(

println!(" {} Project scaffolded", ui::symbols::SUCCESS.green());

let pm = detect_package_manager();
let install_succeeded = if skip_install {
false
let is_rust_project = selected_template.is_rust();

if is_rust_project {
println!();
print_rust_next_steps(&project_name);
} else {
run_install(project_dir, pm)?
};
let pm = detect_package_manager();
let install_succeeded = if skip_install {
false
} else {
run_npm_install(project_dir, pm)?
};

println!();
print_next_steps(&project_name, pm, install_succeeded);
println!();
print_js_next_steps(&project_name, pm, install_succeeded);
}

telemetry::record_create_completed(selected_template.display_name(), start.elapsed());

Ok(())
}

fn run_install(project_dir: &Path, pm: &str) -> Result<bool> {
fn run_npm_install(project_dir: &Path, pm: &str) -> Result<bool> {
ui::print_step("Installing dependencies...");

let (cmd, args) = match pm {
Expand Down Expand Up @@ -149,7 +153,7 @@ fn run_install(project_dir: &Path, pm: &str) -> Result<bool> {
}
}

fn print_next_steps(project_name: &str, pm: &str, install_succeeded: bool) {
fn print_js_next_steps(project_name: &str, pm: &str, install_succeeded: bool) {
println!(
"{} {}",
ui::symbols::SUCCESS.green().bold(),
Expand Down Expand Up @@ -180,3 +184,21 @@ fn print_next_steps(project_name: &str, pm: &str, install_succeeded: bool) {

println!();
}

fn print_rust_next_steps(project_name: &str) {
println!(
"{} {}",
ui::symbols::SUCCESS.green().bold(),
"Ready!".bold()
);
println!();
println!("Build and run:");
println!();
println!(
" {} {} && {}",
"$".dimmed(),
format!("cd {}", project_name).cyan(),
"cargo run".cyan()
);
println!();
}
2 changes: 1 addition & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ enum Commands {
/// Project name (creates directory)
name: Option<String>,

/// Template: react-pumpfun, react-ore
/// Template: react-ore, rust-ore
#[arg(short, long)]
template: Option<String>,

Expand Down
49 changes: 42 additions & 7 deletions cli/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,50 @@ use tar::Archive;
/// Available project templates.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Template {
ReactPumpfun,
ReactOre,
RustOre,
}

impl Template {
/// All available templates.
pub const ALL: &'static [Template] = &[Template::ReactPumpfun, Template::ReactOre];
pub const ALL: &'static [Template] = &[Template::ReactOre, Template::RustOre];

/// Template directory name (as stored in tarball).
pub fn dir_name(&self) -> &'static str {
match self {
Template::ReactPumpfun => "pumpfun-react",
Template::ReactOre => "ore-react",
Template::RustOre => "ore-rust",
}
}

/// Human-readable display name.
pub fn display_name(&self) -> &'static str {
match self {
Template::ReactPumpfun => "react-pumpfun",
Template::ReactOre => "react-ore",
Template::RustOre => "rust-ore",
}
}

/// Description for interactive selection.
pub fn description(&self) -> &'static str {
match self {
Template::ReactPumpfun => "PumpFun token dashboard (React + Vite)",
Template::ReactOre => "ORE mining rounds viewer (React + Vite)",
Template::RustOre => "ORE mining rounds client (Rust + Tokio)",
}
}

/// Parse from string (CLI argument).
pub fn from_str(s: &str) -> Option<Template> {
match s.to_lowercase().as_str() {
"react-pumpfun" | "pumpfun-react" | "pumpfun" => Some(Template::ReactPumpfun),
"react-ore" | "ore-react" | "ore" => Some(Template::ReactOre),
"react-ore" | "ore-react" => Some(Template::ReactOre),
"rust-ore" | "ore-rust" | "ore" => Some(Template::RustOre),
_ => None,
}
}

pub fn is_rust(&self) -> bool {
matches!(self, Template::RustOre)
}
}

/// Template manager handles fetching, caching, and extracting templates.
Expand Down Expand Up @@ -218,6 +222,7 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> {
/// Customize the scaffolded project.
pub fn customize_project(project_dir: &Path, project_name: &str) -> Result<()> {
update_package_json_name(project_dir, project_name)?;
update_cargo_toml_name(project_dir, project_name)?;
update_html_title(project_dir, project_name)?;
copy_env_example(project_dir)?;
Ok(())
Expand Down Expand Up @@ -246,6 +251,36 @@ fn update_package_json_name(project_dir: &Path, project_name: &str) -> Result<()
Ok(())
}

fn update_cargo_toml_name(project_dir: &Path, project_name: &str) -> Result<()> {
let path = project_dir.join("Cargo.toml");
if !path.exists() {
return Ok(());
}

let content = fs::read_to_string(&path).context("Failed to read Cargo.toml")?;

let updated = content
.lines()
.map(|line| {
if line.starts_with("name = ") {
format!("name = \"{}\"", project_name)
} else {
line.to_string()
}
})
.collect::<Vec<_>>()
.join("\n");

let updated = if content.ends_with('\n') {
format!("{}\n", updated)
} else {
updated
};

fs::write(&path, updated).context("Failed to write Cargo.toml")?;
Ok(())
}

fn update_html_title(project_dir: &Path, project_name: &str) -> Result<()> {
let path = project_dir.join("index.html");
if !path.exists() {
Expand Down
18 changes: 9 additions & 9 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@

Usage examples for Hyperstack stacks.

## TypeScript

```bash
cd typescript-basic
npm install && npm start
```

## React

```bash
cd pumpfun-react
cd ore-react
npm install && npm run dev
```

## Rust

```bash
cd rust-client
cd ore-rust
cargo run
```

## TypeScript (CLI)

```bash
cd ore-typescript
npm install && npm start
```
1 change: 0 additions & 1 deletion examples/ore-react/.env.example

This file was deleted.

4 changes: 1 addition & 3 deletions examples/ore-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { OreDashboard } from './components/OreDashboard';
import { HyperstackProvider } from 'hyperstack-react';

const websocketUrl = import.meta.env.VITE_HYPERSTACK_WS_URL;

export default function App() {
return (
<HyperstackProvider
websocketUrl={websocketUrl}
websocketUrl="wss://ore.stack.usehyperstack.com"
autoConnect={true}
>
<OreDashboard />
Expand Down
4 changes: 2 additions & 2 deletions examples/ore-rust/Cargo.lock

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

4 changes: 2 additions & 2 deletions examples/ore-rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use hyperstack_stacks::ore::OreRoundViews;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let hs = HyperStack::connect("wss://ore-round-ubhivg.stack.usehyperstack.com").await?;
println!("Connected to wss://ore-round-ubhivg.stack.usehyperstack.com");
let hs = HyperStack::connect("wss://ore.stack.usehyperstack.com").await?;
println!("Connected to wss://ore.stack.usehyperstack.com");

let views = hs.views::<OreRoundViews>();

Expand Down
10 changes: 5 additions & 5 deletions examples/ore-server/Cargo.lock

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

22 changes: 22 additions & 0 deletions examples/ore-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "ore-typescript-example",
"version": "0.1.0",
"type": "module",
"private": true,
"description": "TypeScript CLI example for Ore Round Hyperstack",
"main": "src/main.ts",
"scripts": {
"start": "npx tsx src/main.ts",
"build": "tsc",
"dev": "npx tsx --watch src/main.ts"
},
"dependencies": {
"hyperstack-typescript": "file:../../typescript/core",
"hyperstack-stacks": "file:../../stacks/sdk/typescript"
},
"devDependencies": {
"typescript": "^5.0.0",
"tsx": "^4.0.0",
"@types/node": "^20.0.0"
}
}
Loading