From 7202bd66bee07817ddfa9f309b71e740f673a825 Mon Sep 17 00:00:00 2001 From: jonaro00 <54029719+jonaro00@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:12:02 +0100 Subject: [PATCH] chore: v0.42.0 (#271) * local run docker tips * 0.42.0 * update references to shuttle-secrets * update metadata page * update secrets page * --raw * diesel shared db return types --- configuration/shuttle-versions.mdx | 2 +- examples/actix-postgres.mdx | 6 +- examples/actix-websocket-actorless.mdx | 4 +- examples/actix.mdx | 4 +- examples/axum-jwt-authentication.mdx | 4 +- examples/axum-postgres.mdx | 6 +- examples/axum-static-files.mdx | 4 +- examples/axum-websockets.mdx | 4 +- examples/axum.mdx | 4 +- examples/poise.mdx | 9 +- examples/rocket-jwt-authentication.mdx | 4 +- examples/rocket-postgres.mdx | 6 +- examples/rocket-static-files.mdx | 4 +- examples/rocket.mdx | 4 +- examples/serenity-todo.mdx | 11 ++- examples/serenity.mdx | 17 ++-- getting-started/local-run.mdx | 38 +++++++++ getting-started/shuttle-commands.mdx | 1 + introduction/how-shuttle-works.mdx | 8 +- migration/migrating-to-shuttle.mdx | 14 +--- resources/custom-resources.mdx | 6 +- resources/overview.mdx | 2 +- resources/shuttle-metadata.mdx | 14 ++-- resources/shuttle-secrets.mdx | 37 ++++---- resources/shuttle-shared-db.mdx | 3 + support/troubleshooting.mdx | 12 +-- templates/tutorials/custom-service.mdx | 18 ++-- .../tutorials/discord-weather-forecast.mdx | 84 ++++++++----------- .../tutorials/send-your-logs-to-datadog.mdx | 16 ++-- templates/tutorials/url-shortener.mdx | 8 +- templates/tutorials/websocket-chat-app-js.mdx | 28 +++---- 31 files changed, 186 insertions(+), 196 deletions(-) diff --git a/configuration/shuttle-versions.mdx b/configuration/shuttle-versions.mdx index 9a29e02..efddab4 100644 --- a/configuration/shuttle-versions.mdx +++ b/configuration/shuttle-versions.mdx @@ -21,7 +21,7 @@ Combining all of the above, these are the recommended steps for upgrading a Shut 2. Update your project's `shuttle-...` dependencies in `Cargo.toml`: ```toml Cargo.toml - shuttle-runtime = "0.41.0" + shuttle-runtime = "0.42.0" # etc ``` diff --git a/examples/actix-postgres.mdx b/examples/actix-postgres.mdx index bda78bc..2909186 100644 --- a/examples/actix-postgres.mdx +++ b/examples/actix-postgres.mdx @@ -117,10 +117,10 @@ edition = "2021" [dependencies] actix-web = "4.3.1" -shuttle-actix-web = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-actix-web = "0.42.0" +shuttle-runtime = "0.42.0" serde = "1.0.148" -shuttle-shared-db = { version = "0.41.0", features = ["postgres", "sqlx"] } +shuttle-shared-db = { version = "0.42.0", features = ["postgres", "sqlx"] } sqlx = "0.7.1" tokio = "1.26.0" ``` diff --git a/examples/actix-websocket-actorless.mdx b/examples/actix-websocket-actorless.mdx index a329daa..b924b93 100644 --- a/examples/actix-websocket-actorless.mdx +++ b/examples/actix-websocket-actorless.mdx @@ -407,8 +407,8 @@ futures = "0.3" reqwest = "0.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -shuttle-actix-web = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-actix-web = "0.42.0" +shuttle-runtime = "0.42.0" tokio = { version = "1", features = ["rt-multi-thread", "sync"] } tracing = "0.1" ``` diff --git a/examples/actix.mdx b/examples/actix.mdx index dbe5156..86d3536 100644 --- a/examples/actix.mdx +++ b/examples/actix.mdx @@ -45,8 +45,8 @@ edition = "2021" [dependencies] actix-web = "4.3.1" -shuttle-actix-web = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-actix-web = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.26.0" ``` diff --git a/examples/axum-jwt-authentication.mdx b/examples/axum-jwt-authentication.mdx index 314bb38..ed74241 100644 --- a/examples/axum-jwt-authentication.mdx +++ b/examples/axum-jwt-authentication.mdx @@ -221,8 +221,8 @@ jsonwebtoken = "8.3.0" once_cell = "1.18.0" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" -shuttle-axum = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.28.2" tracing-subscriber = "0.3.17" ``` diff --git a/examples/axum-postgres.mdx b/examples/axum-postgres.mdx index 0b1bf65..dd8ccbb 100644 --- a/examples/axum-postgres.mdx +++ b/examples/axum-postgres.mdx @@ -102,9 +102,9 @@ edition = "2021" [dependencies] axum = "0.7.3" serde = { version = "1.0.188", features = ["derive"] } -shuttle-axum = "0.41.0" -shuttle-runtime = "0.41.0" -shuttle-shared-db = { version = "0.41.0", features = ["postgres", "sqlx"] } +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" +shuttle-shared-db = { version = "0.42.0", features = ["postgres", "sqlx"] } sqlx = "0.7.1" tokio = "1.28.2" ``` diff --git a/examples/axum-static-files.mdx b/examples/axum-static-files.mdx index 3fbeefc..998ee86 100644 --- a/examples/axum-static-files.mdx +++ b/examples/axum-static-files.mdx @@ -58,8 +58,8 @@ publish = false [dependencies] axum = "0.7.3" -shuttle-axum = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.28.2" tower-http = { version = "0.5.0", features = ["fs"] } ``` diff --git a/examples/axum-websockets.mdx b/examples/axum-websockets.mdx index cd7ba43..c118612 100644 --- a/examples/axum-websockets.mdx +++ b/examples/axum-websockets.mdx @@ -266,8 +266,8 @@ futures = "0.3.28" reqwest = "0.11.23" serde = { version = "1.0.163", features = ["derive"] } serde_json = "1.0.96" -shuttle-axum = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.28.2" tower-http = { version = "0.5.0", features = ["fs"] } ``` diff --git a/examples/axum.mdx b/examples/axum.mdx index a2b5232..d70a8d3 100644 --- a/examples/axum.mdx +++ b/examples/axum.mdx @@ -43,8 +43,8 @@ edition = "2021" [dependencies] axum = "0.7.3" -shuttle-axum = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.28.2" ``` diff --git a/examples/poise.mdx b/examples/poise.mdx index 65d6b4a..835b8b5 100644 --- a/examples/poise.mdx +++ b/examples/poise.mdx @@ -15,7 +15,7 @@ This example shows how to build a Poise bot with Shuttle that responds to the `/ ```rust src/main.rs use anyhow::Context as _; use poise::serenity_prelude::{ClientBuilder, GatewayIntents}; -use shuttle_secrets::SecretStore; +use shuttle_runtime::SecretStore; use shuttle_serenity::ShuttleSerenity; struct Data {} // User data, which is stored and accessible in all command invocations @@ -30,7 +30,7 @@ async fn hello(ctx: Context<'_>) -> Result<(), Error> { } #[shuttle_runtime::main] -async fn main(#[shuttle_secrets::Secrets] secret_store: SecretStore) -> ShuttleSerenity { +async fn main(#[shuttle_runtime::Secrets] secret_store: SecretStore) -> ShuttleSerenity { // Get the discord token set in `Secrets.toml` let discord_token = secret_store .get("DISCORD_TOKEN") @@ -72,10 +72,9 @@ publish = false [dependencies] anyhow = "1.0.68" poise = "0.6.1" -shuttle-runtime = "0.41.0" -shuttle-secrets = "0.41.0" +shuttle-runtime = "0.42.0" # Since poise is a serenity command framework, it can run on Shuttle with shuttle-serenity -shuttle-serenity = "0.41.0" +shuttle-serenity = "0.42.0" tracing = "0.1.37" tokio = "1.26.0" ``` diff --git a/examples/rocket-jwt-authentication.mdx b/examples/rocket-jwt-authentication.mdx index 53c5eb5..4b56278 100644 --- a/examples/rocket-jwt-authentication.mdx +++ b/examples/rocket-jwt-authentication.mdx @@ -38,8 +38,8 @@ jsonwebtoken = { version = "8.1.1", default-features = false } lazy_static = "1.4.0" rocket = { version = "0.5.0", features = ["json"] } serde = { version = "1.0.148", features = ["derive"] } -shuttle-rocket = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-rocket = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.26.0" ``` Your `main.rs` should look like this: diff --git a/examples/rocket-postgres.mdx b/examples/rocket-postgres.mdx index 6b751e8..d9d7e48 100644 --- a/examples/rocket-postgres.mdx +++ b/examples/rocket-postgres.mdx @@ -104,9 +104,9 @@ edition = "2021" [dependencies] rocket = { version = "0.5.0", features = ["json"] } serde = "1.0.148" -shuttle-rocket = "0.41.0" -shuttle-runtime = "0.41.0" -shuttle-shared-db = { version = "0.41.0", features = ["postgres", "sqlx"] } +shuttle-rocket = "0.42.0" +shuttle-runtime = "0.42.0" +shuttle-shared-db = { version = "0.42.0", features = ["postgres", "sqlx"] } sqlx = "0.7.1" tokio = "1.26.0" ``` diff --git a/examples/rocket-static-files.mdx b/examples/rocket-static-files.mdx index 487f9b4..77404d9 100644 --- a/examples/rocket-static-files.mdx +++ b/examples/rocket-static-files.mdx @@ -76,8 +76,8 @@ publish = false [dependencies] rocket = "0.5.0" -shuttle-rocket = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-rocket = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.26.0" ``` diff --git a/examples/rocket.mdx b/examples/rocket.mdx index 921abfd..c6bad55 100644 --- a/examples/rocket.mdx +++ b/examples/rocket.mdx @@ -43,8 +43,8 @@ edition = "2021" [dependencies] rocket = "0.5.0" -shuttle-rocket = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-rocket = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.26.0" ``` diff --git a/examples/serenity-todo.mdx b/examples/serenity-todo.mdx index f98444a..566faf4 100644 --- a/examples/serenity-todo.mdx +++ b/examples/serenity-todo.mdx @@ -38,7 +38,7 @@ use serenity::model::application::{CommandDataOptionValue, CommandOptionType, In use serenity::model::gateway::Ready; use serenity::model::id::GuildId; use serenity::prelude::*; -use shuttle_secrets::SecretStore; +use shuttle_runtime::SecretStore; use sqlx::{Executor, PgPool}; use tracing::{error, info}; @@ -158,7 +158,7 @@ impl EventHandler for Bot { #[shuttle_runtime::main] async fn serenity( #[shuttle_shared_db::Postgres] pool: PgPool, - #[shuttle_secrets::Secrets] secret_store: SecretStore, + #[shuttle_runtime::Secrets] secret_store: SecretStore, ) -> shuttle_serenity::ShuttleSerenity { // Get the discord token set in `Secrets.toml` let token = secret_store @@ -269,10 +269,9 @@ edition = "2021" anyhow = "1.0.66" serde = "1.0.148" serenity = { version = "0.12.0", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } -shuttle-runtime = "0.41.0" -shuttle-secrets = "0.41.0" -shuttle-serenity = "0.41.0" -shuttle-shared-db = { version = "0.41.0", features = ["postgres", "sqlx"] } +shuttle-runtime = "0.42.0" +shuttle-serenity = "0.42.0" +shuttle-shared-db = { version = "0.42.0", features = ["postgres", "sqlx"] } sqlx = "0.7.1" tokio = "1.26.0" tracing = "0.1.37" diff --git a/examples/serenity.mdx b/examples/serenity.mdx index e462997..58d720d 100644 --- a/examples/serenity.mdx +++ b/examples/serenity.mdx @@ -30,12 +30,12 @@ This example shows how to build a Serenity bot with Shuttle that responds to the ```rust src/main.rs -use anyhow::anyhow; +use anyhow::Context as _; use serenity::async_trait; use serenity::model::channel::Message; use serenity::model::gateway::Ready; use serenity::prelude::*; -use shuttle_secrets::SecretStore; +use shuttle_runtime::SecretStore; use tracing::{error, info}; struct Bot; @@ -57,14 +57,10 @@ impl EventHandler for Bot { #[shuttle_runtime::main] async fn serenity( - #[shuttle_secrets::Secrets] secret_store: SecretStore, + #[shuttle_runtime::Secrets] secrets: SecretStore, ) -> shuttle_serenity::ShuttleSerenity { // Get the discord token set in `Secrets.toml` - let token = if let Some(token) = secret_store.get("DISCORD_TOKEN") { - token - } else { - return Err(anyhow!("'DISCORD_TOKEN' was not found").into()); - }; + let token = secrets.get("DISCORD_TOKEN").context("'DISCORD_TOKEN' was not found")?; // Set gateway intents, which decides what events the bot will be notified about let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT; @@ -91,9 +87,8 @@ edition = "2021" [dependencies] anyhow = "1.0.66" serenity = { version = "0.12.0", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } -shuttle-runtime = "0.41.0" -shuttle-secrets = "0.41.0" -shuttle-serenity = "0.41.0" +shuttle-runtime = "0.42.0" +shuttle-serenity = "0.42.0" tokio = "1.26.0" tracing = "0.1.37" ``` diff --git a/getting-started/local-run.mdx b/getting-started/local-run.mdx index 6e16073..43a3b54 100644 --- a/getting-started/local-run.mdx +++ b/getting-started/local-run.mdx @@ -101,3 +101,41 @@ if cfg!(debug_assertions) { router = router.layer(tower_livereload::LiveReloadLayer::new()); } ``` + +### Docker engines + +`cargo-shuttle` uses the [bollard](https://crates.io/crates/bollard) crate to interact with the Docker engine on local runs. + +If you are using a non-standard Docker engine, you might get this error: + +```text +got unexpected error while inspecting docker container: +error trying to connect: No such file or directory +``` + +The error is emitted due to bollard not connecting to the correct Docker Socket location. + +On Unix, bollard defaults to connecting to `unix:///var/run/docker.sock` unless the `DOCKER_HOST` env variable overrides it. + +If you end up using a `DOCKER_HOST` like below, you can add the `export DOCKER_HOST=...` line to your shell's config file to have it automatically set in new shell sessions. + +#### Docker Desktop + +```bash +export DOCKER_HOST="unix://$HOME/.docker/run/docker.sock" +``` + +#### Podman + +You will need to expose a rootless socket for Podman, and then set the `DOCKER_HOST` environment variable: + +```bash +podman system service --time=0 unix:///tmp/podman.sock +export DOCKER_HOST=unix:///tmp/podman.sock +``` + +#### Colima + +```bash +export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock" +``` diff --git a/getting-started/shuttle-commands.mdx b/getting-started/shuttle-commands.mdx index 79b50f5..8648d60 100644 --- a/getting-started/shuttle-commands.mdx +++ b/getting-started/shuttle-commands.mdx @@ -85,6 +85,7 @@ All project-related commands can use: - `cargo shuttle logs --latest`: Get all logs from the currently running deployment. - `cargo shuttle logs `: Get all logs from a specific deployment. - `cargo shuttle logs --follow`: Stream logs of the deployed service and stream them to your terminal. +- `cargo shuttle logs --raw`: Print the logs without timestamps and origin tags. The `--raw` flag is also available for the `run` and `deploy` commands. ### Manage resources diff --git a/introduction/how-shuttle-works.mdx b/introduction/how-shuttle-works.mdx index 9dbc101..dcc4b9c 100644 --- a/introduction/how-shuttle-works.mdx +++ b/introduction/how-shuttle-works.mdx @@ -38,14 +38,10 @@ Here are the examples on how to use them, once you add the required annotations ```rust #[shuttle_runtime::main] async fn axum( - #[shuttle_secrets::Secrets] secret_store: SecretStore, + #[shuttle_runtime::Secrets] secrets: SecretStore, ) -> shuttle_axum::ShuttleAxum { // Get secret defined in `Secrets.toml` file. - let secret = if let Some(secret) = secret_store.get("MY_API_KEY") { - secret - } else { - return Err(anyhow!("secret was not found").into()); - }; + let secret = secrets.get("MY_API_KEY").expect("secret was not found"); } ``` diff --git a/migration/migrating-to-shuttle.mdx b/migration/migrating-to-shuttle.mdx index ab6a36b..1586166 100644 --- a/migration/migrating-to-shuttle.mdx +++ b/migration/migrating-to-shuttle.mdx @@ -18,13 +18,7 @@ The first step is to add `shuttle-runtime` and `shuttle-axum` to your dependenci cargo add shuttle-runtime shuttle-axum ``` -Any [secrets](/resources/shuttle-secrets) you need to use will be kept in a `Secrets.toml` file (dev secrets to be held in `Secrets.dev.toml`) which will be placed at the `Cargo.toml` level - you'll want to use the `shuttle-secrets` crate to be able to use the new secrets: - -```bash -cargo add shuttle-secrets -``` - -Your secrets file will look the same as your previous .env file, so you won't need to change anything. +Any [secrets](/resources/shuttle-secrets) you need to use will be kept in a `Secrets.toml` file (dev secrets in `Secrets.dev.toml`) which will be placed at the `Cargo.toml` level. You can also easily get a provisioned database like so (this example will be for a provisioned PostgreSQL instance specifically): @@ -33,14 +27,14 @@ cargo add shuttle-shared-db --features postgres ``` If you have any database records you'd like to keep, it would be a good idea to export them so that they can be migrated to the new database. -**You will not need a secrets file if you only need a provisioned Postgres database - this will be automatically be provisioned to you in the form of a SQLx connection.** +**You will not need a secrets file if you only need a provisioned Postgres database - this will be automatically be provisioned and given to you in the form of a connection string or an sqlx pool.** ## Migrating your Code To be able to run your project on Shuttle, you need to make a few changes to your code. Instead of the `tokio::main` macro, you will use the `shuttle_runtime::main` macro and swap out `dotenvy` for Shuttle's Postgres annotation: -This is what your `main.rs` file looks before: +This is what your `main.rs` file looks like before: ```rust main.rs #[tokio::main] @@ -72,7 +66,7 @@ And this is what it looks like after: #[shuttle_runtime::main] pub async fn axum ( #[shuttle_shared_db::Postgres] pool: PgPool, - #[shuttle_secrets::Secrets] secrets: shuttle_secrets::SecretStore, + #[shuttle_runtime::Secrets] secrets: shuttle_runtime::SecretStore, ) -> shuttle_axum::ShuttleAxum { sqlx::migrate!() .run(&pool) diff --git a/resources/custom-resources.mdx b/resources/custom-resources.mdx index d785674..69b1c65 100644 --- a/resources/custom-resources.mdx +++ b/resources/custom-resources.mdx @@ -91,9 +91,9 @@ edition = "2021" async-trait = "0.1.56" axum = "0.7.3" serde = { version = "1.0.148", default-features = false, features = ["derive"] } -shuttle-service = "0.41.0" -shuttle-axum = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-service = "0.42.0" +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.28.2" ``` diff --git a/resources/overview.mdx b/resources/overview.mdx index 704c41b..05ef412 100644 --- a/resources/overview.mdx +++ b/resources/overview.mdx @@ -3,7 +3,7 @@ title: "Overview" icon: "face-monocle" --- -The next sections go over one by one the resources currently supported by Shuttle. In broad terms, for the resource needed, simply mark up your code with the appropriate annotation. This is very powerful and brings several benefits: +This section covers the resources currently supported by Shuttle. In broad terms, for the resource needed, simply mark up your code with the appropriate annotation. This is very powerful and brings several benefits: - simplicity - receive a database by writing a simple annotation diff --git a/resources/shuttle-metadata.mdx b/resources/shuttle-metadata.mdx index 93c8078..61481b5 100644 --- a/resources/shuttle-metadata.mdx +++ b/resources/shuttle-metadata.mdx @@ -7,21 +7,17 @@ The metadata plugin allows applications to obtain certain information about thei ## Usage -To use the metadata plugin, add `shuttle-metadata` to the dependencies of your project by manually editing the project's `Cargo.toml` file or by invoking `cargo add shuttle-metadata` from the command line. - -Once the metadata plugin dependency is available to your project, use the resource by annotating your main function with the `shuttle_metadata::ShuttleMetadata` attribute. +Use the resource by annotating your main function with the `shuttle_runtime::ShuttleMetadata` attribute. ## Example -This snippet shows an Axum main function, annotated with the `shuttle_metadata::ShuttleMetadata` attribute. - ```rust main.rs use axum::{routing::get, Router}; -use shuttle_metadata::Metadata; +use shuttle_runtime::DeploymentMetadata; #[shuttle_runtime::main] async fn axum( - #[shuttle_metadata::ShuttleMetadata] metadata: Metadata, + #[shuttle_runtime::Metadata] metadata: DeploymentMetadata, ) -> shuttle_axum::ShuttleAxum { let router = Router::new().route("/", get(format!("{:?}", metadata))); @@ -29,10 +25,10 @@ async fn axum( } ``` -This example has one route which returns the debug print of the `Metadata` struct: +This example has one route which returns the debug print of the `DeploymentMetadata` struct: ```text -DeploymentMetadata { env: Local, project_name: ProjectName("metadata-axum-app"), service_name: "metadata-axum-app", storage_path: ".shuttle-storage" } +DeploymentMetadata { env: Local, project_name: "metadata-axum-app", storage_path: ".shuttle-storage" } ``` The full example can also be found on [GitHub](https://github.com/shuttle-hq/shuttle-examples/tree/main/axum/metadata). diff --git a/resources/shuttle-secrets.mdx b/resources/shuttle-secrets.mdx index 9854afe..3e935f9 100644 --- a/resources/shuttle-secrets.mdx +++ b/resources/shuttle-secrets.mdx @@ -7,8 +7,9 @@ icon: "user-secret" This plugin manages secrets on [Shuttle](https://www.shuttle.rs). ## Usage -Add `shuttle-secrets` to the dependencies for your service, and add a `Secrets.toml` to the root of your project -with the secrets you'd like to store. Make sure to add `Secrets*.toml` to a `.gitignore` to omit your secrets from version control. + +Add a `Secrets.toml` to the crate root or workspace root of your Shuttle service with the secrets you'd like to store. +Make sure to add `Secrets*.toml` to a `.gitignore` to omit your secrets from version control. The format of the Secrets.toml file is a key-value mapping with string values. @@ -17,31 +18,35 @@ MY_API_KEY = 'the contents of my API key' MY_OTHER_SECRET = 'some other secret' ``` -Next, pass `#[shuttle_secrets::Secrets] secret_store: SecretStore` as an argument to your `shuttle_runtime::main` function. +Next, pass `#[shuttle_runtime::Secrets] secrets: shuttle_runtime::SecretStore` as an argument to your `shuttle_runtime::main` function. `SecretStore::get` can now be called to retrieve your API keys and other secrets at runtime. ## Local secrets -When developing locally with `cargo shuttle run`, you can use a different set of secrets. If you add a `Secrets.dev.toml` to the -root of your project, these secrets will be used for local runs. The `Secrets.dev.toml` file will *only* be used for local runs. -If you don't have a `Secrets.dev.toml` file, `Secrets.toml` will be used locally as well as for deployments. If you want to have -both secret files with some of the same secrets for both local runs and deployments, you have to duplicate the secret across both -files. +When developing locally with `cargo shuttle run`, you can use a different set of secrets by adding a `Secrets.dev.toml` file. + +If you don't have a `Secrets.dev.toml` file, `Secrets.toml` will be used locally as well as for deployments. +If you want to have both secret files with some of the same secrets for both local runs and deployments, you have to duplicate the secret across both files. + +## Different secrets file + +You can also use other secrets files (in TOML format) by using the `--secrets [file]` argument on the `run` and `deploy` commands. + +When deploying with the `--secrets` arg, that file is renamed to `Secrets.toml` before being packed and sent to Shuttle. Therefore, it has to be in the same folder that a normal `Secrets.toml` file would have been placed. ## Example -This snippet shows a Shuttle rocket main function that uses the `shuttle_secrets::Secrets` attribute to gain access to a `SecretStore`. + +This snippet shows a Shuttle rocket main function that uses the `shuttle_runtime::Secrets` attribute to gain access to a `SecretStore`. ```rust main.rs +use shuttle_runtime::SecretStore; + #[shuttle_runtime::main] async fn rocket( - #[shuttle_secrets::Secrets] secret_store: SecretStore, -) -> ShuttleRocket { + #[shuttle_runtime::Secrets] secrets: SecretStore, +) -> shuttle_rocket::ShuttleRocket { // get secret defined in `Secrets.toml` file. - let secret = if let Some(secret) = secret_store.get("MY_API_KEY") { - secret - } else { - return Err(anyhow!("secret was not found").into()); - }; + let secret = secrets.get("MY_API_KEY").context("secret was not found")?; let state = MyState { secret }; let rocket = rocket::build().mount("/", routes![secret]).manage(state); diff --git a/resources/shuttle-shared-db.mdx b/resources/shuttle-shared-db.mdx index fa920b4..e02b335 100644 --- a/resources/shuttle-shared-db.mdx +++ b/resources/shuttle-shared-db.mdx @@ -31,6 +31,9 @@ Depending on which type declaration is used as the output type in the macro, you |----------|--------------|------------------|-------------| | Postgres | | `String` | The connection string including username and password | | Postgres | `sqlx` (with rustls) or `sqlx-native-tls` | `sqlx::PgPool` | An sqlx connection pool with reasonable defaults | +| Postgres | `diesel-async` | `diesel_async::AsyncPgConnection` | An async diesel connection with reasonable defaults | +| Postgres | `diesel-async-bb8` | `diesel_bb8::Pool` | A bb8 connection pool with reasonable defaults | +| Postgres | `diesel-async-deadpool` | `diesel_deadpool::Pool` | A deadpool connection pool with reasonable defaults | | MongoDB | | `String` | The connection string including username and password | | MongoDB | | `mongodb::Database` | A client pool connected to the database | diff --git a/support/troubleshooting.mdx b/support/troubleshooting.mdx index 1b2a736..73028db 100644 --- a/support/troubleshooting.mdx +++ b/support/troubleshooting.mdx @@ -7,8 +7,7 @@ This section is aimed at collecting common issues users have to provide quick de - If you've recently updated Shuttle, in order to get your project back up, you need to run: - `cargo shuttle project restart` and everything should be back in working order. + Make sure to follow all of the [upgrading steps](/configuration/shuttle-versions#upgrading-shuttle-version). CustomError is an alias for `anyhow::Error`, the simplest way to output a relevant message here is by using `anyhow::Context` like this: @@ -21,15 +20,8 @@ This section is aimed at collecting common issues users have to provide quick de .context("failed to run migrations")?; ``` - - Add `shuttle-secrets` to the dependencies for your service, and add a `Secrets.toml` to the root of your project with the secrets you'd like to store. Make sure to add `Secrets*.toml` to your `.gitignore` to omit your secrets from version control. - - Next, pass `#[shuttle_secrets::Secrets] secret_store: SecretStore` as an argument to your `shuttle_runtime::main` function. `SecretStore::get` can now be called to retrieve your API keys and other secrets at runtime. - - For more details, and an explanation on how to use local secrets, head on over here: [Shuttle secrets](../resources/shuttle-secrets). - ### Other issues? -Hop on over to [our Discord server](https://github.com/shuttle-hq/shuttle), we are very responsive! +Hop on over to [our Discord server](https://discord.gg/shuttle), we are very responsive! diff --git a/templates/tutorials/custom-service.mdx b/templates/tutorials/custom-service.mdx index ca71dfa..ffec285 100644 --- a/templates/tutorials/custom-service.mdx +++ b/templates/tutorials/custom-service.mdx @@ -26,12 +26,13 @@ To add the bot to a server, we need to create an invite link: ### Initial Setup + Start by running the following command: ```bash cargo shuttle init --template none ``` -This will simply initialize a new cargo crate with a dependency on `shuttle-service`. +This will simply initialize a new cargo crate with a dependency on `shuttle-runtime`. We also want to add several dependencies for this - make sure your Cargo.toml looks like below: @@ -47,14 +48,12 @@ axum = "0.6.4" hyper = "0.14.24" poise = "0.5.2" serde = "1.0" -shuttle-runtime = "0.41.0" -shuttle-secrets = "0.41.0" +shuttle-runtime = "0.42.0" tokio = "1.26.0" ``` ### Getting Started -To get started, we need to return a wrapper struct from our `shuttle_runtime::main` function that -implements `shuttle_service::Service`, which we can declare like so: +To get started, we need to return a wrapper struct from our `shuttle_runtime::main`. ```rust main.rs pub struct CustomService { @@ -77,7 +76,7 @@ impl shuttle_runtime::Service for CustomService { mut self, addr: std::net::SocketAddr, ) -> Result<(), shuttle_runtime::Error> { - + let router = self.router.into_inner(); let serve_router = axum::Server::bind(&addr).serve(router.into_make_service()); @@ -135,10 +134,11 @@ To finish up, we return the wrapper struct from our `shuttle_runtime::main` func for setting up each of our services for the struct, like so: ```rust main.rs -use poise::serenity_prelude as serenity; -use shuttle_secrets::SecretStore; use std::sync::Arc; +use poise::serenity_prelude as serenity; +use shuttle_runtime::SecretStore; + mod commands; use commands::age; @@ -155,7 +155,7 @@ pub struct CustomService { #[shuttle_runtime::main] async fn init( - #[shuttle_secrets::Secrets] secrets: SecretStore, + #[shuttle_runtime::Secrets] secrets: SecretStore, ) -> Result { let discord_api_key = secrets.get("DISCORD_API_KEY").unwrap(); diff --git a/templates/tutorials/discord-weather-forecast.mdx b/templates/tutorials/discord-weather-forecast.mdx index 16672dd..97a1ca4 100644 --- a/templates/tutorials/discord-weather-forecast.mdx +++ b/templates/tutorials/discord-weather-forecast.mdx @@ -76,12 +76,12 @@ cargo shuttle init --template serenity After running it you, should see the following generated in `src/main.rs`: ```rust src/main.rs -use anyhow::anyhow; +use anyhow::Context as _; use serenity::async_trait; use serenity::model::channel::Message; use serenity::model::gateway::Ready; use serenity::prelude::*; -use shuttle_secrets::SecretStore; +use shuttle_runtime::SecretStore; use tracing::{error, info}; struct Bot; @@ -103,14 +103,10 @@ impl EventHandler for Bot { #[shuttle_runtime::main] async fn serenity( - #[shuttle_secrets::Secrets] secret_store: SecretStore, + #[shuttle_runtime::Secrets] secrets: SecretStore, ) -> shuttle_serenity::ShuttleSerenity { // Get the discord token set in `Secrets.toml` - let token = if let Some(token) = secret_store.get("DISCORD_TOKEN") { - token - } else { - return Err(anyhow!("'DISCORD_TOKEN' was not found").into()); - }; + let token = secrets.get("DISCORD_TOKEN").context("'DISCORD_TOKEN' was not found")?; // Set gateway intents, which decides what events the bot will be notified about let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT; @@ -146,7 +142,7 @@ Now that we have the information for setup, we can start writing our bot and its commands. We will first get rid of the `async fn message` hook as we won't be using it in -this example, and then configure the gateway intents to not use any, as we won't be needing them. +this example, and then configure the gateway intents to not use any, as we won't be needing them. ```rust src/main.rs // Set gateway intents, which decides what events the bot will be notified about. @@ -167,7 +163,7 @@ impl EventHandler for Bot { // We are going to move the guild ID to the Secrets.toml file later. let guild_id = GuildId::new(*your guild id*); - // We are creating a vector with commands + // We are creating a vector with commands // and registering them on the server with the guild ID we have set. let commands = vec![CreateCommand::new("hello").description("Say hello")]; let commands = guild_id.set_commands(&ctx.http, commands).await.unwrap(); @@ -218,25 +214,12 @@ Shuttle we create a `Secrets.toml` file with a key value pair: ```toml Secrets.toml DISCORD_TOKEN="your discord token" ``` -This pair is read by the `secret_store.get("DISCORD_TOKEN")` call in the `serenity` function: - -```rust src/main.rs - -#[shuttle_runtime::main] -async fn serenity( - // ... - let token = if let Some(token) = secret_store.get("DISCORD_TOKEN") { - token - } else { - return Err(anyhow!("'DISCORD_TOKEN' was not found").into()); - }; - // ... -} -``` Now we can run our bot and test the hello command: -`cargo shuttle run` +```bash +cargo shuttle run +``` We should see that our bot now displays as online: @@ -417,7 +400,7 @@ struct Bot { #[shuttle_runtime::main] async fn serenity( - #[shuttle_secrets::Secrets] secret_store: SecretStore, + #[shuttle_runtime::Secrets] secret_store: SecretStore, ) -> shuttle_serenity::ShuttleSerenity { // Get the discord token set in `Secrets.toml` let discord_token = secret_store @@ -467,31 +450,30 @@ We will add our new command with a place option/parameter. Back in the `ready` hook, we can add an additional command alongside the existing `hello` command: ```rust src/main.rs -// ready() should look like this: async fn ready(&self, ctx: Context, ready: Ready) { - info!("{} is connected!", ready.user.name); - - let commands = vec![ - CreateCommand::new("hello").description("Say hello"), - CreateCommand::new("weather") - .description("Display the weather") - .add_option( - CreateCommandOption::new( - serenity::all::CommandOptionType::String, - "place", - "City to lookup forecast", - ) - .required(true) - ), - ]; - - let commands = &self - .discord_guild_id - .set_commands(&ctx.http, commands) - .await - .unwrap(); + info!("{} is connected!", ready.user.name); + + let commands = vec![ + CreateCommand::new("hello").description("Say hello"), + CreateCommand::new("weather") + .description("Display the weather") + .add_option( + CreateCommandOption::new( + serenity::all::CommandOptionType::String, + "place", + "City to lookup forecast", + ) + .required(true) + ), + ]; + + let commands = &self + .discord_guild_id + .set_commands(&ctx.http, commands) + .await + .unwrap(); - info!("Registered commands: {:#?}", commands); + info!("Registered commands: {:#?}", commands); } ``` @@ -545,7 +527,7 @@ DISCORD_GUILD_ID = "***" WEATHER_API_KEY = "***" ``` -With the secrets added, we can run the server: +With the secrets added, we can run the bot: ```bash cargo shuttle run diff --git a/templates/tutorials/send-your-logs-to-datadog.mdx b/templates/tutorials/send-your-logs-to-datadog.mdx index c038b66..803846b 100644 --- a/templates/tutorials/send-your-logs-to-datadog.mdx +++ b/templates/tutorials/send-your-logs-to-datadog.mdx @@ -76,7 +76,7 @@ So, the basic idea is to add a new [tracing layer](https://docs.rs/tracing-subsc But for that, we'll need to get **access to the subscriber instance prior to its initialization**, and it turns out that [Shuttle](https://www.shuttle.rs/) provides a way to do that just by disabling the default features on `shuttle-runtime` crate. ```toml -shuttle-runtime = { version = "*" default-features = false } +shuttle-runtime = { version = "*", default-features = false } ``` ## Creating our project @@ -115,8 +115,6 @@ tokio = "1" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "time"] } dd-tracing-layer = "0.1" -# secrets -shuttle-secrets = "0.27.0" ``` ### Instrumenting a little bit the default project @@ -166,7 +164,7 @@ Let's do it! Make sure your `axum` function looks like this: ```rust use axum::{routing::get, Router}; use dd_tracing_layer::{DatadogOptions, Region}; -use shuttle_secrets::SecretStore; +use shuttle_runtime::SecretStore; use tracing::instrument; use tracing_subscriber::prelude::*; @@ -176,7 +174,7 @@ const VERSION: &'static str = "version:0.1.0"; // [...] #[shuttle_runtime::main] -async fn axum(#[shuttle_secrets::Secrets] secret_store: SecretStore) -> shuttle_axum::ShuttleAxum { +async fn axum(#[shuttle_runtime::Secrets] secret_store: SecretStore) -> shuttle_axum::ShuttleAxum { // getting the Datadog Key from the secrets let dd_api_key = secret_store .get("DD_API_KEY") @@ -244,18 +242,14 @@ As you can see in the code above, we are using the [Shuttle Secrets](https://doc So, let's create two files in the root of our project: -`Secrets.dev.toml` - -```toml +```toml Secrets.dev.toml DD_API_KEY = "your-datadog-api-key" DD_TAGS = "env:dev,service:shuttle-datadog-logs" # setting info as the default log level, but debug for our project LOG_LEVEL = "INFO,shuttle_datadog_logs=DEBUG" ``` -and `Secrets.toml` - -```toml +```toml Secrets.toml DD_API_KEY = "your-datadog-api-key" DD_TAGS = "env:prod,service:shuttle-datadog-logs" LOG_LEVEL = "INFO" diff --git a/templates/tutorials/url-shortener.mdx b/templates/tutorials/url-shortener.mdx index 445b86c..ed70ba2 100644 --- a/templates/tutorials/url-shortener.mdx +++ b/templates/tutorials/url-shortener.mdx @@ -129,8 +129,8 @@ edition = "2021" [dependencies] rocket = "0.5.0" -shuttle-rocket = "0.41.0" -shuttle-runtime = "0.41.0" +shuttle-rocket = "0.42.0" +shuttle-runtime = "0.42.0" tokio = "1.26.0" ``` @@ -152,8 +152,8 @@ $ cargo shuttle deploy Compiling rocket_http v0.5.0 Compiling rocket_codegen v0.5.0 Compiling rocket v0.5.0 - Compiling shuttle-rocket v0.41.0 - Compiling shuttle-runtime v0.41.0 + Compiling shuttle-rocket v0.42.0 + Compiling shuttle-runtime v0.42.0 Compiling url-shortener v0.1.0 (/opt/shuttle/crates/url-shortener) Finished dev [unoptimized + debuginfo] target(s) in 1m 01s diff --git a/templates/tutorials/websocket-chat-app-js.mdx b/templates/tutorials/websocket-chat-app-js.mdx index c4f293c..7a9674c 100644 --- a/templates/tutorials/websocket-chat-app-js.mdx +++ b/templates/tutorials/websocket-chat-app-js.mdx @@ -149,7 +149,7 @@ Now that the main design of the app is done, let's think about how we can implem ```js // App.tsx -const websocket = new WebSocket(ws://localhost:8000/ws); +const websocket = new WebSocket("ws://localhost:8000/ws"); ``` This opens a WebSocket connection at `localhost:8000/ws`. Not particularly useful at the moment because we currently don't have anything we can connect it to, but we'll need this for testing later on. @@ -257,10 +257,8 @@ hyper = { version = "0.14", features = ["client", "http2"] } hyper-tls = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -shuttle-axum = { version = "0.41.0" } -shuttle-runtime = { version = "0.41.0" } -shuttle-secrets = "0.41.0" -shuttle-static-folder = "0.41.0" +shuttle-axum = "0.42.0" +shuttle-runtime = "0.42.0" sync_wrapper = "0.1" tokio = { version = "1", features = ["full"] } tower-http = { version = "0.3.5", features = ["fs", "auth"]} @@ -425,8 +423,7 @@ Before we get started however, let's quickly update our main function so that we ```rust #[shuttle_runtime::main] async fn axum( - #[shuttle_secrets::Secrets] secrets: SecretStore, - #[shuttle_static_folder::StaticFolder] static_folder: PathBuf + #[shuttle_runtime::Secrets] secrets: SecretStore, ) -> ShuttleAxum { // We use Secrets.toml to set the BEARER key, just like in a .env file and call it here let secret = secrets.get("BEARER").unwrap_or("Bear".to_string()); @@ -438,7 +435,7 @@ async fn axum( } ``` -Let's dissect our new additions quickly. `shuttle_secrets::Secrets` allows us to set environmental variables using a Secrets.toml file, much like how you'd typically use a .env file to be able to use environment variables locally and in production. `shuttle_static_folder::StaticFolder` provides a path to our static assets folder so that when we deploy to Shuttle, the deployment will know where to find our static assets (although we won't implement that just yet). +Let's dissect our new additions quickly. `shuttle_runtime::Secrets` allows us to set environmental variables using a Secrets.toml file, much like how you'd typically use a .env file to be able to use environment variables locally and in production. Now that our setup for this section is out of the way, let's cover the admin route first as that'll mean we can make sure our WebSocket service is complete before we compile any assets. Let's make a function that will take a user ID and manually disconnect them using the disconnect function we already have: @@ -471,7 +468,7 @@ Now that we have this route, we can actually embed it into our main router using .layer(Extension(users)) ``` -As you can see here, our admin route should actually be `/ws/admin`, as dictated by the nest function. Now if we want to disconnect a user with the user ID of 5 manually for example, we would have to make a post request to `/ws/admin/disconnect/5` with a Bearer authentication header - as an example, if you've set your secret as "keyboard cat", you need to enter an authentication header of `Bearer keyboard cat`). +As you can see here, our admin route should actually be `/ws/admin`, as dictated by the nest function. Now if we want to disconnect a user with the user ID of 5 manually for example, we would have to make a post request to `/ws/admin/disconnect/5` with a Bearer authentication header - as an example, if you've set your secret as "keyboard cat", you need to enter an authentication header of `Bearer keyboard cat`. At this point, your router function should look like this (if not, you have likely missed a step somewhere): @@ -488,7 +485,7 @@ At this point, your router function should look like this (if not, you have like Router::new() .route("/ws", get(ws_handler)) .nest("/admin", admin) - .fallback_service("/", ServeDir::new(static_folder).not_found_service(ServeFile::new(static_folder.join("/index.html")))) + .fallback_service("/", ServeDir::new("assets").not_found_service(ServeFile::new("assets/index.html"))) .layer(Extension(users)) } ``` @@ -542,19 +539,18 @@ Now we can update our main function and router together so that the router will ```rust #[shuttle_runtime::main] async fn axum( - #[shuttle_secrets::Secrets] secrets: SecretStore, - #[shuttle_static_folder::StaticFolder] static_folder: PathBuf + #[shuttle_runtime::Secrets] secrets: SecretStore, ) -> ShuttleAxum { // We use Secrets.toml to set the BEARER key, just like in a .env file and call it here let secret = secrets.get("BEARER").unwrap_or("Bear".to_string()); // set up router with Secrets - let router = router(secret, static_folder); + let router = router(secret); Ok(router.into()) } -fn router(secret: String, static_folder: PathBuf) -> Router { +fn router(secret: String) -> Router { // initialise the Users k/v store and allow the static files to be served let users = Users::default(); @@ -567,7 +563,7 @@ fn router(secret: String, static_folder: PathBuf) -> Router { Router::new() .route("/ws", get(ws_handler)) .nest("/admin", admin) - .fallback_service("/", ServeDir::new(static_folder).not_found_service(ServeFile::new(static_folder.join("/index.html")))) + .fallback_service("/", ServeDir::new("assets").not_found_service(ServeFile::new("assets/index.html"))) .layer(Extension(users)) } ``` @@ -578,7 +574,7 @@ Now we're pretty much done and ready to deploy! We can call our deploy script at ```rust "scripts": { // ...your other scripts - "dev": "concurrently new \"vite\" \"cargo shuttle run --working-directory .API\"", + "dev": "concurrently new \"vite\" \"cargo shuttle run --working-directory ./API\"", "build": "tsc && vite build --emptyOutDir", "deploy": "npm run build && cargo shuttle deploy --working-directory ./API" // ...your other scripts