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
14 changes: 11 additions & 3 deletions _scripts/check-templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
//! ```cargo
//! [dependencies]
//! shuttle-common = "*" # always use latest version
//! toml = "0.8"
//! # for local use
//! # shuttle-common = { path = "../../common" }
//! toml = "0.9"
//! ignore = "0.4"
//! ```

Expand All @@ -13,6 +15,8 @@ fn main() {
let s = std::fs::read_to_string("templates.toml").expect("to find file");
let schema: TemplatesSchema = toml::from_str(&s).expect("to parse toml file");

let print_template_repos = std::env::args().any(|a| a == "--list-template-repos");

let (tx, rx) = std::sync::mpsc::channel::<String>();

let thread =
Expand Down Expand Up @@ -70,7 +74,7 @@ fn main() {
std::process::exit(1);
}

let path = t.path.unwrap_or_default();
let path = t.path.expect("template to have a path field");

if !set.insert(path.clone()) {
eprintln!("Path '{}' referenced in two places", path);
Expand All @@ -84,6 +88,10 @@ fn main() {
);
std::process::exit(1);
}

if print_template_repos && t.has_template_repo.is_some_and(|t| t) {
println!("{}|{}", name, path);
}
}

if !manifests.is_empty() {
Expand All @@ -94,5 +102,5 @@ fn main() {
std::process::exit(1);
}

println!("Template definitions verified ✅")
eprintln!("Template definitions verified ✅")
}
3 changes: 2 additions & 1 deletion _scripts/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ DIRS=$(find . -name Cargo.toml -exec dirname {} \; | sort | awk -v from="$FROM"

# Loop through each directory and run cargo fmt and cargo clippy, exit on failure
for dir in $DIRS; do
# skip workspace roots
grep "\[workspace\]" "$dir/Cargo.toml" > /dev/null && continue

echo "Checking $dir"
cargo fmt --all --manifest-path "$dir/Cargo.toml" -- --check
cargo clippy --no-deps --manifest-path "$dir/Cargo.toml" -- -D warnings
Expand Down
15 changes: 15 additions & 0 deletions _scripts/sync-all-template-repos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Run with:
# bash _scripts/sync-all-template-repos.sh
# Dependencies:
# - rust-script
# - + all dependencies of _scripts/sync-template-repo.sh

set -euo pipefail

for line in $(rust-script _scripts/check-templates.rs -- --list-template-repos); do
name="$(echo "$line" | cut -d'|' -f1)"
path="$(echo "$line" | cut -d'|' -f2)"
echo "Synching $name $path"
bash _scripts/sync-template-repo.sh "$name" "$path"
done
60 changes: 60 additions & 0 deletions _scripts/sync-template-repo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Run with:
# bash _scripts/sync-template-repo.sh [template name] [path]
# where [template name] is the hashmap key in templates.toml
# and [path] is the path attribute of the template
# Dependencies:
# - shuttle CLI (no auth)
# - gh CLI (authenticated with sufficient permissions for the org)
# - git (will create commits with your default name+email)

set -euo pipefail

OWNER="shuttle-hq"
NAME="$1"
TEMPLATE_PATH="$2"

export GH_PAGER=""

set +e
repo=$(gh repo create "$OWNER/$NAME" --public)
if echo "$repo" | grep "Name already exists" -q; then
# GraphQL: Name already exists on this account (createRepository)
echo "already exists"
fi
set -e

# add admin permission for core and devrel teams
gh api -X PUT -H "Accept: application/vnd.github+json" "/orgs/$OWNER/teams/core/repos/$OWNER/$NAME" -f permission=admin
gh api -X PUT -H "Accept: application/vnd.github+json" "/orgs/$OWNER/teams/devrel/repos/$OWNER/$NAME" -f permission=admin

# set repo to be a template
gh api -X PATCH -H "Accept: application/vnd.github+json" "/repos/$OWNER/$NAME" -f is_template=true

tmp=$(mktemp -d)

shuttle init --from "./$TEMPLATE_PATH" --name "$NAME" $tmp
pushd $tmp

# get a fresh Cargo.lock
cargo update

# add template info to end of README
echo "

## Shuttle template: $NAME

This template ([repo](https://github.com/$OWNER/$NAME)) is a synced replica from [shuttle-examples](https://github.com/shuttle-hq/shuttle-examples/tree/main/$TEMPLATE_PATH).

[Deploy on Shuttle with just a few clicks!](https://console.shuttle.dev/templates/$NAME)

" >> README.md

git add .
git commit -am "Sync $NAME with shuttle-hq/shuttle-examples repo"

git remote add origin git@github.com:$OWNER/$NAME.git
git branch -M main
git push -u origin main --force

popd
10 changes: 10 additions & 0 deletions templates.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ path = "actix-web/hello-world"
use_cases = ["Web app"]
tags = ["actix-web"]
template = "actix-web"
has_template_repo = true

[starters.axum-hello-world]
title = "Axum"
Expand All @@ -45,13 +46,15 @@ path = "axum/hello-world"
use_cases = ["Web app"]
tags = ["axum"]
template = "axum"
has_template_repo = true

[starters.bevy-hello-world]
title = "Bevy"
description = "Data driven game engine that compiles to WASM"
path = "bevy/hello-world"
use_cases = ["Web app", "Game"]
tags = ["bevy", "axum"]
has_template_repo = true

[starters.loco-hello-world]
title = "Loco"
Expand All @@ -60,6 +63,7 @@ path = "loco/hello-world"
use_cases = ["Web app"]
tags = ["loco"]
template = "loco"
has_template_repo = true

[starters.poem-hello-world]
title = "Poem"
Expand Down Expand Up @@ -92,6 +96,7 @@ path = "rocket/hello-world"
use_cases = ["Web app"]
tags = ["rocket"]
template = "rocket"
has_template_repo = true

[starters.salvo-hello-world]
title = "Salvo"
Expand Down Expand Up @@ -233,6 +238,7 @@ description = "CRUD API, Postgres database, and static file server combined into
path = "axum/todo-list"
use_cases = ["Web app", "Storage"]
tags = ["axum", "postgres", "database"]
has_template_repo = true

[templates.axum-todo-app]
title = "Todo app"
Expand All @@ -254,13 +260,15 @@ description = "Health monitoring service using websockets"
path = "axum/websocket"
use_cases = ["Web app", "Monitoring"]
tags = ["axum", "websocket"]
has_template_repo = true

[templates.axum-next-fullstack-saas]
title = "Fullstack SaaS"
description = "Opinionated fullstack web app with pre-made routes and assets"
path = "fullstack-templates/saas"
use_cases = ["Web app", "Authentication", "Storage", "Monetization"]
tags = ["nextjs", "axum", "database", "postgres", "typescript", "tailwind"]
has_template_repo = true

[templates.rama-hello-world-tcp]
title = "Rama TCP"
Expand Down Expand Up @@ -324,6 +332,7 @@ description = "Use the image library to rescale the Shuttle logo"
path = "salvo/image-rescaler"
use_cases = ["Web app", "Image processing"]
tags = ["salvo", "image"]
has_template_repo = true

[templates.serenity-postgres]
title = "Postgres"
Expand Down Expand Up @@ -359,6 +368,7 @@ description = "Model Context Protocol server with Server-Sent Events transport"
path = "mcp/mcp-sse"
use_cases = ["Web app", "MCP"]
tags = ["axum", "mcp", "sse"]
has_template_repo = true

[templates.mcp-sse-oauth]
title = "SSE MCP Server with OAuth"
Expand Down
Loading