diff --git a/_scripts/check-templates.rs b/_scripts/check-templates.rs index c3a73769..4dfd599c 100644 --- a/_scripts/check-templates.rs +++ b/_scripts/check-templates.rs @@ -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" //! ``` @@ -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::(); let thread = @@ -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); @@ -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() { @@ -94,5 +102,5 @@ fn main() { std::process::exit(1); } - println!("Template definitions verified ✅") + eprintln!("Template definitions verified ✅") } diff --git a/_scripts/ci.sh b/_scripts/ci.sh index e141c13b..2f27b2db 100755 --- a/_scripts/ci.sh +++ b/_scripts/ci.sh @@ -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 diff --git a/_scripts/sync-all-template-repos.sh b/_scripts/sync-all-template-repos.sh new file mode 100755 index 00000000..74d0b11a --- /dev/null +++ b/_scripts/sync-all-template-repos.sh @@ -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 diff --git a/_scripts/sync-template-repo.sh b/_scripts/sync-template-repo.sh new file mode 100755 index 00000000..8b1d3c20 --- /dev/null +++ b/_scripts/sync-template-repo.sh @@ -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 diff --git a/templates.toml b/templates.toml index 6798b9ba..5ba469f8 100644 --- a/templates.toml +++ b/templates.toml @@ -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" @@ -45,6 +46,7 @@ path = "axum/hello-world" use_cases = ["Web app"] tags = ["axum"] template = "axum" +has_template_repo = true [starters.bevy-hello-world] title = "Bevy" @@ -52,6 +54,7 @@ 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" @@ -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" @@ -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" @@ -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" @@ -254,6 +260,7 @@ 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" @@ -261,6 +268,7 @@ 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" @@ -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" @@ -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"