diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 00000000..a33a8794 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,13 @@ +You are an expert in Bash scripting, Docker, and Ansible. You possess deep knowledge of best practices and performance optimizations techniques for writing Bash and Ansible code. + +The project you're working on is called Spin, which is a tool that helps people create new projects, create infrastructure, and maintain existing infrastructure using Docker Compose, Docker Swarm, Ansible, and more. + +Code Style and Structure +- Write clean, maintainable and technically accurate code. +- All bash must be POSIX compliant. +- All bash must be compatible with Linux, WSL2, and MacOS (Bash v3) +- Never use an approach you're not confident about. If you're unsure about something, ask for clarity. +- Always follow best practices for Bash, Ansible, and Docker. +- This project should work on Linux, WSL2, and MacOS without installing any additional dependencies other than Docker. + +This project is open source and the code is available on GitHub, so be sure to follow best practices to make it easy for others to understand, modify, and contribute to the project. diff --git a/README.md b/README.md index 3412e810..f46dacff 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ Spin serves as a collection of open source technologies, put together in one sim - [serversideup/docker-swarm-deploy-github-action](https://github.com/marketplace/actions/docker-swarm-deploy-github-action) - A simplified syntax to deploy to Docker Swarm Mode via GitHub Actions. - [serversideup/php](https://serversideup.net/open-source/docker-php/) - PHP Docker images highly optimized to work with Laravel + Spin. - [serversideup/docker-ssh](https://github.com/serversideup/docker-ssh) - A lightweight docker image that runs SSH. This is a fantastic method on using a secure SSH tunnel into your database cluster. +- [serversideup/docker-ansible](https://github.com/serversideup/docker-ansible) - A lightweight docker image that runs Ansible. +- [serversideup/docker-github-cli](https://github.com/serversideup/docker-github-cli) - A lightweight docker image that runs GitHub CLI. ## Resources - **[Website](https://serversideup.net/open-source/spin/)** overview of the product. diff --git a/bin/spin b/bin/spin index 73ec755a..543a1bf1 100755 --- a/bin/spin +++ b/bin/spin @@ -3,6 +3,11 @@ set -e # Default Environment SPIN_ENV=${SPIN_ENV:-dev} +SPIN_DEBUG=${SPIN_DEBUG:-false} + +if [[ "$SPIN_DEBUG" == "true" ]]; then + set -x +fi # Set up our structure for our re-used commands export COMPOSE_CMD=${COMPOSE_CMD:-"docker compose"} @@ -16,17 +21,21 @@ export SPIN_GROUP_ID # Default Images SPIN_PHP_IMAGE=${SPIN_PHP_IMAGE:-"serversideup/php:cli"} SPIN_NODE_IMAGE=${SPIN_NODE_IMAGE:-"node:20"} -SPIN_ANSIBLE_IMAGE=${SPIN_ANSIBLE_IMAGE:-"docker.io/serversideup/ansible-core:2.17-alpine"} -SPIN_ANSIBLE_COLLECTION_NAME=${SPIN_ANSIBLE_COLLECTION_NAME:-"serversideup.spin"} +SPIN_ANSIBLE_IMAGE=${SPIN_ANSIBLE_IMAGE:-"docker.io/serversideup/ansible-core:2.18-alpine"} +SPIN_GH_CLI_IMAGE=${SPIN_GH_CLI_IMAGE:-"docker.io/serversideup/github-cli:alpine"} # Script Configuration SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" SPIN_HOME=$(dirname "$SCRIPT_DIR") #Assume the parent directory of this script is the home SPIN_CACHE_DIR=${SPIN_CACHE_DIR:-$SPIN_HOME/cache} SPIN_CONFIG_FILE_LOCATION=${SPIN_CONFIG_FILE_LOCATION:-"$SPIN_HOME/conf/spin.conf"} +SPIN_CI_FOLDER=${SPIN_CI_FOLDER:-".infrastructure/conf/ci"} AUTO_UPDATE_INTERVAL_IN_DAYS=${AUTO_UPDATE_INTERVAL_IN_DAYS:-14} AUTO_PULL_INTERVAL_IN_DAYS=${AUTO_PULL_INTERVAL_IN_DAYS:-1} +# Ansible Variables +SPIN_ANSIBLE_COLLECTION_NAME="${SPIN_ANSIBLE_COLLECTION_NAME:-"serversideup.spin"}" + # Import common functions source "$SPIN_HOME/lib/functions.sh" setup_color @@ -44,7 +53,7 @@ case "$1" in "" | base64 | debug | help | update | version | --version | -v) : # Silent output for the user, but we're skipping the compose check too. ;; - init | kill | mkpasswd | new | prune | provision | vault) + configure | gh | init | kill | maintain | mkpasswd | new | prune | provision | vault) check_if_docker_is_running ;; *) @@ -67,6 +76,10 @@ if [ $# -gt 0 ]; then source "$SPIN_HOME/lib/actions/build.sh" action_build "$@" ;; + configure) + source "$SPIN_HOME/lib/actions/configure.sh" + action_configure "$@" + ;; debug) source "$SPIN_HOME/lib/actions/debug.sh" action_debug "$@" @@ -87,6 +100,10 @@ if [ $# -gt 0 ]; then source "$SPIN_HOME/lib/actions/help.sh" action_help ;; + gh) + source "$SPIN_HOME/lib/actions/gh.sh" + action_gh "$@" + ;; init) source "$SPIN_HOME/lib/actions/init.sh" action_init "$@" @@ -103,6 +120,10 @@ if [ $# -gt 0 ]; then source "$SPIN_HOME/lib/actions/logs.sh" action_logs "$@" ;; + maintain) + source "$SPIN_HOME/lib/actions/maintain.sh" + action_maintain "$@" + ;; mkpasswd) source "$SPIN_HOME/lib/actions/mkpasswd.sh" action_mkpasswd "$@" diff --git a/docs/components/Docs/Navigation.vue b/docs/components/Docs/Navigation.vue index 2621838b..b340afaf 100644 --- a/docs/components/Docs/Navigation.vue +++ b/docs/components/Docs/Navigation.vue @@ -1,5 +1,5 @@ @@ -53,4 +47,13 @@ const route = useRoute(); const { navigation, toc } = useContent(); + +onMounted(() => { + console.log(route.path); + const element = document.querySelector(`[data-attr-link-id="${route.path}"]`); + if (element) { + // Can we scroll to this element without using smooth? + element.scrollIntoView(); + } +}) \ No newline at end of file diff --git a/docs/components/Docs/NavigationGroup.vue b/docs/components/Docs/NavigationGroup.vue index 9fb56ed9..27d33b9a 100644 --- a/docs/components/Docs/NavigationGroup.vue +++ b/docs/components/Docs/NavigationGroup.vue @@ -15,6 +15,7 @@ }"> + + \ No newline at end of file diff --git a/docs/components/Global/MobileMenu.vue b/docs/components/Global/MobileMenu.vue index 03568481..421c74aa 100644 --- a/docs/components/Global/MobileMenu.vue +++ b/docs/components/Global/MobileMenu.vue @@ -13,6 +13,18 @@ Search + +
+ + + +
+ + Docs +
+ diff --git a/docs/components/content/HubMain.vue b/docs/components/content/HubMain.vue new file mode 100644 index 00000000..75d6e91e --- /dev/null +++ b/docs/components/content/HubMain.vue @@ -0,0 +1,82 @@ + + + \ No newline at end of file diff --git a/docs/components/content/MarketingHeader.vue b/docs/components/content/MarketingHeader.vue index 64d81b9b..934c6832 100644 --- a/docs/components/content/MarketingHeader.vue +++ b/docs/components/content/MarketingHeader.vue @@ -66,6 +66,18 @@ Docs + + + +
+ + + +
+ + Hub
diff --git a/docs/components/content/Search.vue b/docs/components/content/Search.vue index e46cb6f4..c6898e0b 100644 --- a/docs/components/content/Search.vue +++ b/docs/components/content/Search.vue @@ -106,17 +106,19 @@ import DocsIcon from './DocsIcon.vue'; import HeartIcon from './HeartIcon.vue'; import GitHubIcon from './GitHubIcon.vue'; +import hotkeys from 'hotkeys-js'; + /** * CMD + K shortcut for activating the modal */ const show = ref(false); -const { meta, k } = useMagicKeys(); -watchEffect(() => { - if (meta.value && k.value) { +if( import.meta.client ){ + hotkeys('ctrl+k,command+k', (event, handler) => { show.value = true; - } -}); + event.preventDefault(); + }); +} /** * Event handler for opening the modal diff --git a/docs/content/docs/1.index.md b/docs/content/docs/1.index.md index 36d387f2..369d031b 100644 --- a/docs/content/docs/1.index.md +++ b/docs/content/docs/1.index.md @@ -9,7 +9,7 @@ layout: docs

diff --git a/docs/content/docs/1.installation/1.install-macos.md b/docs/content/docs/1.installation/1.install-macos.md index 11785964..806a843a 100644 --- a/docs/content/docs/1.installation/1.install-macos.md +++ b/docs/content/docs/1.installation/1.install-macos.md @@ -3,7 +3,14 @@ head.title: 'Install to macOS - Spin by Server Side Up' title: 'Install to macOS' description: 'Learn how to install Spin on macOS. No root access required.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/installation/install-macos --- + +# Install to macOS +::lead-p +Spin easily runs on any macOS machine that runs Docker Desktop. Getting started is as simple as installing Docker Desktop, then running a single command to install `spin`. +:: + ## Install Docker Desktop MacOS does not ship with Docker by default. To get Docker installed, you will need "Docker Desktop", which is the official desktop tool developed by Docker. diff --git a/docs/content/docs/1.installation/2.install-windows.md b/docs/content/docs/1.installation/2.install-windows.md index 6d4761ec..4ebbe512 100644 --- a/docs/content/docs/1.installation/2.install-windows.md +++ b/docs/content/docs/1.installation/2.install-windows.md @@ -3,6 +3,7 @@ head.title: 'Install to Windows - Spin by Server Side Up' title: 'Install to Windows' description: 'Learn how to install Spin on Windows using the Linux Subsystem.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/installation/install-windows --- # Install to Windows diff --git a/docs/content/docs/1.installation/3.install-linux.md b/docs/content/docs/1.installation/3.install-linux.md index d3ed4cbb..54074dcd 100644 --- a/docs/content/docs/1.installation/3.install-linux.md +++ b/docs/content/docs/1.installation/3.install-linux.md @@ -3,8 +3,14 @@ head.title: 'Install to Linux - Spin by Server Side Up' title: 'Install to Linux' description: 'Learn how to install Spin on Linux.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/installation/install-linux --- +# Install to Linux +::lead-p +Spin is able to run on any Linux machine that supports Docker. Getting started is as simple as installing Docker, then running a single command to install `spin`. +:: + ## Prerequisites You must have a working installation of Docker. diff --git a/docs/content/docs/1.installation/4.install-composer.md b/docs/content/docs/1.installation/4.install-composer.md index 4bbaa451..29db57df 100644 --- a/docs/content/docs/1.installation/4.install-composer.md +++ b/docs/content/docs/1.installation/4.install-composer.md @@ -3,11 +3,17 @@ head.title: 'Install via Composer - Spin by Server Side Up' title: 'Install via Composer' description: 'Spin can be installed using Composer, a popular PHP package manager. This enables you to make Spin accessible without other developers doing any steps on their end.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/installation/install-composer --- -Installing `spin` at the project level is a great way to deploy `spin` without much configuration from the user's end. [View `spin` on Packagist →](https://packagist.org/packages/serversideup/spin) +# Install via Composer +::lead-p +Spin can be installed using Composer, a popular PHP package manager. This enables you to make Spin accessible without other developers doing any steps on their end. +:: ## Add `spin` to your project with Composer +Installing `spin` at the project level is a great way to deploy `spin` without much configuration from the user's end. [View `spin` on Packagist →](https://packagist.org/packages/serversideup/spin) + We can use Docker to run `composer` and install it on your project. **Run this command from the parent folder of your project.** ::code-panel diff --git a/docs/content/docs/1.installation/5.install-npm-yarn.md b/docs/content/docs/1.installation/5.install-npm-yarn.md index f47877ed..0e248cf8 100644 --- a/docs/content/docs/1.installation/5.install-npm-yarn.md +++ b/docs/content/docs/1.installation/5.install-npm-yarn.md @@ -3,11 +3,17 @@ head.title: 'Install via NPM/Yarn - Spin by Server Side Up' title: 'Install via NPM/Yarn' description: 'Spin can be installed using NPM or Yarn, two popular JavaScript package managers. This enables you to make Spin accessible without other developers doing any steps on their end.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/installation/install-npm-yarn --- -Installing `spin` at the project level is a great way to deploy `spin` without much configuration from the user's end. [View `spin` on NPM →](https://www.npmjs.com/package/@serversideup/spin) +# Install via NPM/Yarn +::lead-p +Spin can be installed using NPM or Yarn, two popular JavaScript package managers. This enables you to make Spin accessible without other developers doing any steps on their end. +:: ## Add `spin` to your project with Yarn/NPM +Installing `spin` at the project level is a great way to deploy `spin` without much configuration from the user's end. [View `spin` on NPM →](https://www.npmjs.com/package/@serversideup/spin) + We can use Docker to run install `spin` on your project. **Run this command from the parent folder of your project.** ::note diff --git a/docs/content/docs/11.community/1.get-help.md b/docs/content/docs/11.community/1.get-help.md new file mode 100644 index 00000000..9c5dfa5d --- /dev/null +++ b/docs/content/docs/11.community/1.get-help.md @@ -0,0 +1,61 @@ +--- +head.title: 'Get Help - Spin by Server Side Up' +title: 'Get Help' +description: 'Learn how to get help with Spin.' +layout: docs +--- + +# Get Help +::lead-p +We're huge believers the product is only as good as the community that supports it. We're here to help you get the most out of Spin. +:: + +## Ensure you're on the latest version +We're always shipping new features and bug fixes for Spin. Ensure you're on the latest release by checking the [releases page](https://github.com/serversideup/spin/releases). + +You can also run `spin update` to ensure you're on the latest version. + +If you're still having issues, sometimes deleting Spin and reinstalling might be a quick fix because it's so easy to delete and reinstall. + +## Debugging +If you think you've found a bug in Spin, you can get debug logs by setting the `SPIN_DEBUG` environment variable to `true` before running any commands. + +::code-panel +--- +label: Set SPIN_DEBUG to true +--- +```bash +SPIN_DEBUG=true spin version +``` +:: + +This will show you exactly what Spin is running and these debug logs can help the community help you faster. + +## Going back to the stable release +Going back to the stable release is as easy as deleting the `~/.spin` directory then following the installation instructions to reinstall Spin. + +::code-panel +--- +label: Delete Spin and reinstall +--- +```bash +rm -rf ~/.spin +``` +:: + +Now follow the installation instructions for your operating system: + +- [Install to macOS](/docs/installation/install-macos) +- [Install to Linux](/docs/installation/install-linux) +- [Install to Windows](/docs/installation/install-windows) + +## Community Support +If you're still having issues, our community primarily uses [GitHub Discussions](https://github.com/serversideup/spin/discussions) and [Discord](https://serversideup.net/discord) to get help with Spin. + +## Resources +We offer a number of resources to help you get the most out of Spin: +- **[Website](https://serversideup.net/open-source/spin)** overview of the product. +- **[Docs](https://serversideup.net/open-source/spin/docs)** for a deep-dive on how to use the product. +- **[Discord](https://serversideup.net/discord)** for friendly support from the community and the team. +- **[GitHub Repo](https://github.com/serversideup/spin)** for source code, bug reports, and project management. +- **[Get Professional Help](https://serversideup.net/professional-support)** - If you need video + screen-sharing support, we offer that too. diff --git a/docs/content/docs/11.community/2.testing.md b/docs/content/docs/11.community/2.testing.md new file mode 100644 index 00000000..cf528712 --- /dev/null +++ b/docs/content/docs/11.community/2.testing.md @@ -0,0 +1,61 @@ +--- +head.title: 'Testing New Releases - Spin by Server Side Up' +title: 'Testing New Releases' +description: 'Learn how to test new releases of Spin.' +layout: docs +--- + +# Testing New Releases +::lead-p +Spin is a collection of many open source projects built by the Server Side Up community. If you're looking for the latest features, here is how you can help test them out. +:: + +## Installing latest Spin beta release +Spin is very lightweight and is installed to your `~/.spin` directory by default. If you'd like to install the latest beta, we can easily + +::code-panel +--- +label: Install latest Spin beta release +--- +```bash +# Ensure previous installation is removed +rm -rf ~/.spin + +# Install the latest release (regardless of pre-release or stable) +bash -c "$(curl -fsSL https://raw.githubusercontent.com/serversideup/spin/main/tools/install.sh)" "" --beta +``` +:: + +## Going back to the stable release +Going back to the stable release is as easy as deleting the `~/.spin` directory then following the installation instructions to reinstall Spin. + +::code-panel +--- +label: Delete the ~/.spin directory +--- +```bash +rm -rf ~/.spin +``` +:: + +Now follow the installation instructions for your operating system: + +- [Install to macOS](/docs/installation/install-macos) +- [Install to Linux](/docs/installation/install-linux) +- [Install to Windows](/docs/installation/install-windows) + +## Repositories +There are many repositories that are part of the Spin project. Refer to each repository for instructions on how to test the latest changes. + +#### Ansible Collection +- [Spin Ansible Collection](https://github.com/serversideup/ansible-collection-spin) - Used to provision and maintain your production server. + +#### GitHub Actions +- [serversideup/docker-build-action](https://github.com/marketplace/actions/docker-build-action) - A simplified syntax to build and publish your Docker images with GitHub Actions. +- [serversideup/docker-swarm-deploy-github-action](https://github.com/marketplace/actions/docker-swarm-deploy-github-action) - A simplified syntax to deploy to Docker Swarm Mode via GitHub Actions. + +#### Docker Images +- [serversideup/php](https://serversideup.net/open-source/docker-php/) - PHP Docker images highly optimized to work with Laravel + Spin. +- [serversideup/docker-ssh](https://github.com/serversideup/docker-ssh) - A lightweight docker image that runs SSH. This is a fantastic method on using a secure SSH tunnel into your database cluster. +- [serversideup/docker-ansible](https://github.com/serversideup/docker-ansible) - A lightweight docker image that runs Ansible. This allows users to provision their server without needing to know how to install or maintain their Ansible installation. +- [serversideup/docker-github-cli](https://github.com/serversideup/docker-github-cli) - A lightweight docker image that runs GitHub CLI. This allows users to interact with GitHub without needing to install the GitHub CLI on their machine. \ No newline at end of file diff --git a/docs/content/docs/6.community/3.contributing.md b/docs/content/docs/11.community/3.contributing.md similarity index 100% rename from docs/content/docs/6.community/3.contributing.md rename to docs/content/docs/11.community/3.contributing.md diff --git a/docs/content/docs/2.getting-started/1.how-spin-works.md b/docs/content/docs/2.getting-started/1.how-spin-works.md index 839c17ab..1d659ed9 100644 --- a/docs/content/docs/2.getting-started/1.how-spin-works.md +++ b/docs/content/docs/2.getting-started/1.how-spin-works.md @@ -3,6 +3,7 @@ head.title: 'How Spin Works - Spin by Server Side Up' title: 'How Spin Works' description: 'Learn what Spin is and how it works.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/getting-started/how-spin-works --- # How Spin Works @@ -10,6 +11,9 @@ layout: docs Spin is a collection of proven open source technologies delivered to you in a lightweight and simple experience. This document explains what technologies Spin uses, how it works, and how this tool can enable you to have a lot of power and freedom to self-host and distribute your applications. :: +## Spin stays out of your way +Spin is designed to be flexible and gives you the power to choose what you'd like Spin to help you with. For example, if you want to use your current PaaS provider for production deployments, but you just want to use Spin for local development, you can easily do that without Spin getting in the way. Simply delete the `docker-compose.prod.yml` file and you won't need to do anything else. + ## What Spin Is Built On Top Of ![Spin Diagram](/images/docs/whats-spin/spin-diagram.png) @@ -18,8 +22,8 @@ At the core of Spin are: - [Docker Swarm Mode](https://docs.docker.com/engine/swarm/) (used in production, staging, etc.) - [Ansible](https://www.ansible.com/) (used to ensure your server is configured correctly) -### Templates -We offer templates to help you get started with Spin, or you can [create your own template](/docs/project-templates/create-your-own-template). +### Templates on Spin Hub +We have organized a [community hub](/hub) where you can find templates created by the Spin team and the community. You can also [create your own template](/docs/advanced/create-your-own-template). ### Flexible deployments With Spin, you have two options for deployments: @@ -39,6 +43,8 @@ Spin accesses and utilizes other open source projects to simplify your developme #### Docker Images - [serversideup/php](https://serversideup.net/open-source/docker-php/) - PHP Docker images highly optimized to work with Laravel + Spin. - [serversideup/docker-ssh](https://github.com/serversideup/docker-ssh) - A lightweight docker image that runs SSH. This is a fantastic method on using a secure SSH tunnel into your database cluster. +- [serversideup/docker-ansible](https://github.com/serversideup/docker-ansible) - A lightweight docker image that runs Ansible. This allows users to provision their server without needing to know how to install or maintain their Ansible installation. +- [serversideup/docker-github-cli](https://github.com/serversideup/docker-github-cli) - A lightweight docker image that runs GitHub CLI. This allows users to interact with GitHub without needing to install the GitHub CLI on their machine. ## Why not just use these technologies directly? Spin is designed to reduce the learning curve for delivering containerized applications with Docker. This is where we believe the 99% of people are. If you're looking to start a new project and deliver it quickly without breaking the bank, Spin is for you. Everything is designed to have your application disposable, repeatable, and scalable. @@ -52,13 +58,7 @@ Spin's flexibility comes from embracing the [Docker Compose Overrides (aka Docke Here is a visual of what the file structure looks like: -::responsive-image ---- -src: /images/docs/whats-spin/docker-compose-overrides.png -alt: 'Docker Compose Overrides' -maxWidth: 500 ---- -:: +![Docker Compose Overrides](/images/docs/whats-spin/docker-compose-overrides.png) Docker natively selecting these files, but it is a huge pain to type out each time. This is what we would have to type to bring our project up. diff --git a/docs/content/docs/2.getting-started/2.create-a-new-project-with-spin.md b/docs/content/docs/2.getting-started/2.create-a-new-project-with-spin.md new file mode 100644 index 00000000..40a14403 --- /dev/null +++ b/docs/content/docs/2.getting-started/2.create-a-new-project-with-spin.md @@ -0,0 +1,41 @@ +--- +head.title: 'Create a New Project with Spin - Spin by Server Side Up' +title: 'Create a New Project with Spin' +description: 'Learn how to create a new project with Spin.' +layout: docs +--- + +# Create a project with Spin +::lead-p +Getting up an running with Spin is very easy. It's recommended to install Spin globally so you can easily create new projects. Follow the installation instructions for [macOS](/docs/installation/install-macos/), [Linux](/docs/installation/install-linux), or [Windows](/docs/installation/install-windows) before continuing. We'll walk you through the process of creating a new Laravel project with Spin. +:: + +## Finding a project on "Spin Hub" +We created a [community hub](/hub) where you can find templates created by the Spin team and the community. We're in the early stages of development, but [anyone can create their own template](/docs/advanced/create-your-own-template) and we will publish it to Spin Hub for you. + +## Let's create a new Laravel project +For our example, we will use the "Laravel" template from Spin Hub. We have a command called `spin new`, which will take care of the heavy lifting for you + +::code-panel +--- +label: "Create a new Laravel project called \"my-billion-dollar-idea\"" +--- +```bash +spin new laravel my-billion-dollar-idea +``` +:: + +This will create a project in your current directory called `my-billion-dollar-idea`. You can also specify a path to create the project in a different directory. + +

+ +

+ +Assuming you completed the prerequisites, the onboard wizard will take you through the process so you can get a local development environment with `spin up`. + +## Starting the development environment +Next, we'll start the development environment with `spin up`. + +[Starting up your Development Environment →](/docs/development-environment/starting-up-your-development-environment) \ No newline at end of file diff --git a/docs/content/docs/2.getting-started/2.add-spin-to-an-existing-project.md b/docs/content/docs/2.getting-started/3.add-spin-to-an-existing-project.md similarity index 82% rename from docs/content/docs/2.getting-started/2.add-spin-to-an-existing-project.md rename to docs/content/docs/2.getting-started/3.add-spin-to-an-existing-project.md index 5971b4dc..4b4d5fd7 100644 --- a/docs/content/docs/2.getting-started/2.add-spin-to-an-existing-project.md +++ b/docs/content/docs/2.getting-started/3.add-spin-to-an-existing-project.md @@ -35,10 +35,11 @@ spin init ``` :: -You need to replace `` with the name of the template you want to use. Look under "Project Templates" in the sidebar/navigation to see the available templates. - -## Important things to know +::note Each template will have its own special `init` process. Be sure to refer to the project's README file for any steps required after adding Spin to your project. +:: + +You need to replace `` with the name of the template you want to use. You can find available templates under ["Spin Hub"](/hub), which is a collection of templates contributed by the Spin community. ## Next Steps -[Prepare Your Servers for Spin →](/docs/getting-started/preparing-your-servers-for-spin) \ No newline at end of file +[Start up your development environment →](/docs/development-environment/starting-up-your-development-environment) \ No newline at end of file diff --git a/docs/content/docs/2.getting-started/3.create-a-new-project-with-spin.md b/docs/content/docs/2.getting-started/3.create-a-new-project-with-spin.md deleted file mode 100644 index 9044ce05..00000000 --- a/docs/content/docs/2.getting-started/3.create-a-new-project-with-spin.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -head.title: 'Create a project - Spin by Server Side Up' -title: 'Create a new project with Spin' -description: 'Learn how to create a new project with Spin.' -layout: docs ---- - -# Create a project with Spin -::lead-p -Getting up an running with Spin is very easy. It's recommended to install Spin globally so you can easily create new projects. Follow the installation instructions for [macOS](/docs/installation/install-macos/), [Linux](/docs/installation/install-linux), or [Windows](/docs/installation/install-windows) before continuing. We'll walk you through the process of creating a new Laravel project with Spin. -:: - -## Let's create a new Laravel project -For our example, we will walk you though how to create a new Laravel project with Spin. We have a command called `spin new`, which will take care of the heavy lifting for you - -::code-panel ---- -label: "Create a new Laravel project called \"my-billion-dollar-idea\"" ---- -```bash -spin new laravel my-billion-dollar-idea -``` -:: - -This will create a project in your current directory called `my-billion-dollar-idea`. You can also specify a path to create the project in a different directory. - -

- -

- -Assuming you completed the prerequisites, the onboard wizard will take you through the process so you can get a local development environment with `spin up`. - -## Further Configuration -::note -Since we're using the [Laravel Basic](/docs/project-templates/laravel-basic) template, it's important to review the [project's README file](https://github.com/serversideup/spin-template-laravel-basic) for any additional steps required after creating the project. -:: - -Regardless of what template you're using, it's always important to review the template's README file for any additional steps required after creating the project. You may need to customize project URLs, environment variables, or other settings to get your project up and running. - -Once you have things configured, it could be possible to stop here and just have Spin manage your development environment -- but you won't get the full benefit of Spin. - -Spin is designed to keep your infrastructure 100% the same, no matter the environment. In the remainder of this guide, we'll show you how to deploy your project to a server. - -## Choosing a deployment strategy -Spin offers two deployment strategies: - -1. [`spin deploy` ](/docs/command-reference/deploy) (simple) -1. [GitHub Actions](/docs/advanced/zero-downtime-deployments-with-github-actions) (advanced) - -| | spin deploy | GitHub Actions | -|-------------------------|----------------------|-------------------------| -| Difficulty | Very Easy | Complex (it's automated CI/CD after all 🤪) | -| Team Size | Small teams | Small to very large teams | -| Zero-downtime Deployments | ✅ | ✅ | -| Automated Testing | ❌ | ✅ (you have to configure this though) | - - -### `spin deploy` (simple) -If you're a small team or bootstrapping a project to get it out the door as fast as possible, `spin deploy` might be your best friend. - -Without the headache of CI/CD, `spin deploy` will perform a zero-downtime deployment from your local machine to your server directly over SSH. There are no requirements for having a CI/CD pipeline or private Docker registries. - -Read more about `spin deploy` in the [command reference](/docs/command-reference/deploy). - -### GitHub Actions (advanced) -For GitHub Actions, we created an entire guide on how to achieve zero-downtime deployments with GitHub Actions. This is a more advanced strategy, but it's worth it if you're looking to automate your deployments. - -[Learn more about GitHub Actions deployments →](/docs/advanced/zero-downtime-deployments-with-github-actions) - -## Deploying to a server -Although it is possible to just use Spin for local development only, you'll get the full benefit of Spin when you use it to deploy your application to a server. - -Use the guide below to learn how to get your servers ready for a Spin deployment: - -[Prepare Your Servers for Spin →](/docs/getting-started/preparing-your-servers-for-spin) \ No newline at end of file diff --git a/docs/content/docs/2.getting-started/3.preparing-your-servers-for-spin.md b/docs/content/docs/2.getting-started/3.preparing-your-servers-for-spin.md deleted file mode 100644 index 241542b0..00000000 --- a/docs/content/docs/2.getting-started/3.preparing-your-servers-for-spin.md +++ /dev/null @@ -1,278 +0,0 @@ ---- -head.title: 'Preparing Your Servers For Spin - Spin by Server Side Up' -title: 'Preparing Your Servers For Spin' -description: 'Learn how to get your server ready for Spin deployment.' -layout: docs ---- - -# Preparing your server -::lead-p -Spin gives you the power to choose any host that you want. Although the responsibility is on your shoulders to get a server set up, Spin makes it very easy to get this accomplished. -:: - -## Choose a host -We've run [VPS Benchmarks against a number of hosts](https://521dimensions.notion.site/VPS-Benchmarks-for-Self-hosters-c6eca7c5f16d4bb8aeb989174fc58ffe?pvs=4) and have found the following hosts to have great value. The links below are referral links, meaning we get a small kickback if you sign up -- but this doesn't affect our recommendations. - -### Hetzner -Hetzner is a German-based company with data centers in the EU and the US. Their pricing is very competitive and they have incredible performance. - -With the link below, they offer €⁠20 (~$21 USD) credits free which can get you a server for a few months. - -[Sign Up with Hetzner →](https://hetzner.cloud/?ref=lhLUIrkdUPhl) - -### Vultr -Vultr is a US-based company that has a great reputation for performance and value. They have a wide range of data centers around the world and a great API for automation. - -With the link below, they are offering $100 in credits -- which can get you quite a bit for a few months. - -[Sign Up with Vultr →](https://www.vultr.com/?ref=9627777-8H) - -### Digital Ocean -Digital Ocean is a US-based company that has data centers all around the world. Although their performance generally lacks compared to other providers, there are so many resources that natively support their offerings. - -With the link below, they are offering $200 in credits over 60 days. - -[Sign Up with Digital Ocean →](https://m.do.co/c/f3bad4b927ca) - -### Any other host -Spin is host-agnostic, meaning you can run Spin anywhere. If you have a preferred host, you can use them as well. Just make sure that your host meets the following requirements below. - -## Recommended Server Specs -::note -It is best to run `spin provision` against a **brand new server** with a fresh installation of Ubuntu 22.04 or newer. This will ensure that the server is in a known state and that the configuration will work as expected. -:: - -Hardware specs largely depend on your project, but even for simple apps, we recommend the following: - -- **CPU**: 1 CPU core -- **RAM**: 1GB -- **Storage**: >25GB SSD -- **Architecture**: x86_64 (64-bit) [[request ARM64 support](https://github.com/serversideup/spin/discussions/9)] -- **Operating System**: Ubuntu 22.04 LTS or newer -- **Snapshots/Backups**: Highly recommended -- **Firewall**: Configured to allow SSH and HTTPS traffic (22/tcp, 80/tcp, 443/tcp) -- **Access**: Root SSH access - -## Prepare your DNS -Once you have a server running with the host of your choice, it's recommended to configure your DNS to have two A records created pointing to the IP address of your server. - -Two records should be created: - -1. Application Domain Name (example: `myapp.example.com`) -2. Server Hostname (example: `server01.example.com`) - -::responsive-image ---- -src: /images/docs/getting-started/server-dns.png -alt: 'Configure your DNS' -maxWidth: 500 ---- -:: - -## Validate your SSH connection -Before you start, make sure you can SSH into your server **using an SSH key pair**. Many hosts do this by default, but you need to follow your hosts instructions for adding a trusted public key that can connect to your server. If you need help generating an SSH key, you can follow our guide on [Generating a Secure SSH Key](/docs/advanced/generating-a-secure-ssh-key). - - You can do this by running the following command: - -::note -We're assuming your host provisions their Ubuntu images as `root` as the default user. Use whatever standard your host uses, just as long as it has root access. -:: - -::code-panel ---- -label: "SSH into your server" ---- -```bash -ssh root@myserver01.example.com -``` -:: - -## Ensure your server is up to date -On your server, it's good practice to ensure everything is updated. Run this command below to update all packages on your server. Restarting the server is a good idea too to ensure everything is up to date. - -::code-panel ---- -label: "Update your server" ---- -```bash -apt -y update && apt -y upgrade && apt -y autoremove && apt -y autoclean && reboot -``` -:: - -## Configure server inventory and settings -::note -If your files are encrypted, you need to use the [`spin vault edit`](/docs/command-reference/vault) command to edit the file. This will decrypt the file, open it in your default editor, and then re-encrypt it when you save and close the file. -:: -Assuming you've already ran [`spin new`](/docs/command-reference/new) or [`spin init`](/docs/command-reference/init) in your project, there are two files that will need to be configured: -- `.spin-inventory.ini` -- `.spin.yml` - -#### Inventory -We call the list of our servers "inventory". This follows the [Ansible Inventory format](https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html). For example, if you wanted to add `server01.example.com` as the production server, we would configure our file to look like this and add this server to go underneath the `production_manager_servers` group: - -::note -Be sure to use your **server's hostname** (NOT your application's **domain name**) in the inventory file. This will ensure we have a dedicated DNS entry for managing your server. -:: - -::code-panel ---- -label: ".spin-inventory.ini: Add `server01.example.com` to Production" ---- -```ini -#################### -# Host Types -#################### - -[production_manager_servers] -server01.example.com - -[staging_manager_servers] -# server02.example.com - -#################### -# Swarm Roles -#################### -[swarm_managers:children] -production_manager_servers -staging_manager_servers - -#################### -# Environment -#################### -[production:children] -production_manager_servers - -[staging:children] -staging_manager_servers - -[all_servers:children] -production -staging -``` -:: - -## Configure server settings -In the `.spin.yml` file, you will find many other settings. These are required for you to change so you can connect to your server. - -#### 👉 Required to be changed -::note -**If you intend to use GitHub Actions**, we strongly recommend creating a dedicated SSH key pair for a "deploy" user (instead of using your own login, like `bob`). - -[Learn how to create a dedicated "deploy" SSH key pair →](/docs/advanced/generating-a-secure-ssh-key#generating-a-deployment-key) -:: - -| Key | Description | -|-----|---| -| `server_contact` | Change this to a valid email. **If you're using our Traefik templates**, you'll also need to change the `letsencryptresolver.acme.email` key under the `.infrastructure/conf/traefik/prod/traefik.yml` file. | -| `docker_user.authorized_ssh_keys` | OPTIONAL: Use this only if you're having something like GItHub Actions connect as the Docker User. This must be the **public key** of the your [DEPLOY user SSH keypair that you generated](/docs/advanced/generating-a-secure-ssh-key#generating-a-deployment-key). -| `users.username` | Set this to a username that you'll remember. Be sure to not use any weird characters. It might be in your best interest to match the username of your local machine. You can find this by running `whoami` in a new terminal.| -| `users.password` | Set this to the secure hash of the password you'd like to use. All users will be prompted to change on first login. Use [`spin mkpasswd`](/docs/command-reference/mkpasswd) to quickly generate a hash.| -| `users.username.authorized_ssh_keys` | Set this to **your USER _public_ SSH key value**. ⚠️ The key pair must be accessible under `~/.ssh` on your host. Most of the time the file ends in `*.pub`.| - - -::code-panel ---- -label: ".spin.yml" ---- -```yml ---- -########################################### -# Basic Server Configuration -########################################### -server_timezone: "Etc/UTC" -server_contact: changeme@example.com # 👈 Change this - -# SSH -ssh_port: "22" - -## Email Notifications -postfix_hostname: "{{ inventory_hostname }}" # ✅ Set automatically - -## Set variables below to enable external SMTP relay -# postfix_relayhost: "smtp.example.com" -# postfix_relayhost_port: "587" -# postfix_relayhost_username: "myusername" -# postfix_relayhost_password: "mysupersecretpassword" - -############################################################## -# Deploy User -############################################################## -docker_user: - username: deploy - uid: 9999 - group: deploy - secondary_groups: "docker" - gid: 9999 - ## Uncomment to set authorized SSH keys for the docker user. - # authorized_ssh_keys: - # - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNJGtd7a4DBHsQi7HGrC5xz0eAEFHZ3Ogh3FEFI2345 deploy" # 👈 Change this - # - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRfXxUZ8q9vHRcQZ6tLb0KwGHu8xjQHfYopZKLmnopQ anotherfake@key" - -############################################################## -# Users -############################################################## - -### Use the template below to set users and their authorized keys -## Passwords must be set with an encrypted hash. To do this, see the Ansible FAQ. -## Your best bet is probably using the `mkpasswd` on a Linux machine. -## https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-encrypted-passwords-for-the-user-module - -users: - - username: alice # 👈 Change this - name: Alice Smith # 👈 Change this - state: present - groups: ['adm','sudo'] - password: "$6$mysecretsalt$qJbapG68nyRab3gxvKWPUcs2g3t0oMHSHMnSKecYNpSi3CuZm.GbBqXO8BE6EI6P1JUefhA0qvD7b5LSh./PU1" # 👈 Change this - shell: "/bin/bash" - authorized_keys: - - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" # 👈 Change this - -# - username: bob -# name: Bob Smith -# state: present -# password: "$6$mysecretsalt$qJbapG68nyRab3gxvKWPUcs2g3t0oMHSHMnSKecYNpSi3CuZm.GbBqXO8BE6EI6P1JUefhA0qvD7b5LSh./PU1" -# groups: ['adm','sudo'] -# shell: "/bin/bash" -# authorized_keys: -# - public_key: "ssh-ed25519 AAAAC3NzaC1anotherfakekeyIMVIzwQXBzxxD9b8Erd1FKVvu bob" -``` -:: - -## Run `spin provison` -Once you have `.spin.yml` and `.spin-inventory.ini` configure, you're ready to provision your server(s). - -::note -Notice how we're using `--user root` in the command below. This is because most hosts provision `root` as the default user. - -By default `spin provision` will attempt to connect over SSH using the username on your workstation (how SSH works by default). Since your server doesn't come with your username already created, we need to connect as `root` on our first provision so Spin can configure the accounts configured in `.spin.yml`. -:: - -::code-panel ---- -label: "Provision your server(s)" ---- -```bash -spin provision --user root -``` -:: - -This process will automatically download [our Ansible collection](https://github.com/serversideup/ansible-collection-spin) and apply the changes on your server. - -## Confirm everything works -To confirm everything works as expected, try to SSH into your server using the username you created before. You will be prompted to change your password to a new password as soon as you login. - -::code-panel ---- -label: "Connect to your server via SSH" ---- -```bash -ssh myuser@server01.example.com -``` -:: - -If you get a connection, you should be ready to receive deployments to Docker Swarm via SSH 🥳 - -## Next Steps -If you're using the `spin deploy` deployment strategy, then take a look at our guide on how to run a simple deployment. - -[Deploy Your Project with `spin deploy` →](/docs/getting-started/run-a-simple-deployment) \ No newline at end of file diff --git a/docs/content/docs/2.getting-started/4.run-a-simple-deployment.md b/docs/content/docs/2.getting-started/4.run-a-simple-deployment.md deleted file mode 100644 index 897d193a..00000000 --- a/docs/content/docs/2.getting-started/4.run-a-simple-deployment.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -head.title: 'Run a Simple Deployment with Spin - Spin by Server Side Up' -title: 'Run a Simple Deployment with Spin' -description: 'Learn how to get your server ready for Spin deployment.' -layout: docs ---- - -# Deploying your application with Spin -::lead-p -This guide is a continuation of the "Getting Started" section of the Spin documentation. In this section, we assumme [created a project with Spin](/docs/getting-started/create-a-new-project-with-spin), and you want to use the [`spin deploy`](/docs/getting-started/create-a-new-project-with-spin#choosing-a-deployment-strategy) deployment strategy. -:: - -## Prerequisites -- Your [project has been created with Spin](/docs/getting-started/create-a-new-project-with-spin) or [you added Spin to your existing project](/docs/getting-started/add-spin-to-an-existing-project) -- Your [server has been provisioned with Spin](/docs/getting-started/preparing-your-servers-for-spin) -- You've [completed any additional steps for your project template](/docs/getting-started/create-a-new-project-with-spin#further-configuration), defined by its README - -## Running your deployment -Once you have the prerequisites in place, you can run the `spin deploy` command to deploy your application to your server. - -::code-panel ---- -label: Deploy your application with Spin ---- -```bash -spin deploy -``` -:: - -By default the environment is `production`, but you can specify your own environment if you'd like. For example, if you have a `staging` environment, you can run `spin deploy staging`. - -## Using different `.env` files per environment -By default, everything runs off the `.env` file. This is great for local development, but it can be a challenge if you want to deploy to multiple environments from the same folder. - -To solve this, you can create `.env` files for each environment you want to deploy to. For example, you can create `.env.production` and `.env.staging` files. When you run `spin deploy production`, the `.env.production` file will be used. When you run `spin deploy staging`, the `.env.staging` file will be used. - -::note -This approach is highly reliant on the framework you're using. For example, when you run `spin deploy production`, the `APP_ENV` variable will be set to `production`. [Laravel is intelligent enough](https://laravel.com/docs/11.x/configuration#additional-environment-files) to know to use the `.env.production` file. If you're using a different framework, you might need to adjust your configuration to use this approach. -:: - -Be sure to add `.env.*` files to your `.gitignore` file so they are not committed to your repository. - -## What happens when you run `spin deploy`? -Running the `spin deploy` command automates the process of deploying your application to your server without the headache of worrying about CI/CD. - -You can read more about the `spin deploy` command in the [command reference](/docs/command-reference/deploy). Long stort short, the `spin deploy` command does the following: - -1. **Builds your application**: The `spin deploy` command will package your application into a Docker image -1. **Creates a temporary Docker registry on your workstation**: The Docker image is pushed to a temporary Docker registry on your workstation -1. **A SSH tunnel is created**: A SSH tunnel is created to your server so it links to the temporary Docker registry -1. **The Docker image is pulled to your server**: The Docker image is pulled from the temporary Docker registry to your server -1. **A Swarm Deployment is created**: A Swarm deployment is created on your server with the new Docker image -1. **The temporary Docker registry is turned off**: The temporary Docker registry is turned off on your workstation and the SSH tunnel is terminated - -This process greatly reduces the knowledge and effort to complete a zero-downtime deployment. - -## This might not work best for all team sizes -::note -While the `spin deploy` command is great for small teams or solo developers, it might not be the best solution for larger teams. If you have a larger team, you might want to consider setting up a CI/CD pipeline with Spin. This will allow you to have more control over your deployments and ensure that your team is following the same deployment process with improved credential management. -:: - -Always remember, Spin is a **structure** just as much as it is a tool. We ensure flexibility is always available to you so you can use Spin when you need it and Spin get's out of your way when you don't want to use it. To help larger teams, we created open-source workflows that can be used with GitHub actions to create zero-downtime deployments. - -[Learn more about setting up zero-downtime deployments with GitHub Actions](/docs/advanced/zero-downtime-deployments-with-github-actions) \ No newline at end of file diff --git a/docs/content/docs/3.development-environment/1.starting-up-your-development-environment.md b/docs/content/docs/3.development-environment/1.starting-up-your-development-environment.md new file mode 100644 index 00000000..3163f031 --- /dev/null +++ b/docs/content/docs/3.development-environment/1.starting-up-your-development-environment.md @@ -0,0 +1,66 @@ +--- +head.title: 'Starting Up Your Development Environment - Spin by Server Side Up' +title: 'Starting Up Your Development Environment' +description: 'Learn how to start up your development environment with Spin.' +layout: docs +--- + +# Starting up your Development Environment +::lead-p +Spin uses Docker Compose to start up your development environment in a single command. This allows you to exactly replicate your production environment for local development. +:: + +

+ +

+ +## Prerequisites +- Docker is installed and running. +- Spin is installed to your system or to your project (via composer or as a node dependency). + +## Ensure your application dependencies are installed +Before you can bring up your development environment, you need to ensure your application dependencies are installed. + +::note +If you just ran `spin new` to create a project, the application dependencies will already be installed. You can skip this step. +:: + +::code-panel +--- +label: Install dependencies (this depends on your project) +--- +```bash +# PHP dependencies +spin run php composer install + +# Node dependencies +spin run node yarn install +``` +:: + +## Bring up your development environment +Once your dependencies are installed, you can bring up your development environment with the following command: + +::note +Make sure to run this command from your project directory. +:: + +::code-panel +--- +label: Bring up your development environment +--- +```bash +spin up +``` +:: + +::note +To stop your development environment, press `Ctrl+C`. +:: + +Since Spin is running Docker Compose, you can also pass any argument to the `up` command. For example, `spin up -d` will start your development environment in detached mode. See the full documentation for [spin up](/docs/command-reference/up) for more information. + +## Accessing your development environment +In order to access your development environment, review the README of the template you're using. Each template has different instructions on how to access the services. diff --git a/docs/content/docs/3.development-environment/2.running-commands-in-development.md b/docs/content/docs/3.development-environment/2.running-commands-in-development.md new file mode 100644 index 00000000..f34d9623 --- /dev/null +++ b/docs/content/docs/3.development-environment/2.running-commands-in-development.md @@ -0,0 +1,51 @@ +--- +head.title: 'Running Commands in Development - Spin by Server Side Up' +title: 'Running Commands in Development' +description: 'Learn how to run commands in your development environment with Spin.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/development-environment/running-commands-in-development +--- + +# Running Commands in Development +::lead-p +Spin makes it easy to run commands in your development environment by following the Docker Compose CLI syntax. +:: + +## How to run commands in development +You have two options to run commands in your development environment: + +1. [`spin exec`](/docs/command-reference/exec) - This executes commands in an EXISTING container (and requires that service to be already running with `spin up`). +2. [`spin run`](/docs/command-reference/run) - This executes commands in a NEW container, then exits. + +For example, let's say you want to run `composer install` in your development environment for Laravel, you would use the following command: + +::note +If you have `spin up` running in a terminal window, you will need to run the `spin run` or `spin exec` command in a new terminal window from your project directory. +:: + +::code-panel +--- +label: Run a command in a new container +--- +```bash +# Usage: spin run +spin run php composer install +``` +:: + +::code-panel +--- +label: Execute a command in an existing container +--- +```bash +# Usage: spin exec +spin exec php composer install +``` +:: + +Take note of the syntax. You specify the service you want to run the command in, then the command you want to run. The same is true for `spin exec`. + +## When to use `spin exec` vs `spin run` +In development, it's often easier to use `spin run` because it will create a new container and then exit. This is faster than having to start the container with `spin up` and then use `spin exec`. + +If you have something that you specifically need to run in an already running container, then you should use `spin exec`. \ No newline at end of file diff --git a/docs/content/docs/4.command-reference/provision.md b/docs/content/docs/4.command-reference/provision.md deleted file mode 100644 index 34d2848c..00000000 --- a/docs/content/docs/4.command-reference/provision.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -head.title: 'provision | Command Reference - Spin by Server Side Up' -title: 'provision' -description: 'Command reference for "spin provision"' -layout: docs ---- -# spin provision -::lead-p -Provision and set up your inventory of servers. Before running this command, make sure you have everything configured and a supported server online with any host of your choice. -:: - -## Usage -::code-panel ---- -label: Usage for "spin provision" ---- -```bash -spin provision [ -p|--port -u|--user -U|--upgrade ] -``` -:: - -## Checklist before executing this command -Before you execute this command, you should have the following completed: - -- You should have a running **Ubuntu 22.04+ server** with properly configured SSH access and DNS -- The `.spin.yml` file should be configured -- The `.spin-inventory.ini` file should properly be configured - -## Options -The following options are available to set when running this command. -| Option | Short | Default | Description | -| --- | --- | --- | --- | -| `--port` | `-p` | `22` | The port to SSH into the server with. | -| `--user` | `-u` | The username of your HOST machine (run `whoami` in a new terminal) | The user to SSH into the server with. | -| `--upgrade` | `-U` | Check for Ansible collection updates once per day. | Force upgrade the Ansible Collection on your machine before provisioning. | - -## Learn More -[Configuring your servers for "spin provision" →](/docs/getting-started/preparing-your-servers-for-spin) \ No newline at end of file diff --git a/docs/content/docs/4.server-configuration/1.server-requirements.md b/docs/content/docs/4.server-configuration/1.server-requirements.md new file mode 100644 index 00000000..ef03d2b0 --- /dev/null +++ b/docs/content/docs/4.server-configuration/1.server-requirements.md @@ -0,0 +1,62 @@ +--- +head.title: 'Server Requirements - Spin by Server Side Up' +title: 'Server Requirements' +description: 'Learn the requirements for running Spin.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/server-configuration/server-requirements +--- + +# Server Requirements +::lead-p +Spin gives you the power to choose any host that you want. Although the responsibility is on your shoulders to get a server set up, Spin makes it very easy to get this accomplished. +:: + +## Choose a host +We've run [VPS Benchmarks against a number of hosts](https://521dimensions.notion.site/VPS-Benchmarks-for-Self-hosters-c6eca7c5f16d4bb8aeb989174fc58ffe?pvs=4) and have found the following hosts to have great value. The links below are referral links, meaning we get a small kickback if you sign up -- but this doesn't affect our recommendations. + +### Hetzner +Hetzner is a German-based company with data centers in the EU and the US. Their pricing is very competitive and they have incredible performance. + +With the link below, they offer €⁠20 (~$21 USD) credits free which can get you a server for a few months. + +[Sign Up with Hetzner →](https://hetzner.cloud/?ref=lhLUIrkdUPhl) + +### Vultr +Vultr is a US-based company that has a great reputation for performance and value. They have a wide range of data centers around the world and a great API for automation. + +With the link below, they are offering $100 in credits -- which can get you quite a bit for a few months. + +[Sign Up with Vultr →](https://www.vultr.com/?ref=9627777-8H) + +### Digital Ocean +Digital Ocean is a US-based company that has data centers all around the world. Although their performance generally lacks compared to other providers, there are so many resources that natively support their offerings. + +With the link below, they are offering $200 in credits over 60 days. + +[Sign Up with Digital Ocean →](https://m.do.co/c/f3bad4b927ca) + +### Any other host +Spin is host-agnostic, meaning you can run Spin anywhere. If you have a preferred host, you can use them as well. Just make sure that your host meets the following requirements below. + +## Recommended Server Specs +::note +Note if you use any of our supported providers in the left sidebar, we'll automatically configure everything for you. 🤓 +:: + +Hardware specs largely depend on your project, but even for simple apps, we recommend the following: + +- **CPU**: 1 CPU core +- **RAM**: 1GB +- **Storage**: >25GB SSD +- **Architecture**: x86_64 (64-bit) [[request ARM64 support](https://github.com/serversideup/spin/discussions/9)] +- **Operating System**: Ubuntu 22.04 LTS or newer +- **Snapshots/Backups**: Highly recommended +- **Firewall**: Configured to allow SSH and HTTPS traffic (22/tcp, 80/tcp, 443/tcp, 2222/tcp) +- **Access**: Root SSH access is required + +**Spin requires a fresh server installation.** Do not install Spin on servers that already have control panels (like Plesk, cPanel) or other server management tools (like Laravel Forge) installed, as this can cause configuration conflicts. + +## Next steps +The next step is to understand how servers work with Spin. + +[Learn how servers work with Spin →](/docs/server-configuration/server-configuration-basics) diff --git a/docs/content/docs/4.server-configuration/2.server-configuration-basics.md b/docs/content/docs/4.server-configuration/2.server-configuration-basics.md new file mode 100644 index 00000000..28fb1cb6 --- /dev/null +++ b/docs/content/docs/4.server-configuration/2.server-configuration-basics.md @@ -0,0 +1,45 @@ +--- +head.title: 'Server Configuration Basics - Spin by Server Side Up' +title: 'Server Configuration Basics' +description: 'Learn how to configure your servers with Spin.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/server-configuration/server-configuration-basics +--- + +# Server Configuration Basics +::lead-p +Spin ensures your application can be 100% replicated from development to production without any vendor lock-in. Spin also gives you the flexibility to choose if you want Spin to manage your servers or not. +:: + +## 👉 Important: Spin doesn't need to manage your servers if you don't want it to +It's kind of weird to start a document to talk about how NOT to use a feature, but we want to show the flexibility that Spin gives you. If you're satisfied with your current hosting provider, you don't need to use Spin to manage your servers. You can easily configure your PaaS to use the same Dockerfiles that you're using in development with Spin. You'll just need to reference your PaaS's own documentation on how to do this. + +All you need to do is delete the `.spin.yml` and any `docker-compose.*.yml` files that you don't want and `spin up` will still work perfectly fine. + +If you prefer to not have any vendor lock-in from a PaaS provider and you're interested in the full control of your own servers, keep reading! Spin will give you the power of a PaaS without the restriction of running your application on a specific platform. + +## How servers work with Spin +Spin places all of its configuration in the `.spin.yml` file. This file is used to configure your servers, users, connections to providers, and more. +When you run `spin provision`, Spin will pull down [our Ansible Docker image](https://github.com/serversideup/docker-ansible/) and use the [Spin Ansible Collection](https://github.com/serversideup/ansible-collection-spin) to configure your providers and servers. + +![Spin Provision Diagram](/images/docs/whats-spin/spin-provision.png) + +## Connect a provider or bring your own server +You can see our ever expanding list of providers on the left sidebar. If you'd like to use a provider that isn't listed, you can easily [use your own server](/docs/providers/use-any-host). This means you can run Spin on any host that meets the [Spin Server Requirements](/docs/server-configuration/server-requirements), including big cloud providers down to an old computer running in your grandmother's basement 🤠. + +If you connect a provider via an API token, you'll get added benefits of automated server creation, backups, firewalls, and more. + +## The `.spin.yml` file +The main configuration file for using servers with Spin is the `.spin.yml` file. Whenever you run `spin new` or `spin init`, this file is created. By default, the `.spin.yml` file is not included in your Git repository. You could think of it as an `.env` file for your servers. The file can contain sensitive information, so be sure to keep it secure. + +[Learn how to use the .spin.yml file →](/docs/server-configuration/spin-yml-usage) + +## Provision your server on any host +Once you have your `.spin.yml` file configured, we can use `spin provision` to provision your server on any host that meets the [Spin Server Requirements](/docs/server-configuration/server-requirements). + +Pick the provider you'd like to use to learn more: + +- [Hetzner](/docs/providers/hetzner) +- [Vultr](/docs/providers/vultr) +- [Digital Ocean](/docs/providers/digitalocean) +- [Any other host](/docs/providers/use-any-host) diff --git a/docs/content/docs/4.server-configuration/3.spin-yml-usage.md b/docs/content/docs/4.server-configuration/3.spin-yml-usage.md new file mode 100644 index 00000000..4a65971a --- /dev/null +++ b/docs/content/docs/4.server-configuration/3.spin-yml-usage.md @@ -0,0 +1,346 @@ +--- +head.title: '.spin.yml Usage - Spin by Server Side Up' +title: '.spin.yml Usage' +description: 'Learn how to use the .spin.yml file to configure your servers with Spin.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/server-configuration/spin-yml-usage +--- + +# .spin.yml Usage +::lead-p +The `.spin.yml` file is the main configuration file for using servers with Spin. It is used to configure your users, providers, servers, hardware profiles, environments, and more. +:: + +## Example `.spin.yml` file +Here's an example of what the `.spin.yml` file looks like: + +::code-panel +--- +label: .spin.yml +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + - username: alice + name: Alice Smith + groups: ['sudo'] + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" + + - username: bob + name: Bob Smith + state: present + password: "$6$mysecretsalt$qJbapG68nyRab3gxvKWPUcs2g3t0oMHSHMnSKecYNpSi3CuZm.GbBqXO8BE6EI6P1JUefhA0qvD7b5LSh./PU1" + groups: ['sudo'] + shell: "/bin/bash" + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1anotherfakekeyIMVIzwQXBzxxD9b8Erd1FKVvu bob" + +############################################################## +# 👇 Providers - You must set at least one provider +############################################################## + +providers: + - name: digitalocean + api_token: Set token here OR delete this line and set environment variable DO_API_TOKEN + + - name: hetzner + api_token: Set token here OR delete this line and set environment variable HCLOUD_TOKEN + + - name: vultr + api_token: Set token here OR delete this line and set environment variable VULTR_API_KEY + +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + - server_name: ubuntu-2gb-ash-1 + environment: production + hardware_profile: hetzner_2c_2gb_ubuntu2404 + + - server_name: ubuntu-1gb-ord-2 + environment: staging + hardware_profile: vultr_1c_1gb_ubuntu2404 + +############################################################## +# 🤖 Hardware Profiles +############################################################## + +hardware_profiles: + # Hetzner + - name: hetzner_2c_2gb_ubuntu2404 + provider: hetzner + profile_config: + location: ash + server_type: cpx11 + image: ubuntu-24.04 + backups: true + + # Vultr + - name: vultr_1c_1gb_ubuntu2404 + provider: vultr + profile_config: + region: ord + plan: vc2-1c-1gb + os: "Ubuntu 24.04 LTS x64" + backups: true + + # DigitalOcean + - name: digitalocean_1c_1gb_ubuntu2404 + provider: digitalocean + profile_config: + region: nyc3 + size: s-1vcpu-1gb + image: ubuntu-24-04-x64 + backups: true + +############################################################## +# 🌎 Environments +############################################################## +environments: + - name: production + - name: staging + - name: development + +############################################################## +# 🤓 Advanced Server Configuration +############################################################## + +# Timezone and contact settings +server_timezone: "Etc/UTC" +server_contact: admin@example.com + +# If you the SSH port below, you may need to run `spin provision -p ` +# to get a connection on your first provision. Otherwise, SSH will try connecting +# to your new port before the SSH server configuration is updated. +ssh_port: "22" + +## You can set this to false to require a password for sudo. +## If you disable passwordless sudo, you must set a password for all sudo users. +## generate an encrypted hash with `spin mkpasswd`. Learn more: +## https://serversideup.net/open-source/spin/docs/command-reference/mkpasswd +use_passwordless_sudo: true + +## Email Notifications +postfix_hostname: "{{ inventory_hostname }}" + +## Set variables below to enable external SMTP relay +# postfix_relayhost: "smtp.example.com" +# postfix_relayhost_port: "587" +# postfix_relayhost_username: "myusername" +# postfix_relayhost_password: "mysupersecretpassword" + +## Deploy user customization - You can customize the deploy user below if you'd like +# docker_user: +# username: deploy +# authorized_ssh_keys: +# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNJGtd7a4DBHsQi7HGrC5xz0eAEFHZ3Ogh3FEFI2345 fake@key" +# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRfXxUZ8q9vHRcQZ6tLb0KwGHu8xjQHfYopZKLmnopQ anotherfake@key" +``` +:: + +## Users +You'll need to configure a user for your server. This will be the user that you use to SSH into your server. + +::note +For your best user experience, you might want to consider having the `username` match the username on your host machine. You can run `whoami` to see your current username and use that as the `username` in your `.spin.yml` file. +:: + +::code-panel +--- +label: .spin.yml Server Users +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + - username: alice + name: Alice Smith + groups: ['sudo'] + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" + + - username: bob + name: Bob Smith + state: present + password: "$6$mysecretsalt$qJbapG68nyRab3gxvKWPUcs2g3t0oMHSHMnSKecYNpSi3CuZm.GbBqXO8BE6EI6P1JUefhA0qvD7b5LSh./PU1" + groups: ['sudo'] + shell: "/bin/bash" + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1anotherfakekeyIMVIzwQXBzxxD9b8Erd1FKVvu bob" +``` +:: + +The above configuration is creating two users: `alice` and `bob`. Both users are added to the `sudo` group, which allows them to use `sudo` to run commands with elevated privileges (such as `sudo su` to get a root shell). + +| Variable | Description | Example | +| -------- | ----------- | ------- | +| `username` | The username of the user. | `alice` | +| `name` | The full name of the user. | `Alice Smith` | +| `groups` | The groups the user belongs to. Separate multiple groups with a comma. | `['sudo', 'docker']` | +| `authorized_keys.public_key` | The authorized public SSH key that can connect to the server for this user. | `ssh-ed25519 AAA...d1FKVvu alice` | +| `password` | The encrypted password for the user. Generate an encrypted hash with [`spin mkpasswd`](/docs/command-reference/mkpasswd). The user will be prompted to change their password on first login. The password will only be used if `use_passwordless_sudo` is set to `false`. | `$6$mysecretsalt$...` | + +### Setting your SSH public key +To get your SSH public key, look for files under your `~/.ssh` folder. The filename could be something like `id_ed25519.pub` or `id_rsa.pub`. Just make sure the file ends with `.pub`. + +Once you find the file, you can get the contents of the public key by running this command: + +::code-panel +--- +label: Get SSH public key +--- +```bash +# Change the filename if your key is named differently +cat ~/.ssh/id_ed25519.pub +``` +:: + +You should see something that looks like this: + +::code-panel +--- +label: Example SSH public key +--- +``` +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your_email@example.com +``` +:: + +Copy the entire line (including the "ssh-ed25519" part) and paste it into your `.spin.yml` file as shown in the example above. + +### Generating a new SSH key +If you need to generate a new SSH key, see our guide on [generating a secure SSH key](/docs/advanced/generating-a-secure-ssh-key). + +## Providers +::note +Providers are only required if you want Spin to automatically create servers. If you're using existing servers, see our guide on [using your own servers with Spin](/docs/providers/use-any-host). +:: + +The `providers:` section in your `.spin.yml` configures connections to supported cloud providers. Spin natively supports these providers: + +| Provider | Configuration Name | Environment Variable | +| -------- | ----------------- | ------------------- | +| [DigitalOcean](/docs/providers/digitalocean) | `digitalocean` | `DO_API_TOKEN` | +| [Hetzner](/docs/providers/hetzner) | `hetzner` | `HCLOUD_TOKEN` | +| [Vultr](/docs/providers/vultr) | `vultr` | `VULTR_API_KEY` | + +::note +API tokens grant powerful access to your cloud resources. For better security, you may consider storing your API tokens as environment variables on your local machine (using `.env` files or your preferred secure method) instead of directly in `.spin.yml`. +:: + +Click on any provider name above for detailed configuration instructions. + +## Hardware Profiles +The `hardware_profiles:` section defines the server resources and provider for each server. Each hardware profile references a provider and specifies the server's resources. + +Each hardware profile structure is slightly different depending on the provider. Click on a provider name above for detailed configuration instructions. + +## Servers +The `servers:` section defines the servers that Spin will create and manage. Each server requires a few key properties to determine its configuration. + +Here's an example of configuring two servers: + +::code-panel +--- +label: .spin.yml +--- +```yaml +servers: + - server_name: ubuntu-2gb-ash-1 + environment: production + hardware_profile: hetzner_2c_2gb_ubuntu2404 + + - server_name: ubuntu-1gb-ord-2 + environment: staging + hardware_profile: vultr_1c_1gb_ubuntu2404 +``` +:: + +| Property | Description | Example | Required | +| -------- | ----------- | ------- | -------- | +| `server_name` | A unique, descriptive name for your server. Use alphanumeric characters and hyphens only (no spaces or special characters). | `web-server-1` | Yes | +| `environment` | The deployment environment (`production`, `staging`, `development`). Must match an environment defined in your `.spin.yml` file. | `production` | Yes | +| `hardware_profile` | References a hardware profile defined in your `.spin.yml` that specifies the server's resources and provider. | `hetzner_2c_2gb_ubuntu2404` | Only if you intend to use a provider | +| `address` | The server's IP address or hostname. Leave blank for auto-assigned IPs - Spin will update this after provisioning.

**Using existing servers?** ⚠️ You must specify the IP address or hostname here. | `1.2.3.4` | Only if you are using existing servers | + +## Environments +Environments are managed under the `environments:` section in your `.spin.yml` file. You can define as many environments as you'd like or change the names of the environments. + +The most important thing to remember is the `environment` property in the `servers:` section must match an environment defined in your `.spin.yml` file. + +You can also set the `backups` property to `true` or `false` to enable or disable backups for the environment. If the `backups` property is set on a server, it will override the environment setting. + +::code-panel +--- +label: Example environments configuration in .spin.yml +--- +```yaml +############################################################## +# 🌎 Environments +############################################################## +environments: + - name: production + - name: staging + - name: development + backups: false +``` +:: + +## Advanced Server Configuration +We also give you a few other options to configure your servers that will configure the behavior for the [Spin Ansible Collection](https://github.com/serversideup/ansible-collection-spin). + +::code-panel +--- +label: .spin.yml Advanced Server Configuration +--- +```yaml +############################################################## +# 🤓 Advanced Server Configuration +############################################################## + +# Timezone and contact settings +server_timezone: "Etc/UTC" +server_contact: admin@example.com + +# If you the SSH port below, you may need to run `spin provision -p ` +# to get a connection on your first provision. Otherwise, SSH will try connecting +# to your new port before the SSH server configuration is updated. +ssh_port: "22" + +## You can set this to false to require a password for sudo. +## If you disable passwordless sudo, you must set a password for all sudo users. +## generate an encrypted hash with `spin mkpasswd`. Learn more: +## https://serversideup.net/open-source/spin/docs/command-reference/mkpasswd +use_passwordless_sudo: true + +## Email Notifications +postfix_hostname: "{{ inventory_hostname }}" + +## Set variables below to enable external SMTP relay +# postfix_relayhost: "smtp.example.com" +# postfix_relayhost_port: "587" +# postfix_relayhost_username: "myusername" +# postfix_relayhost_password: "mysupersecretpassword" + +## Deploy user customization - You can customize the deploy user below if you'd like +# docker_user: +# username: deploy +# authorized_ssh_keys: +# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNJGtd7a4DBHsQi7HGrC5xz0eAEFHZ3Ogh3FEFI2345 fake@key" +# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRfXxUZ8q9vHRcQZ6tLb0KwGHu8xjQHfYopZKLmnopQ anotherfake@key" +``` +:: + +## Encrypting your `.spin.yml` file +It is possible that if you want to encrypt your `.spin.yml` file for security reasons, you can use the [`spin vault`](/docs/command-reference/vault) command which will use Ansible Vault to securely encrypt your file. + +If you do choose to encrypt your `.spin.yml` file, just be sure to add the `.vault-password` file so Ansible can securely decrypt it when it runs. + +[Learn more about "spin vault" →](/docs/command-reference/vault) diff --git a/docs/content/docs/5.providers/0.digitalocean.md b/docs/content/docs/5.providers/0.digitalocean.md new file mode 100644 index 00000000..ea2c7439 --- /dev/null +++ b/docs/content/docs/5.providers/0.digitalocean.md @@ -0,0 +1,212 @@ +--- +head.title: 'Create a Server with DigitalOcean - Spin by Server Side Up' +title: 'DigitalOcean' +description: 'Learn how to create a server with DigitalOcean.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/providers/digitalocean +--- + +# Create a Server with DigitalOcean +::lead-p +DigitalOcean is one of the most popular cloud VPS providers in the development community. Many tools and services are built to support it. +:: + +## Create an account +The first step is to create an account with DigitalOcean. You can do this by going to their website and clicking the "Sign Up" button. With the link below, they offer $200 in credits. This is an affiliate link, meaning we get a small kickback if you sign up, but this doesn't affect our recommendations. + +[Sign Up with DigitalOcean →](https://m.do.co/c/f3bad4b927ca) + +## Create an API token +::note +This API token is very powerful and should be treated as a secret. Do not share this token and treat it like you would a password. +:: + +To create an API token, you'll need to sign in to the [DigitalOcean Console](https://cloud.digitalocean.com/). On the left sidebar, click on the "API" section. + +![Go to DigitalOcean API](/images/docs/digitalocean/api-sidebar.png) + +On the API page, click the "Generate New Token" button. + +![Generate DigitalOcean API Token](/images/docs/digitalocean/generate-token.png) + +You can set a number of settings for the token: + +- **Name**: Set this to something descriptive. +- **Expiration**: Set how long the token should be valid for. The shorter the value, the more secure, but the more often you'll need to generate a new token. +- **Scopes**: Select "Custom Scopes" for the most secure option. + +![DigitalOcean API Token Scopes](/images/docs/digitalocean/token-scopes.png) + +If you choose "Custom Scopes", you can select the following permissions: + +::note +The "Full Access" scope is the easiest option, but it gives the token access to everything. Choose what makes the most sense for your use case. Use the custom scopes below for the most secure option. +:: + +| Access Level | Scopes | Details | +|-------------|---------|---------| +| Fully Scoped Access | 10 scopes | - firewall (4): create, read, update, delete
- regions (1): read
- sizes (1): read
- ssh_key (4): create, read, update, delete | +| Create Access | 4 scopes | project / tag / image / droplet | +| Read Access | 5 scopes | snapshot / project / tag / image / droplet | +| Update Access | 3 scopes | project / image / droplet | +| **Total Custom Scopes** | **22 scopes** | | + + +## Place the token in your `.spin.yml` file +In your `.spin.yml` file, you'll find a `providers` section where you can add your DigitalOcean API token. + +::code-panel +--- +label: .spin.yml configure providers +--- +```yaml +############################################################## +# 👇 Providers - You must set at least one provider +############################################################## + +providers: + - name: digitalocean + api_token: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz +``` +:: + +## Set as DO_API_TOKEN environment variable +If you prefer to set the token as an environment variable, you can also set the `DO_API_TOKEN` variable in your `.env` file, secrets manager, or wherever you store your environment variables. + +## Configure users +Make sure you configure your system users in your `.spin.yml` file. + +[Learn more about users →](/docs/server-configuration/spin-yml-usage#users) + +::code-panel +--- +label: Example users configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + - username: alice + name: Alice Smith + groups: ['sudo'] + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" +``` +:: + +## Configuring a DigitalOcean hardware profile +By default we set you up with a very cost-effective hardware profile to get up and running quickly. + +::code-panel +--- +label: .spin.yml default Hardware Profiles +--- +```yaml +############################################################## +# 🤖 Hardware Profiles +############################################################## + +hardware_profiles: + # DigitalOcean + - name: digitalocean_1c_1gb_ubuntu2404 + provider: digitalocean + profile_config: + region: nyc3 + size: s-1vcpu-1gb + image: ubuntu-24-04-x64 + backups: true +``` +:: + +You can uncomment the defaults we give you or create/modify your own hardware profile. + +| Profile Config | Description | API Reference | +| ------------------- | ----------- | ------------------- | +| `region` | The data center and location you want to deploy your server to (pick the closest one to your customers). | See below for common examples. | +| `size` | The type of server. | See below for common examples. | +| `image` | The image to use for the server. | [DigitalOcean Images](https://docs.digitalocean.com/reference/api/api-reference/#tag/Images) | +| `backups` | Whether to enable snapshot backups for the server (may add 20% to the monthly price). | Learn more about [DigitalOcean snapshot backups](https://www.digitalocean.com/products/backups). | + +### Common Region Values +The `region` value is the `slug` of the region. You can find the full list of regions [here](https://docs.digitalocean.com/reference/api/api-reference/#tag/Regions). + +| Location | Country | Value | +|----------|---------|-------| +| New York 1 | USA | `nyc1` | +| New York 3 | USA | `nyc3` | +| San Francisco 3 | USA | `sfo3` | +| Amsterdam 3 | Netherlands | `ams3` | +| Singapore 1 | Singapore | `sgp1` | +| London 1 | UK | `lon1` | +| Frankfurt 1 | Germany | `fra1` | +| Toronto 1 | Canada | `tor1` | +| Bangalore 1 | India | `blr1` | +| Sydney 1 | Australia | `syd1` | + +### Common Size Values +::note +Make sure your server type is supported in the region you choose. Also, some of the pricing may change so be sure to check the latest pricing on the [DigitalOcean Droplet Pricing Page](https://www.digitalocean.com/pricing/droplets). +:: + +You can find the full list of server types [here](https://docs.digitalocean.com/reference/api/api-reference/#tag/Sizes). + +| Server Type | vCPUs | RAM | Storage | Traffic | Monthly Price (USD) | Description | +| ----------- | ----- | --- | ------- | ------- | ------------------ | ----------- | +| **Basic Droplets** | +| `s-1vcpu-1gb` | 1 | 1GB | 25GB | 1TB | $6.00 | Basic | +| `s-1vcpu-1gb-amd` | 1 | 1GB | 25GB | 1TB | $7.00 | Basic AMD | +| `s-1vcpu-1gb-intel` | 1 | 1GB | 25GB | 1TB | $7.00 | Basic Intel | +| `s-1vcpu-2gb` | 1 | 2GB | 50GB | 2TB | $12.00 | Basic | +| `s-2vcpu-2gb` | 2 | 2GB | 60GB | 3TB | $18.00 | Basic | +| `s-2vcpu-4gb` | 2 | 4GB | 80GB | 4TB | $24.00 | Basic | +| `s-2vcpu-4gb-amd` | 2 | 4GB | 80GB | 4TB | $28.00 | Basic AMD | +| `s-2vcpu-4gb-intel` | 2 | 4GB | 80GB | 4TB | $28.00 | Basic Intel | +| **CPU-Optimized Droplets** | +| `c-2` | 2 | 4GB | 25GB | 4TB | $42.00 | CPU-Optimized | +| `c-4` | 4 | 8GB | 50GB | 5TB | $84.00 | CPU-Optimized | +| `c-8` | 8 | 16GB | 100GB | 6TB | $168.00 | CPU-Optimized | +| `c-16` | 16 | 32GB | 200GB | 7TB | $336.00 | CPU-Optimized | + +## Configure your server +By default, we give you defaults to get up and running quickly, but you might want to make some changes. + +[Learn more about server configuration →](/docs/server-configuration/spin-yml-usage#servers) + +::code-panel +--- +label: Example server configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + - server_name: ubuntu-1gb-nyc-1 + environment: production + hardware_profile: digitalocean_1c_1gb_ubuntu2404 +``` +:: + +## Provision your server +Once you have everything ready, you can provision your server(s) by running this simple command: + +::code-panel +--- +label: Provision your server(s) +--- +```bash +spin provision +``` +:: + +This command will create your server(s) with DigitalOcean and configure your server(s) to be ready for deployment. There are many more options available when provisioning your server(s). To learn more see the guide below: + +[Learn more about "spin provision" →](/docs/command-reference/provision) + +## Getting ready for deployment +Now that you have a server ready, you'll want to learn how to get ready for deployment. Next steps are to choose a deployment strategy. + +[Learn which deployment strategy is right for you →](/docs/deployment/choosing-a-deployment-strategy) diff --git a/docs/content/docs/5.providers/0.hetzner.md b/docs/content/docs/5.providers/0.hetzner.md new file mode 100644 index 00000000..769d9012 --- /dev/null +++ b/docs/content/docs/5.providers/0.hetzner.md @@ -0,0 +1,188 @@ +--- +head.title: 'Create a Server with Hetzner - Spin by Server Side Up' +title: 'Hetzner' +description: 'Learn how to create a server with Hetzner.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/providers/hetzner +--- + +# Create a Server with Hetzner +::lead-p +Hetzner is a German-based company with data centers in the EU and the US. Their pricing is very competitive and they have incredible performance. +:: + +## Create an account +The first step is to create an account with Hetzner. You can do this by going to their website and clicking the "Sign Up" button. With the link below, they offer €⁠20 (~$21 USD) credits free which can get you a server for a few months. This is an affiliate link, meaning we get a small kickback if you sign up, but this doesn't affect our recommendations. + +[Sign Up with Hetzner →](https://hetzner.cloud/?ref=lhLUIrkdUPhl) + +## Create an API token +To create an API token, you'll need to sign in to the [Hetzner Cloud Console](https://console.hetzner.cloud/). Once signed in, select your project. + +![Select Hetzner Project](/images/docs/hetzner/select-project.png) + +Inside of the project, look for the "Security" page on the left, then click "API Tokens" at the top. + +![Hetzner API Tokens](/images/docs/hetzner/api-token-page.png) + +When you click "Generate API Token", you'll be presented with the option to select the scopes for the token. Select "Read & Write". + +![Hetzner API Token Scopes](/images/docs/hetzner/generate-token.png) + +::note +This API token is very powerful and should be treated as a secret. Do not share this token and treat it like you would a password. +:: + +## Place the token in your `.spin.yml` file +In your `.spin.yml` file, you'll find a `providers` section where you can add your Hetzner API token. + +::code-panel +--- +label: .spin.yml configure providers +--- +```yaml +############################################################## +# 👇 Providers - You must set at least one provider +############################################################## + +providers: + - name: hetzner + api_token: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz +``` +:: + +## Set as HCLOUD_TOKEN environment variable +If you prefer to set the token as an environment variable, you can also set the `HCLOUD_TOKEN` variable in your `.env` file, secrets manager, or wherever you store your environment variables. + +## Configure users +Make sure you configure your system users in your `.spin.yml` file. + +[Learn more about users →](/docs/server-configuration/spin-yml-usage#users) + +::code-panel +--- +label: Example users configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + - username: alice + name: Alice Smith + groups: ['sudo'] + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" +``` +:: + +## Configuring a Hetzner hardware profile +By default we set you up with a very cost-effective hardware profile to get up and running quickly. + +::code-panel +--- +label: .spin.yml default Hardware Profiles +--- +```yaml +############################################################## +# 🤖 Hardware Profiles +############################################################## + +hardware_profiles: + # Hetzner + - name: hetzner_2c_2gb_ubuntu2404 + provider: hetzner + profile_config: + location: ash + server_type: cpx11 + image: ubuntu-24.04 + backups: true +``` +:: + +You can uncomment the defaults we give you or create/modify your own hardware profile. + +| Profile Config | Description | API Reference | +| ------------------- | ----------- | ------------------- | +| `location` | The data center and location you want to deploy your server to (pick the closest one to your customers). | See below for common examples. | +| `server_type` | The type of server. | See below for common examples. | +| `image` | The image to use for the server. | [Hetzner Images](https://docs.hetzner.cloud/#images-get-all-images) | +| `backups` | Whether to enable snapshot backups for the server (may add 20% to the monthly price). | Learn more about [Hetzner snapshot backups](https://docs.hetzner.com/cloud/billing/faq#how-do-you-bill-for-snapshots-and-backups). | + +### Common Location Values +The `location` value is the `name` of the location. You can find the full list of locations [here](https://docs.hetzner.cloud/#locations-get-all-locations). + +| Location | Country | Value | +| -------------- | ------- | ------ | +| Falkenstein | Germany | `fsn1` | +| Nuremberg | Germany | `nbg1` | +| Helsinki | Finland | `hel1` | +| Ashburn, VA | USA | `ash` | +| Hillsboro, OR | USA | `hil` | +| Singapore | Singapore| `sin` | + +### Common Server Types +::note +Make sure your server type is supported in the location you choose. Also, some of the pricing may change so be sure to check the latest pricing on the [Hetzner Cloud Pricing Page](https://www.hetzner.com/cloud/). +:: + +You can find the full list of server types [here](https://docs.hetzner.cloud/#server-types-get-all-server-types). + +| Server Type | vCPUs | RAM | Storage | Traffic | Monthly Price (in EUR) | +| ----------- | ----- | --- | ------- | ------- | -------------- | +| **Shared CPU - AMD (CPX)** | +| `cpx11` | 2 | 2GB | 40GB | 1TB | €4.99 | +| `cpx21` | 3 | 4GB | 80GB | 2TB | €9.49 | +| `cpx31` | 4 | 8GB | 160GB | 3TB | €16.49 | +| `cpx41` | 8 | 16GB| 240GB | 4TB | €30.49 | +| `cpx51` | 16 | 32GB| 360GB | 5TB | €60.49 | +| **Dedicated CPU (CCX)** | +| `ccx13` | 2 | 8GB | 80GB | 1TB | €12.99 | +| `ccx23` | 4 | 16GB| 160GB | 2TB | €25.99 | +| `ccx33` | 8 | 32GB| 240GB | 3TB | €49.99 | +| `ccx43` | 16 | 64GB| 360GB | 4TB | €99.99 | +| `ccx53` | 32 | 128GB| 600GB | 6TB | €199.99 | + +## Configure your server +By default, we give you defaults to get up and running quickly, but you might want to make some changes. + +[Learn more about server configuration →](/docs/server-configuration/spin-yml-usage#servers) + +::code-panel +--- +label: Example server configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + - server_name: ubuntu-2gb-ash-1 + environment: production + hardware_profile: hetzner_2c_2gb_ubuntu2404 +``` +:: + + +## Provision your server +Once you have everything ready, you can provision your server(s) by running this simple command: + +::code-panel +--- +label: Provision your server(s) +--- +```bash +spin provision +``` +:: + +This command will create your server(s) with Hetzner and configure your server(s) to be ready for deployment. There are many more options available when provisioning your server(s). To learn more see the guide below: + +[Learn more about "spin provision" →](/docs/command-reference/provision) + +## Getting ready for deployment +Now that you have a server ready, you'll want to learn how to get ready for deployment. Next steps are to choose a deployment strategy. + +[Learn which deployment strategy is right for you →](/docs/deployment/choosing-a-deployment-strategy) diff --git a/docs/content/docs/5.providers/0.vultr.md b/docs/content/docs/5.providers/0.vultr.md new file mode 100644 index 00000000..d00dc975 --- /dev/null +++ b/docs/content/docs/5.providers/0.vultr.md @@ -0,0 +1,205 @@ +--- +head.title: 'Create a Server with Vultr - Spin by Server Side Up' +title: 'Vultr' +description: 'Learn how to create a server with Vultr.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/providers/vultr +--- + +# Create a Server with Vultr +::lead-p +Vultr is a US-based company that has a great reputation for performance and value. They have a wide range of data centers around the world and a great API for automation. +:: + +## Create an account +The first step is to create an account with Vultr. You can do this by going to their website and clicking the "Sign Up" button. With the link below, they offer $100 in credits. This is an affiliate link, meaning we get a small kickback if you sign up, but this doesn't affect our recommendations. + +[Sign Up with Vultr →](https://www.vultr.com/?ref=9627777-8H) + +## Create an API token +::note +This API token is very powerful and should be treated as a secret. Do not share this token and treat it like you would a password. +:: + +To create an API token, you'll need to sign in to the [Vultr Console](https://my.vultr.com/). Click on your name in the upper right, then click "API". + +![Go to Vultr API](/images/docs/vultr/api-link.png) + +On the API page, click the "Enable API" button. + +![Enable Vultr API](/images/docs/vultr/enable-api.png) + +You'll see your API token on the page. Press the "Copy" button to copy the token to your clipboard. + +![Vultr API Token](/images/docs/vultr/api-token.png) + +::note +This token is like a password that has full access to your account. If you want to limit the API key by IP address, you can do so by adjusting the "Access Control" settings on the API page. +:: + + +## Place the token in your `.spin.yml` file +In your `.spin.yml` file, you'll find a `providers` section where you can add your Vultr API token. + +::code-panel +--- +label: .spin.yml configure providers +--- +```yaml +############################################################## +# 👇 Providers - You must set at least one provider +############################################################## + +providers: + - name: vultr + api_token: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz +``` +:: + +## Set as VULTR_API_KEY environment variable +If you prefer to set the token as an environment variable, you can also set the `VULTR_API_KEY` variable in your `.env` file, secrets manager, or wherever you store your environment variables. + +## Configure users +Make sure you configure your system users in your `.spin.yml` file. + +[Learn more about users →](/docs/server-configuration/spin-yml-usage#users) + +::code-panel +--- +label: Example users configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + - username: alice + name: Alice Smith + groups: ['sudo'] + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" +``` +:: + +## Configuring a Vultr hardware profile +By default we set you up with a very cost-effective hardware profile to get up and running quickly. + +::code-panel +--- +label: .spin.yml default Hardware Profiles +--- +```yaml +############################################################## +# 🤖 Hardware Profiles +############################################################## + +hardware_profiles: + # Vultr + - name: vultr_1c_1gb_ubuntu2404 + provider: vultr + profile_config: + region: ord + plan: vc2-1c-1gb + os: "Ubuntu 24.04 LTS x64" + backups: true +``` +:: + +You can uncomment the defaults we give you or create/modify your own hardware profile. + +| Profile Config | Description | API Reference | +| ------------------- | ----------- | ------------------- | +| `region` | The data center and location you want to deploy your server to (pick the closest one to your customers). | See below for common examples. | +| `plan` | The type of server. | See below for common examples. | +| `os` | The image to use for the server. | [Vultr Images](https://www.vultr.com/api/#tag/images) | +| `backups` | Whether to enable snapshot backups for the server (may add 20% to the monthly price). | Learn more about [Vultr snapshot backups](https://docs.vultr.com/vps-automatic-backups). | + +### Common Region Values +The `region` value is the `id` of the region. You can find the full list of regions using the [Vultr API](https://www.vultr.com/api/#tag/region/operation/list-regions). + +| Location | Country | Value | +|----------|---------|-------| +| Amsterdam | Netherlands | `ams` | +| Atlanta | USA | `atl` | +| Bangalore | India | `blr` | +| Chicago | USA | `ord` | +| Dallas | USA | `dfw` | +| Frankfurt | Germany | `fra` | +| London | UK | `lhr` | +| Los Angeles | USA | `lax` | +| Mumbai | India | `bom` | +| New Jersey | USA | `ewr` | +| Paris | France | `cdg` | +| Seoul | South Korea | `icn` | +| Singapore | Singapore | `sgp` | +| Sydney | Australia | `syd` | +| Tokyo | Japan | `nrt` | +| Toronto | Canada | `yto` | + +### Common Plan Values + +You can find the full list of server types [here](https://www.vultr.com/api/#tag/plans/operation/list-plans). + +::note +These prices can change at anytime. Be sure to check the latest pricing on the [Vultr Pricing Page](https://www.vultr.com/pricing/). +:: + +| Server Type | vCPUs | RAM | Storage | Traffic | Monthly Price (USD) | Description | +| ----------- | ----- | --- | ------- | ------- | ------------------ | ----------- | +| **Regular Performance** | +| `vc2-1c-1gb` | 1 | 1GB | 25GB | 1TB | $5.00 | Basic SSD | +| `vc2-1c-2gb` | 1 | 2GB | 55GB | 2TB | $10.00 | Basic SSD | +| `vc2-2c-4gb` | 2 | 4GB | 80GB | 3TB | $20.00 | Basic SSD | +| `vc2-4c-8gb` | 4 | 8GB | 160GB | 4TB | $40.00 | Basic SSD | +| **High Performance (AMD/Intel)** | +| `vhp-1c-1gb` | 1 | 1GB | 25GB | 2TB | $6.00 | NVMe SSD | +| `vhp-1c-2gb` | 1 | 2GB | 50GB | 3TB | $12.00 | NVMe SSD | +| `vhp-2c-4gb` | 2 | 4GB | 100GB | 5TB | $24.00 | NVMe SSD | +| `vhp-4c-8gb` | 4 | 8GB | 180GB | 6TB | $48.00 | NVMe SSD | +| **CPU Optimized** | +| `vcp-2c-4gb` | 2 | 4GB | 75GB | 5TB | $40.00 | CPU Optimized | +| `vcp-4c-8gb` | 4 | 8GB | 150GB | 6TB | $80.00 | CPU Optimized | +| `vcp-8c-16gb` | 8 | 16GB | 300GB | 7TB | $160.00 | CPU Optimized | + +## Configure your server +By default, we give you defaults to get up and running quickly, but you might want to make some changes. + +[Learn more about server configuration →](/docs/server-configuration/spin-yml-usage#servers) + +::code-panel +--- +label: Example server configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + - server_name: ubuntu-1gb-ord-2 + environment: production + hardware_profile: vultr_1c_1gb_ubuntu2404 +``` +:: + +## Provision your server +Once you have everything ready, you can provision your server(s) by running this simple command: + +::code-panel +--- +label: Provision your server(s) +--- +```bash +spin provision +``` +:: + +This command will create your server(s) with Vultr and configure your server(s) to be ready for deployment. There are many more options available when provisioning your server(s). To learn more see the guide below: + +[Learn more about "spin provision" →](/docs/command-reference/provision) + +## Getting ready for deployment +Now that you have a server ready, you'll want to learn how to get ready for deployment. Next steps are to choose a deployment strategy. + +[Learn which deployment strategy is right for you →](/docs/deployment/choosing-a-deployment-strategy) diff --git a/docs/content/docs/5.providers/99.use-any-host.md b/docs/content/docs/5.providers/99.use-any-host.md new file mode 100644 index 00000000..4f396dd6 --- /dev/null +++ b/docs/content/docs/5.providers/99.use-any-host.md @@ -0,0 +1,170 @@ +--- +head.title: 'Use Any Host with Spin - Spin by Server Side Up' +title: 'Use Any Host' +description: 'Learn how to use your own servers with Spin.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/providers/use-any-host +--- + +# Use Any Host with Spin +::lead-p +Spin is designed to work self-hosting first, which makes it compatible with almost any host. This includes running servers on a major cloud provider down to an old computer running in your grandmother's basement 🤠. Check our [server requirements](/docs/server-configuration/server-requirements) for more details. +:: + +## Server Requirements +Make sure your server and host meet the minimum server requirements. + +[View the Spin Server Requirements →](/docs/server-configuration/server-requirements) + +## Ensure you're able to connect to your server via SSH +Since you'll have to take care of the set up manually, ensure you can connect to your server via SSH. + +::code-panel +--- +label: 'Confirm SSH access' +--- +```bash +# Replace 1.2.3.4 with your server's IP address +ssh root@1.2.3.4 +``` +:: + +## Configure users +Make sure you configure your system users in your `.spin.yml` file. + +[Learn more about users →](/docs/server-configuration/spin-yml-usage#users) + +::code-panel +--- +label: Example users configuration in .spin.yml +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + - username: alice + name: Alice Smith + groups: ['sudo'] + authorized_keys: + - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" +``` +:: + +## You can remove "providers" and "hardware_profiles" if you want +By default, the `.spin.yml` file includes some defaults for providers and hardware profiles. You can remove these if you want to use your own. + +::code-panel +--- +label: '❌ You can remove these lines if you want' +--- +```yaml +# ############################################################## +# # 👇 Providers - You must set at least one provider +# ############################################################## + +# providers: +# - name: digitalocean +# api_token: Set token here OR delete this line and set environment variable DO_API_TOKEN + +# - name: hetzner +# api_token: Set token here OR delete this line and set environment variable HCLOUD_TOKEN + +# - name: vultr +# api_token: Set token here OR delete this line and set environment variable VULTR_API_KEY + +# ############################################################## +# # 🤖 Hardware Profiles +# ############################################################## + +# hardware_profiles: +# # Hetzner +# - name: hetzner_2c_2gb_ubuntu2404 +# provider: hetzner +# profile_config: +# location: ash +# server_type: cpx11 +# image: ubuntu-24.04 +# backups: true + +# # Vultr +# - name: vultr_1c_1gb_ubuntu2404 +# provider: vultr +# profile_config: +# region: ord +# plan: vc2-1c-1gb +# os: "Ubuntu 24.04 LTS x64" +# backups: true + +# # DigitalOcean +# - name: digitalocean_1c_1gb_ubuntu2404 +# provider: digitalocean +# profile_config: +# region: nyc3 +# size: s-1vcpu-1gb +# image: ubuntu-24-04-x64 +# backups: true +``` +:: + +## Set your server address under "servers" +If you use our native providers with Spin, it will automatically create and populate the `address` property for you. Since in this case we're providing our own server, we'll need to set the `address` property for the server in the `.spin.yml` file. + +::code-panel +--- +label: '.spin.yml' +--- +```yaml +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + - server_name: my-awesome-server + environment: production + address: 1.2.3.4 # 👈 Be sure to set this to your server's IP address or DNS hostname + # hardware_profile: hetzner_2c_2gb_ubuntu2404 (❌ You can comment or delete this line out) +``` +:: + +You can use the IP address or hostname of your server. Feel free to remove the `hardware_profile` property if you do not plan to use any providers. + +## Provision your server +Once you have everything ready, you can provision your server(s) with the following command: + +::code-panel +--- +label: Provision your server(s) +--- +```bash +spin provision -u root +``` +:: + +::note +⚠️ Most cloud providers (DigitalOcean, Vultr, Hetzner) use `root` as the default user. If your server uses a different sudo user, replace `root` with that username. +:: + +To learn more about this process, see the guide below: + +[Learn more about "spin provision" →](/docs/command-reference/provision) + +### Troubleshooting SSH Connection +If you encounter connection issues, verify your SSH access first: + +::code-panel +--- +label: Verify SSH access +--- +```bash +ssh root@1.2.3.4 # Replace with your username and server IP +``` +:: + +You should be able to connect using SSH key authentication without being prompted for a password. + +## Getting ready for deployment +Now that you have a server ready, you'll want to learn how to get ready for deployment. Next steps are to choose a deployment strategy. + +[Learn which deployment strategy is right for you →](/docs/deployment/choosing-a-deployment-strategy) \ No newline at end of file diff --git a/docs/content/docs/6.deployment/1.choosing-a-deployment-strategy.md b/docs/content/docs/6.deployment/1.choosing-a-deployment-strategy.md new file mode 100644 index 00000000..d200a6b5 --- /dev/null +++ b/docs/content/docs/6.deployment/1.choosing-a-deployment-strategy.md @@ -0,0 +1,37 @@ +--- +head.title: 'Choosing a Deployment Strategy - Spin by Server Side Up' +title: 'Choosing a Deployment Strategy' +description: 'Learn how to choose a deployment strategy for your application with Spin.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/deployment/choosing-a-deployment-strategy +--- + +# Choosing a Deployment Strategy +::lead-p +Spin supports automated deployments and very simple deployments from your local machine. You can choose between these two strategies depending on your needs. +:: + +## Types of Deployments +There are two types of deployments: + +1. **Simple Deployments from your local machine** - We can use the [`spin deploy`](/docs/command-reference/deploy) command to deploy your application from your local machine to your server. +2. **Automated Deployments** - You can use our GitHub Actions to help you automatically deploy your application when you push to GitHub. + +| | spin deploy | GitHub Actions | +|-------------------------|----------------------|-------------------------| +| Difficulty | Very Easy | Basic Understanding of SSH & GitHub Actions required | +| Team Size | Solo-developers | Small to very large teams | +| Zero-downtime Deployments | ✅ | ✅ | +| Automated Deployments | ❌ | ✅ | + +Automated deployments are recommended for teams because they standardize the deployment process without human interaction. If you're a solo developer, you may find that [`spin deploy`](/docs/command-reference/deploy) is so easy to use you'll never need to learn the complexities of CI/CD or GitHub Actions. + +## Simple Deployments from your local machine +To learn more about our simple deployments from your local machine, see the guide below. + +[Simple Deployments from your local machine →](/docs/deployment/using-spin-deploy) + +## Automated Deployments with GitHub Actions + To learn more about our automated deployments, see the guide below. + +[Automated Deployments with GitHub Actions →](/docs/deployment/automated-deployments-with-github-actions) \ No newline at end of file diff --git a/docs/content/docs/4.advanced/1.zero-downtime-deployments-with-github-actions.md b/docs/content/docs/6.deployment/2.automated-deployments-with-github-actions.md similarity index 63% rename from docs/content/docs/4.advanced/1.zero-downtime-deployments-with-github-actions.md rename to docs/content/docs/6.deployment/2.automated-deployments-with-github-actions.md index 0061008b..c531a311 100644 --- a/docs/content/docs/4.advanced/1.zero-downtime-deployments-with-github-actions.md +++ b/docs/content/docs/6.deployment/2.automated-deployments-with-github-actions.md @@ -1,15 +1,19 @@ --- -head.title: 'Zero-downtime deployments with GitHub Actions - Spin by Server Side Up' -title: 'Zero-downtime Deployments with GitHub Actions' +head.title: 'Automated Deployments with GitHub Actions - Spin by Server Side Up' +title: 'Automated Deployments with GitHub Actions' description: 'Learn how to automate deployments with GitHub Actions.' layout: docs --- -# Zero-downtime deployments with GitHub Actions +# Automated Deployments with GitHub Actions ::lead-p Spin is compatible with any CI/CD that can build Docker images and run SSH commands on a remote server. In this guide, we cover the tools available to help make your deployments through GitHub Actions easier. :: +::note +**Looking for the easiest way to automated Laravel deployments?**

We offer a [Spin Pro template](https://getspin.pro) that is a "turn-key" solution for Laravel deployments. All of this will be automated for you and you won't need to spend any time learning the complexities of GitHub Actions. 😃 +:: + ## Important concepts **Zero-downtime deployments highly depend on your configuration with Docker.** For a zero-downtime deployment to work, there are many things that need to align in order for this to happen: @@ -23,16 +27,10 @@ Spin is compatible with any CI/CD that can build Docker images and run SSH comma Spin's official templates provide containers that are ready for zero-downtime deployments out of the box. -::responsive-image ---- -src: /images/docs/github-actions/zero-downtime-deployment.png -alt: 'GitHub Actions: Zero-downtime Deployment' -maxWidth: 500 ---- -:: +![GitHub Actions: Zero-downtime Deployment](/images/docs/github-actions/zero-downtime-deployment.png) -## Spin's GitHub Actions -We provide GitHub Actions that are designed to work with Spin's structure for deployments. +## Open Source GitHub Actions +Even if you decide to not use the Spin Pro template, we offer our GitHub Actions as open source tools to help you deploy your application with Spin. - [serversideup/docker-build-action](https://github.com/marketplace/actions/docker-build-action) - A simplified syntax to build and publish your Docker images with GitHub Actions. - [serversideup/docker-swarm-deploy-github-action](https://github.com/marketplace/actions/docker-swarm-deploy-github-action) - A simplified syntax to deploy to Docker Swarm Mode via GitHub Actions. @@ -43,4 +41,9 @@ Each GitHub Action has it's own documentation on how you can implement it into y Be aware that you're taking a sensitive deployment key, putting that into GitHub actions, and allowing SSH connections from anywhere to connect to your production server. If you want to further harden your server, you may consider: - Deploying your own Self-hosted GitHub Runner -- Locking down SSH access to your server from specific IP addresses \ No newline at end of file +- Locking down SSH access to your server from specific IP addresses + +## The easiest way to deploy your Laravel application +If you're looking for the easiest way to deploy your Laravelapplication, we highly recommend using the [Spin Pro template](https://getspin.pro). This template includes a "turn-key" solution for Laravel deployments that is fully automated and requires minimal knowledge of GitHub Actions or Docker. + +[Learn more about Spin Pro →](https://getspin.pro) diff --git a/docs/content/docs/6.deployment/3.using-spin-deploy.md b/docs/content/docs/6.deployment/3.using-spin-deploy.md new file mode 100644 index 00000000..8f5db40c --- /dev/null +++ b/docs/content/docs/6.deployment/3.using-spin-deploy.md @@ -0,0 +1,26 @@ +--- +head.title: 'Using "spin deploy" - Spin by Server Side Up' +title: 'Using "spin deploy"' +description: 'Learn how to use the "spin deploy" command to deploy your application from your local machine to your server.' +layout: docs +--- + +# Using "spin deploy" +::lead-p +The `spin deploy` command is the easiest way to deploy your application without downtime while avoiding the complexities of learning CI/CD or GitHub Actions. +:: + +## When "spin deploy" is the right choice +We created `spin deploy` for the solo developer who wants an easy way to quickly deploy their application without the burden of learning CI/CD or GitHub Actions. If you're on a larger team or share your application with many developers, we highly recommend coming up with an [automated deployment strategy](/docs/deployment/automated-deployments-with-github-actions). + +## How "spin deploy" works +It's quite brilliant how `spin deploy` works. When you run this command, it will build your application locally with Docker, upload it to a temporary registry running on your machine, establish an SSH tunnel to your server, then have your server pull the new image from your local machine without any downtime. + +All of this is completed in one command without you needing to know how to configure Docker registries, CI/CD tools, or anything else. 🤯 + +![Spin Deploy](/images/docs/whats-spin/spin-deploy.png) + +## Learn more about "spin deploy" +To learn more about the `spin deploy` command, see the guide below. + +[Learn more about "spin deploy" →](/docs/command-reference/deploy) diff --git a/docs/content/docs/7.server-access/1.connecting-to-your-server.md b/docs/content/docs/7.server-access/1.connecting-to-your-server.md new file mode 100644 index 00000000..4514b66f --- /dev/null +++ b/docs/content/docs/7.server-access/1.connecting-to-your-server.md @@ -0,0 +1,41 @@ +--- +head.title: 'Connecting to Your Server - Spin by Server Side Up' +title: 'Connecting to Your Server' +description: 'Learn how to connect to your server.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/server-access/connecting-to-your-server +--- + +# Connecting to Your Server +::lead-p +Spin eliminates the headache of understanding the intricacies of configuring a secure SSH connection. After you provision your server, connecting and managing your server is a breeze. +:: + +## Connect to your server +To connect to your server, you can use the `spin ssh` command. + +::code-panel +--- +label: Connect to your server +--- +```bash +ssh @
+``` +:: + +If you use the same username on your host machine as the username on your server, you can omit the username from the command. + +## Deploying your application to your server +To deploy the application to your server, you can choose between different deployment strategies. + +[Learn more about deployment strategies →](/docs/deployment/choosing-a-deployment-strategy) + +## Troubleshooting your application +If you run into any issues with your application, we've compiled a guide to help you view and troubleshoot your application. + +[Troubleshooting your application →](/docs/server-access/troubleshooting-your-application) + +## Updating your server +Keeping your server up to date is important for security and performance. We've compiled a guide to show how easy it is to update your server. + +[Updating your server →](/docs/server-access/updating-your-server) diff --git a/docs/content/docs/7.server-access/2.troubleshooting-your-application.md b/docs/content/docs/7.server-access/2.troubleshooting-your-application.md new file mode 100644 index 00000000..a93ed30b --- /dev/null +++ b/docs/content/docs/7.server-access/2.troubleshooting-your-application.md @@ -0,0 +1,383 @@ +--- +head.title: 'Troubleshooting Your Application - Spin by Server Side Up' +title: 'Troubleshooting Your Application' +description: 'Learn how to troubleshoot your application.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/server-access/troubleshooting-your-application +--- + +# Troubleshooting Your Application +::lead-p +If you're reading this document, it probably means you're quite frustrated and looking for answers. In this guide, we'll give you some common troubleshooting tips and help you get back on track. +:: + +::note +Before running any of these commands, you'll need to [connect to your server](/docs/server-access/connecting-to-your-server). +:: + +## View Docker Services +Whenever you deploy a container with Docker, they are grouped into units called "services". Services could be things like databases, web servers, queues, etc. + +::code-panel +--- +label: View Docker services +--- +```bash +sudo docker service ls +``` +:: + +[Learn more about "docker service ls" →](https://docs.docker.com/reference/cli/docker/service/ls/) + +## View Docker Service Logs +When you list out your services, you can now view the logs of that service. + +::note +If you want to view the logs in real-time, you can use the `-f` flag. +:: + +::code-panel +--- +label: View Docker service logs +--- +```bash +sudo docker service logs +``` +:: + +[Learn more about "docker service logs" →](https://docs.docker.com/reference/cli/docker/service/logs/) + +## Running commands in a running container +Sometimes you might need to do some troubleshooting in a running container. To do this, we need to follow this process: + +1. Determine the *running* container ID for the service. +2. Use `docker exec` (with a TTY and interactive mode) attach to the shell of the running container. + +We will use [`docker ps`](https://docs.docker.com/reference/cli/docker/ps/) to get the container ID, then use [`docker exec`](https://docs.docker.com/reference/cli/docker/exec/) to open a shell to the container. + +::code-panel +--- +label: Open a shell to a running container +--- +```bash +# Get the container ID of the running service +sudo docker ps --filter "name=" --format "{{.ID}}" + +# Run a command in the running container (you can replace "sh" with any command) +# but by default, this will open a shell to the container (type "exit" to close) +sudo docker exec -it sh +``` +:: + +This is a two step process, but if you want to do this in a single command, we can use this trick below: + +::code-panel +--- +label: Open a shell to a running container (single command) +--- +```bash +# Replace with the name of your service. This will run `sh` in the first container ID it finds. +# You can change `sh` to `bash` if your container. Bash will give you a better experience. +sudo docker exec -it $(sudo docker ps --filter "name=" --format "{{.ID}}" | head -n 1) sh +``` +:: + +We're able to run this in a single command because we're using a subshell to get the container ID and then passing that to the `docker exec` command. + +If you don't need a shell and you want to run something like `php artisan migrate`, you would just replace `sh` with `php artisan migrate`. Just make sure your container is running commands relative to the directory you want to run the command in. Sometimes it's best to just open a shell and navigate to the directory you want to run the command in. + +## Viewing application logs +When you're viewing application logs, you might have multiple places to look depending on the configuration of your application and what output you're trying to look for. + +1. **Docker Swarm Service Logs** - The output here will be more server centric. Meaning you'll see deeper services logs such as PHP, FPM, Nginx, etc. +2. **Application Logs** - The output here will be more application centric. Meaning you'll see logs from your application itself. +3. **Error Tracking Services** - If you're using a logging service like Sentry or GlitchTip (which we highly recommend), you'll want to check those logs as well. These services will also alert you if there are any critical errors happening in your application. + +## Viewing Laravel Logs +If you don't use an error tracking service like Sentry or GlitchTip, Laravel 11 by default will log to the `storage/logs/laravel.log` file. You can access that by running a command like this: + +::code-panel +--- +label: View Laravel logs +--- +```bash +# Find the service name of your application +sudo docker service ls + +# Open a shell to the first container ID returned from your service +sudo docker exec -it $(sudo docker ps --filter "name=" --format "{{.ID}}" | head -n 1) sh + +# View the available log files +ls storage/logs/ + +# View the laravel.log file +cat storage/logs/laravel.log +``` +:: + +You can see it's a bit of a process to get to the log file, so that's why we recommend using an error tracking service like Sentry or GlitchTip which you can self-host for no cost if you wanted. + +## Enabling Debug Mode +Depending on the service you're troubleshooting, the official documentation for the Docker image you're using may have instructions on how to enable debug mode. + +For example, if you're running `serversideup/php:8.4-fpm-nginx`, [the official documentation](https://serversideup.net/open-source/docker-php/docs/reference/environment-variable-specification) talks about setting `LOG_OUTPUT_LEVEL=debug`. + +In this example, we would just send up a commit with the environment variable set. + +::note +Only use debug modes if you're actively troubleshooting an issue. Debug mode will log a lot of information to your logs which can slow down your application and use a lot of disk space. +:: + +::code-panel +--- +label: Set the LOG_OUTPUT_LEVEL docker-compose.prod.yml file +--- +```yml +services: + php: + image: ${SPIN_IMAGE_DOCKERFILE_PHP} + environment: + LOG_OUTPUT_LEVEL: debug +``` +:: + +## Services won't start or are not accessible +When services won't start or they are not accessible, they are usually broken down into two categories: + +1. The container or service fail to start +2. The container or service is running, but you can't access it through HTTP/HTTPS + +### Types of Health Checks +When you're running applications with zero-downtime deployments, everything depends on health checks. There are **multiple levels** of health checks, so make sure you reference above to determine where your issue might be: + +1. The Docker Swarm health check (the container or service fails to start) +2. The reverse proxy health check (the container or service is running, but you can't access it through HTTP/HTTPS) + +### Docker Health Check +The Docker health check is a health check that is more native to the container itself. Container authors can set [`HEALTHCHECK`](https://docs.docker.com/reference/dockerfile/#healthcheck) instructions in their `Dockerfile` to control the health of the container. + +Health checks can also be overridden by the Docker Swarm service configuration at with the [`healthcheck`](https://docs.docker.com/reference/compose-file/services/#healthcheck) option. + +One of the most frustrating things might be you try using the `docker service logs` command, but the service doesn't have any logs because the service doesn't exist yet. To get around this, we can use the [`docker service ps`](https://docs.docker.com/reference/cli/docker/service/ps/) command to get more information on why a container won't start. + +::code-panel +--- +label: Get more information on why a container won't start +--- +```bash +sudo docker service ps --no-trunc +``` +:: + +The above command will show a glimpse of why a container won't start. If everything looks normal there, you might want to inspect the service with [`docker service inspect`](https://docs.docker.com/reference/cli/docker/service/inspect/) to see if there are any other issues. + +::code-panel +--- +label: Inspect a Docker service +--- +```bash +sudo docker service inspect --pretty +``` +:: + +Adding the `--pretty` flag will show nice output of the service configuration and you can look for clues to see why the service might not be starting. + +::code-panel +--- +label: Example of a Docker health check failure +--- +```bash +task: non-zero exit (137): dockerexec: unhealthy container +``` +:: + +If you see an error message like the one above, this means something in the container is causing the Docker health check to fail. This could be caused by a start up script failing, a configuration issue, or something else. + +Start up scripts can be tricky to debug because if any of the scripts send a "non-zero exit" code, the Docker health check could fail. Make sure you're properly handling exit codes and testing your scripts in all scenarios. + +To get more information, put your container image in debug mode or add verbose logging and try again. You may get more information from `docker service logs` or `docker service inspect`. + +::note +If the container you're debugging is **serversideup/php**, you can set container image to debug mode by setting the `LOG_OUTPUT_LEVEL` environment variable to `debug` to get more information. +:: + +### Reverse Proxy Health Check +If you're trying to access your application through HTTP/HTTPS, but you're getting a 503 or 404 error, it's likely something is wrong with the reverse proxy health check. For our default configurations, we use [Traefik](https://doc.traefik.io/traefik/) as the reverse proxy, so we'll give you a few tips on how to proceed with troubleshooting. + +::note +The example below shows a **broken configuration**. Don't copy from it. Let's learn from it instead. 😃 +:: + +::code-panel +--- +label: Example of a broken configuration +--- +```yml +services: + php: + image: ${SPIN_IMAGE_DOCKERFILE} + environment: + SSL_MODE: "full" + deploy: + labels: + - "traefik.enable=true" + - "traefik.http.routers.my-php-app.rule=Host(`${SPIN_APP_DOMA1N}`)" + - "traefik.http.routers.my-php-app.entrypoints=websecure" + - "traefik.http.routers.my-php-app.tls=true" + - "traefik.http.routers.my-php-app.tls.certresolver=letsencryptresolver" + - "traefik.http.services.my-php-app.loadbalancer.server.port=8080" + - "traefik.http.services.my-php-app.loadbalancer.server.scheme=http" + # Health check + - "traefik.http.services.my-php-app.loadbalancer.healthcheck.path=/invalid" + - "traefik.http.services.my-php-app.loadbalancer.healthcheck.interval=30s" + - "traefik.http.services.my-php-app.loadbalancer.healthcheck.timeout=1s" + - "traefik.http.services.my-php-app.loadbalancer.healthcheck.scheme=http" +``` +:: + +#### Invalid Host +I have a typo in my Host rule: `Host(`${SPIN_APP_DOMA1N}`)"` + +Since the variable is `SPIN_APP_DOMAIN`, I have a typo and used `SPIN_APP_DOMA1N` instead. This would cause a `404` error because the host is invalid and the variable is undefined. + +To fix this I would need to fix my typo and ensure `SPIN_APP_DOMAIN` is properly defined. + +#### Invalid Port + Scheme +If I look at the documentation of [`serversideup/php`](https://serversideup.net/open-source/docker-php/docs/customizing-the-image/configuring-ssl), you can see I have `SSL_MODE: "full"` set in the service configuration. This tells the application to use HTTPS. + +However, there are 4 issues with the configuration because of this change: +- `server.port=8080`: The docs say this should be changed to `8443` when `SSL_MODE: "full"` is set. +- `server.scheme=http`: Since we're using HTTPS, this should be set to `https`. +- `healthcheck.timeout=1s`: This health check timeout is quite low. This could cause a health check failure if the application takes longer than 1 second to start. +- `healthcheck.scheme=http`: The health check definition is different from the server definition. The health check should use `https`. +- `healthcheck.path=/invalid`: If I don't have a page at `/invalid`, this will cause a health check failure. I can read the serversideup/php docs to set this to `/healthcheck` to use the native container health check endpoint or `/up` if I am running Laravel. + +Wow, you can see how many moving parts it takes to get zero-downtime deployments working. This process is true regardless of any service that you're running, so it's important to understand how each piece works together. + +Always remember: +- Start with the service, check to make sure your application is actually getting deployed +- Then check the reverse proxy configuration. +- The health check MUST return a `200` status code to pass. Things like `301` or `302` redirects can cause the health check to fail. +- Always refer to the official documentation of the Docker image you're using for looking for proper health check definitions. + +::note +If you want the "easy button" and you want a turn-key solution, look into [Spin Pro](https://getspin.pro). We include everything you need to deploy Laravel quickly, reliably, and easily. Everything we talk about above is configured for you automatically. +:: + +## Database connection issues +A common thing we also see are people who have connection issues. This is usually caused by: + +1. The database service isn't ready by the time a script in your other container tries to access it (controlled by container [start orders](https://docs.docker.com/compose/how-tos/startup-order/)). +2. The host is invalid, like `localhost` or `127.0.0.1`, instead of using the name of the database service in their Docker Compose file (like `postgres`). +3. The credentials in the `.env` file get changed and the application can't connect to the database. + +If you're provisioning a database like Postgres, MariaDB, or MySQL, they make an environment variable available called `POSTGRES_USER` and`POSTGRES_PASSWORD` (or similar) that you can use to connect to the database. + +::note +Any of these `*_USER` or `*_PASSWORD` variables will provision **on initialization only**. If you change your `.env` to use a new password, the database will not automatically update the password. +:: + +To fix this, you'll need to reference the documentation of your database engine to manually change the password. + +### Diagnosing connection issues between services +If you notice connection errors between services, make sure your `.env` file has the correct values. Special things to double check: + +- Ensure your `DB_HOST` (or comparable like `REDIS_HOST`) is set to the service name (ie. `mariadb`, `redis`, and NOT `127.0.0.1` or `localhost`) +- Ensure your environment variable values don't have any weird special characters in them. +- Ensure your `APP_URL` in `.env.` is set correctly (ie. `https://app.example.com`). +- Ensure any services have strong passwords set. Sometimes default passwords are rejected for security reasons. You can use a number of online tools to [generate a strong password for your service](https://www.random.org/strings/?num=100&len=26&digits=on&upperalpha=on&loweralpha=on&unique=on&format=html&rnd=new). + +## Stopping a service +If you need to stop a service, you can use the `docker service rm` command. For example, to stop a service named `my-service`, you can run: + +::code-panel +--- +label: "Stop a service" +--- +```bash +sudo docker service rm my-service +``` +:: + +Replace `my-service` with the name of the service you want to stop. + +## Removing volumes +Volumes are used to persist data in your containers. If you need to remove a volume, you can use the `docker volume rm` command. For example, to remove a volume named `my-volume`, you can run: + +::code-panel +--- +label: "Remove a volume" +--- +```bash +sudo docker volume rm my-volume +``` +:: + +Replace `my-volume` with the name of the volume you want to remove. You can find the volume name by running `sudo docker volume ls`. + +## Removing Swarm configurations +If you need to remove a Swarm configuration, you can use the `docker config rm` command. For example, to remove a configuration named `my-config`, you can run: + +::code-panel +--- +label: "Remove a configuration" +--- +```bash +sudo docker config rm my-config +``` +:: + +Replace `my-config` with the name of the configuration you want to remove. You can find the configuration name by running `sudo docker config ls`. + +## Removing networks +If you need to remove a network, you can use the `docker network rm` command. For example, to remove a network named `my-network`, you can run: + +::code-panel +--- +label: "Remove a network" +--- +```bash +sudo docker network rm my-network +``` +:: + +## Pruning unused resources +If you need to remove unused resources, you can use the `docker system prune` command. For example, to remove all stopped containers, unused networks, and dangling images, you can run: + +::code-panel +--- +label: "Prune unused resources" +--- +```bash +sudo docker system prune +``` +:: + +This command removes all stopped containers, unused networks, and dangling images. You can add the `--all` flag to remove all unused images as well. + +This will free up disk space on your server. + +## Starting over +We've all been there. The process of learning sometimes can start with a "redo". The good news is since everything is containerized, everything is designed to be **disposable and repeatable**. + +Since we're running Docker, we *do not* need to rebuild the server. We can simply remove the Docker data and start over with a fresh deployment. + +::note +The commands below will destroy all data. This means things like databases, logs, etc will all be deleted and you'll have a fresh Docker environment to deploy to. +:: + +::code-panel +--- +label: Delete all docker data and start over +--- +```bash +sudo docker service rm $(sudo docker service ls -q) # Remove all services +sudo docker stop $(sudo docker ps -aq) # Stop all containers +sudo docker rm $(sudo docker ps -aq) # Remove all containers +sudo docker system prune --all # Remove all unused images and networks +sudo docker volume rm $(sudo docker volume ls -q) # Remove all volumes +sudo docker config rm $(sudo docker config ls -q) # Remove all configurations +``` +:: + +Run the commands above individually to ensure everything is removed properly. Once you've verified everything is removed, you can deploy a fresh copy of your application. \ No newline at end of file diff --git a/docs/content/docs/7.server-access/3.updating-your-server.md b/docs/content/docs/7.server-access/3.updating-your-server.md new file mode 100644 index 00000000..70e88ce2 --- /dev/null +++ b/docs/content/docs/7.server-access/3.updating-your-server.md @@ -0,0 +1,52 @@ +--- +head.title: 'Updating Your Server - Spin by Server Side Up' +title: 'Updating Your Server' +description: 'Learn how to update your server.' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/server-access/updating-your-server +--- + +# Updating Your Server +::lead-p +Keeping your server up to date is important for security and performance. Spin makes it stupid-easy to update your server. +:: + +## Background +Running an out of date server stack can lead to security vulnerabilities and performance issues. Spin makes it easy to keep your server up to date. There are two categories of updates: + +- Application Stack Updates +- Server Updates + +`spin maintain` focuses on *server updates*. Here's a quick look at what that looks like: + +![Spin Maintain Command](/images/docs/whats-spin/spin-maintain.png) + +### Application Stack Updates +What we mean by application stack updates are things like Redis, Postgres, Node, PHP, etc. These are things you'll want to look at in your `docker-compose.*.yml`, `composer.json`, `package.json`, etc. Whenever you make a change, it will be version controlled so you can easily test and revert if needed. + +### Server Updates +Server updates are things like the underlying operating system, Docker, etc. It's important to keep these packages up to date as well. Thankfully Spin includes the [`spin maintain`](/docs/command-reference/maintain) command which will take care of the heavy lifting for you. + +::note +When you perform maintenance on your server, your server may experience brief downtime if it the updates require a reboot. Be sure to run this command during a communicated maintenance window. +:: + +::code-panel +--- +label: Upgrade your servers +--- +```bash +# Update all servers +spin maintain + +# Update a specific environment +spin maintain production +``` +:: + +The above command will: + +- Connect to your server(s) +- Update the all operating system packages +- Update Docker +- Reboot the server (if needed) diff --git a/docs/content/docs/4.advanced/2.generating-a-secure-ssh-key.md b/docs/content/docs/8.advanced/2.generating-a-secure-ssh-key.md similarity index 54% rename from docs/content/docs/4.advanced/2.generating-a-secure-ssh-key.md rename to docs/content/docs/8.advanced/2.generating-a-secure-ssh-key.md index 54c70584..32df3107 100644 --- a/docs/content/docs/4.advanced/2.generating-a-secure-ssh-key.md +++ b/docs/content/docs/8.advanced/2.generating-a-secure-ssh-key.md @@ -3,6 +3,7 @@ head.title: 'Generating a Secure SSH Key - Spin by Server Side Up' title: 'Generating a Secure SSH Key' description: 'Learn how to generate a secure SSH key.' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/advanced/generating-a-secure-ssh-key --- # Generating a Secure SSH Key @@ -18,7 +19,7 @@ A few things about that will make this key secure: If you want to nerd out on why we suggest this algorithm, you can [read this guide](https://blog.g3rt.nl/upgrade-your-ssh-keys.html). -## 👨‍💻 Generating a USER key (for server connection) +## 👨‍💻 Generating an SSH key ::note If you're using services like GitHub already, there's a good chance you already have a key like this. All you need is the **public key** value of your existing SSH key pair. You can likely find this in your `~/.ssh` directory, and look for files ending in `*.pub`. @@ -29,7 +30,7 @@ If you need to create an SSH key, you can run this on your local machine: ::code-panel --- -label: "Generate a USER key" +label: "Generate an SSH key" --- ```bash ssh-keygen -o -a 100 -t ed25519 @@ -58,27 +59,7 @@ cat ~/.ssh/id_ed25519.pub ``` :: -This will echo the public key value to your terminal. You can copy this value and use it in your `.spin.yml` [when you configure your "users.username.authorized_ssh_keys" for your server](/docs/getting-started/preparing-your-servers-for-spin#configure-other-server-settings). - -## 🚀 Generating a DEPLOYMENT key -If you're using advanced deployments like GitHub Actions, then you will want to create a deployment key specifically for your CI/CD pipeline. - -Run this on your local machine: - -::note -If you're asked to create a password, you can skip it. To keep things simple, we do not want GitHub Actions to be prompted to ask for a password during deployment. -:: - -::code-panel ---- -label: "Generate a deployment key on your Desktop" ---- -```bash -ssh-keygen -o -a 100 -t ed25519 -f ~/Desktop/id_ed25519_deploy -C deploy -``` -:: - -Change `deploy` to whatever you'd like. Since we do not want to use this key for our local connections, the command above defaults it to go to your desktop so you can easily find it and copy the values to your clipboard. +This will echo the public key value to your terminal. ## What to do with this key -Store this key in a secure place. You may need the private key contents during CI/CD and you'll need to set the public key value in your `.spin.yml` [when you configure your "docker_user.authorized_ssh_keys" for your server](/docs/getting-started/preparing-your-servers-for-spin#configure-server-settings). \ No newline at end of file +You can copy this value and use it in your `.spin.yml` [when you configure your "users.username.authorized_ssh_keys" for your server](/docs/server-configuration/spin-yml-usage#users). \ No newline at end of file diff --git a/docs/content/docs/4.advanced/3.adding-other-services.md b/docs/content/docs/8.advanced/3.adding-other-services.md similarity index 100% rename from docs/content/docs/4.advanced/3.adding-other-services.md rename to docs/content/docs/8.advanced/3.adding-other-services.md diff --git a/docs/content/docs/3.project-templates/99.create-your-own-template.md b/docs/content/docs/8.advanced/99.create-your-own-template.md similarity index 98% rename from docs/content/docs/3.project-templates/99.create-your-own-template.md rename to docs/content/docs/8.advanced/99.create-your-own-template.md index e87e8eb8..f35e8721 100644 --- a/docs/content/docs/3.project-templates/99.create-your-own-template.md +++ b/docs/content/docs/8.advanced/99.create-your-own-template.md @@ -26,13 +26,7 @@ spin new serversideup/spin-template-laravel-basic You can see in this case, we're just appending `serversideup/spin-template-laravel-basic` to the `spin new` command which will then prompt the user if they trust that repository to install. -::responsive-image ---- -src: /images/docs/community-templates/spin-new-community-template.png -alt: 'GitHub Actions: Zero-downtime Deployment' -maxWidth: 500 ---- -:: +![GitHub Actions: Zero-downtime Deployment](/images/docs/community-templates/spin-new-community-template.png) ## Structuring your repository You can check out the [serversideup/spin-template-laravel-basic](https://github.com/serversideup/spin-template-laravel-basic) project to get a feel for how to structure your repository. Here are some key points: diff --git a/docs/content/docs/8.advanced/migrating-from-spin-v2-to-v3.md b/docs/content/docs/8.advanced/migrating-from-spin-v2-to-v3.md new file mode 100644 index 00000000..f6b1aaf7 --- /dev/null +++ b/docs/content/docs/8.advanced/migrating-from-spin-v2-to-v3.md @@ -0,0 +1,450 @@ +--- +head.title: 'Migrating from Spin v2 to v3 - Spin by Server Side Up' +title: 'Migrating from Spin v2 to v3' +description: 'Learn how to migrate from Spin v2 to v3.' +layout: docs +canonical: 'https://serversideup.net/open-source/spin/docs/advanced/migrating-from-spin-v2-to-v3' +--- + +# Migrating from Spin v2 to v3 +::lead-p +Although Spin v3 doesn't ship with any breaking changes, there is a new structure for managing your configurations with Spin that you may want to upgrade to take advantage of. +:: + +::note +**Spin v3 ships with zero breaking changes for Spin v2 configurations.** This means these steps are completely optional and is only required if you want to take advantage of the new features with Spin (like using native providers to provision servers on DigitalOcean, Vultr, and Hetzner) +:: + +## The new ".spin.yml" file +In Spin v3, we introduced a new way to manage your server inventory. Previously, we had the configurations separated across multiple files (`.spin-inventory.ini` and `.spin.yml`). + +Everything has been merged into a single `.spin.yml` file. This new format gives you the ability to provision servers right from the command line with providers like DigitalOcean, Vultr, and Hetzner. + +::code-panel +--- +label: Example .spin.yml with v3 +--- +```yaml +############################################################## +# 👇 Users - You must set at least one user +############################################################## + +users: + # - username: alice + # name: Alice Smith + # groups: ['sudo'] + # authorized_keys: + # - public_key: "ssh-ed25519 AAAAC3NzaC1lmyfakeublickeyMVIzwQXBzxxD9b8Erd1FKVvu alice" + + # - username: bob + # name: Bob Smith + # state: present + # password: "$6$mysecretsalt$qJbapG68nyRab3gxvKWPUcs2g3t0oMHSHMnSKecYNpSi3CuZm.GbBqXO8BE6EI6P1JUefhA0qvD7b5LSh./PU1" + # groups: ['sudo'] + # shell: "/bin/bash" + # authorized_keys: + # - public_key: "ssh-ed25519 AAAAC3NzaC1anotherfakekeyIMVIzwQXBzxxD9b8Erd1FKVvu bob" + +############################################################## +# 👇 Providers - You must set at least one provider +############################################################## + +providers: +# - name: digitalocean +# api_token: Set token here OR delete this line and set environment variable DO_API_TOKEN + +# - name: hetzner +# api_token: Set token here OR delete this line and set environment variable HCLOUD_TOKEN + +# - name: vultr +# api_token: Set token here OR delete this line and set environment variable VULTR_API_KEY + +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + # - server_name: ubuntu-2gb-ash-1 + # environment: production + # hardware_profile: hetzner_2c_2gb_ubuntu2404 + + # - server_name: ubuntu-1gb-ord-2 + # environment: staging + # hardware_profile: vultr_1c_1gb_ubuntu2404 + +############################################################## +# 🤖 Hardware Profiles +############################################################## + +hardware_profiles: + # Hetzner + - name: hetzner_2c_2gb_ubuntu2404 + provider: hetzner + profile_config: + location: ash + server_type: cpx11 + image: ubuntu-24.04 + backups: true + + # Vultr + - name: vultr_1c_1gb_ubuntu2404 + provider: vultr + profile_config: + region: ord + plan: vc2-1c-1gb + os: "Ubuntu 24.04 LTS x64" + backups: true + + # DigitalOcean + - name: digitalocean_1c_1gb_ubuntu2404 + provider: digitalocean + profile_config: + region: nyc3 + size: s-1vcpu-1gb + image: ubuntu-24-04-x64 + backups: true + +############################################################## +# 🌎 Environments +############################################################## +environments: + - name: production + - name: staging + - name: development + +############################################################## +# 🤓 Advanced Server Configuration +############################################################## + +# Timezone and contact settings +server_timezone: "Etc/UTC" +server_contact: changeme@example.com + +# If you the SSH port below, you may need to run `spin provision -p ` +# to get a connection on your first provision. Otherwise, SSH will try connecting +# to your new port before the SSH server configuration is updated. +ssh_port: "22" + +## You can set this to false to require a password for sudo. +## If you disable passwordless sudo, you must set a password for all sudo users. +## generate an encrypted hash with `spin mkpasswd`. Learn more: +## https://serversideup.net/open-source/spin/docs/command-reference/mkpasswd +use_passwordless_sudo: true + +## Email Notifications +postfix_hostname: "{{ inventory_hostname }}" + +## Set variables below to enable external SMTP relay +# postfix_relayhost: "smtp.example.com" +# postfix_relayhost_port: "587" +# postfix_relayhost_username: "myusername" +# postfix_relayhost_password: "mysupersecretpassword" + +## Deploy user customization - You can customize the deploy user below if you'd like +# docker_user: +# username: deploy +# authorized_ssh_keys: +# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNJGtd7a4DBHsQi7HGrC5xz0eAEFHZ3Ogh3FEFI2345 fake@key" +# - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRfXxUZ8q9vHRcQZ6tLb0KwGHu8xjQHfYopZKLmnopQ anotherfake@key" +``` +:: + +## What's changed +We changed a few things regarding this new set up: +- The `.spin-inventory.ini` is no longer configured on new projects +- The `.spin.yml` file now manages both server settings and inventory (instead of just settings before) +- On project creation, the `.spin.yml` file is no longer included in the repository by default (it acts like an `.env` file) +- On project creation, you are no longer prompted to encrypt your `.spin.yml` file (you can still do this manually) +- `use_passwordless_sudo` is now set to `true` by default, allowing sudo users to become root without a password, but they are still authenticated by their SSH key. Keep this value to `false` if you'd like to require a password for sudo. + +## How to migrate a project to the new structure +You can continue to use your existing set up if you'd like, but if you want the new features, here is how you can take a Spin v2 project and set it up like a brand new Spin v3 project. + +### Make a backup +This is always a good idea when you're making big changes like this. It's better to have one than not 😃. Here's where you should make backups: + +- Make a copy of your local project directory +- Take a snapshot/backup of your production and staging servers +- Make sure you're on a clean branch within your project +- Plan and communicate to your users that there may be a brief interruption during the upgrade (running `spin provision` might update packages and cause 1-2 minutes of downtime) + +### Ensure your .gitignore is up to date +Essentially what we want to do, is remove `.spin.yml` from the Git repository (unless if you find a certain use case to keep it in there -- as long as it's encrypted with a secure password). + +::code-panel +--- +label: Ensure these exist in your .gitignore +--- +``` +.spin* +.vault-password +``` +:: + +### Ensure your ".env." files are up to date +Depending how you deploy, we may be re-uploading your `.env.` files to GitHub Actions. Make sure these files are the latest and have every value accurate to what is currently running in your environments. + +### Remove the ".spin-inventory.ini" file and ".spin.yml" file from the repository +We know what to remove the files from being tracked by Git. + +::code-panel +--- +label: Stop tracking these files in Git +--- +```bash +git rm --cached .spin-inventory.ini +git rm --cached .spin.yml +``` +:: + +### Decrypt your files so you can edit them +To make it easy for you, it's probably easiest to decrypt your files so you can easily edit them. + +::code-panel +--- +label: Decrypt your files +--- +```bash +spin vault decrypt .spin-inventory.ini +spin vault decrypt .spin.yml +``` +:: + +### Rename your ".spin.yml" file to ".spin.original.yml" +We just need to temporarily rename the file so we can reference it. + +### Download the new ".spin.yml" file +You can copy the contents of our example file from GitHub and paste into our new `.spin.yml` file. + +[View latest .spin.yml file on GitHub →](https://github.com/serversideup/ansible-collection-spin/blob/main/.spin.example.yml) + +### Migrate contents from your ".spin.original.yml" file +Move any setting you'd like, but especially do not forget about these: +- `server_timezone` +- `users` +- `server_contact` +- `use_passwordless_sudo` + +### Migrate the contents of your ".spin-inventory.ini" file +The other important thing is to move over our inventory from our `.spin-inventory.ini` file. To do this, let's say we have this example: + +::code-panel +--- +label: Example .spin-inventory.ini +--- +```ini +########################################### +# 👇 Basic Server Configuration - Set your server DNS or IP address +########################################### + +[production_manager_servers] +server01.example.com + +[staging_manager_servers] +server02.example.com + +########################################### +# 🤓 Advanced Environment Settings +########################################### +# Swarm Configuration +[swarm_managers:children] +production_manager_servers +staging_manager_servers + +# Environment +[production:children] +production_manager_servers + +[staging:children] +staging_manager_servers + +[all_servers:children] +production +staging +``` +:: + +The most important thing in the file is our "Basic Server Configuration" section. You can see this file has two servers, `server01.example.com` and `server02.example.com`. + +We want to move them into our `.spin.yml` file, so it looks like this: + +::code-panel +--- +label: Example .spin.yml with migrated inventory +--- +```yaml +############################################################## +# 👇 Servers - You must set at least one server +############################################################## + +servers: + - server_name: server01.example.com # ✅ You can set this to anything you want. It's just a label. + environment: production + # 👇 You MUST set this. Make sure it matches from your ".spin-inventory.ini" file + address: server01.example.com + # ❌ You can delete the line below if you're not using our native providers + # hardware_profile: hetzner_2c_2gb_ubuntu2404 + + # 👇 Here is a full example of "server02.example.com" without comments + - server_name: server02.example.com + environment: staging + address: server02.example.com +``` +:: + +### Remove the "providers" and "hardware_profiles" sections if you want +If you do not want the native providers to be used, you can remove the `providers` and `hardware_profiles` sections. As long as your server has an `address` set, Spin will use whatever host you'd like. + +::note +Keep these sections if you want to use the native providers. +:: + +::code-panel +--- +label: '❌ You can remove these lines if you want' +--- +```yaml +# ############################################################## +# # 👇 Providers - You must set at least one provider +# ############################################################## + +# providers: +# - name: digitalocean +# api_token: Set token here OR delete this line and set environment variable DO_API_TOKEN + +# - name: hetzner +# api_token: Set token here OR delete this line and set environment variable HCLOUD_TOKEN + +# - name: vultr +# api_token: Set token here OR delete this line and set environment variable VULTR_API_KEY + +# ############################################################## +# # 🤖 Hardware Profiles +# ############################################################## + +# hardware_profiles: +# # Hetzner +# - name: hetzner_2c_2gb_ubuntu2404 +# provider: hetzner +# profile_config: +# location: ash +# server_type: cpx11 +# image: ubuntu-24.04 +# backups: true + +# # Vultr +# - name: vultr_1c_1gb_ubuntu2404 +# provider: vultr +# profile_config: +# region: ord +# plan: vc2-1c-1gb +# os: "Ubuntu 24.04 LTS x64" +# backups: true + +# # DigitalOcean +# - name: digitalocean_1c_1gb_ubuntu2404 +# provider: digitalocean +# profile_config: +# region: nyc3 +# size: s-1vcpu-1gb +# image: ubuntu-24-04-x64 +# backups: true +``` +:: + +### Remove the v2 configuration files +Make sure to delete the old v2 files from the project when you're confident you've migrated everything. + +::code-panel +--- +label: Remove the files from the project +--- +```bash +rm .spin-inventory.ini +rm .spin.original.yml +``` +:: + +### Re-encrypt (if you want) +With this new set up, the `.spin.yml` file acts like an `.env` file. If you'd like the extra security, you can re-encrypt the file. + +::note +If you choose TO NOT encrypt the file, be sure to delete the `.vault-password` file from your local machine. +:: + +::code-panel +--- +label: Re-encrypt the file +--- +```bash +spin vault encrypt .spin.yml +``` +:: + +You will need a `.vault-password` file on your local machine if you intend to use the encrypted file. + +### Run spin provision +If you'd like to test the new setup, you can run `spin provision` and it will use the new `.spin.yml` file. + +::note +⚠️ Running `spin provision` might cause a brief interruption in your services if there is an update for Docker. +:: + +::code-panel +--- +label: Run spin provision on your staging servers +--- +```bash +spin provision staging +``` +:: + +### Update GitHub Actions +If you're using GitHub Actions, we no longer need these environment variables. They will be reuploaded to GitHub Actions under new names when we run `spin configure gha `. + +| Environment Variable | New Behavior | +| -------------------- | ------------ | +| `ENV_FILE_BASE64` | This has been renamed to `_ENV_FILE_BASE64`. It will be recreated with `spin configure gha `.| +| `SSH_REMOTE_HOSTNAME` | This has been renamed to `_SSH_REMOTE_HOSTNAME`. It will be recreated with `spin configure gha `. | +| `SSH_DEPLOY_PRIVATE_KEY` | You can keep this if you want. But if you do let it, we automatically create a new deploy key for you, store it under `.infrastructure/ci/SSH_DEPLOY_PRIVATE_KEY` and add it to GitHub Actions secrets. This process happens when you run `spin configure gha `. | + +::note +If you added other variables such as `DB_PASSWORD` or `REDIS_PASSWORD`, you can remove those from GitHub Actions if you're confident your `.env.` files are accurate. Spin v3 will use the values from your `.env.` files to configure these services. +:: + +### Run "spin configure gha " +For each environment, you will need to run `spin configure gha ` to update the GitHub Actions environment variables. + +::code-panel +--- +label: Run "spin configure gha " +--- +```bash +spin configure gha staging +spin configure gha production +``` +:: + +### Get latest Spin template +If you purchased Spin Pro, you can get the latest GitHub Actions template by reinitializing your project. + +::note +The command below will delete all Dockerfiles and Spin configurations then ask you to reinitialize your project. If you made a lot of customizations to the Dockerfiles and Spin templates, you way want to manually copy the GitHub Actions template over.

+**The links below are only accessible to Spin Pro customers.**
+- [action_deploy-production.yml](https://github.com/serversideup/spin-template-laravel-pro/blob/main/blocks/github-actions/.github/workflows/action_deploy-production.yml) +- [service_docker-build-and-publish.yml](https://github.com/serversideup/spin-template-laravel-pro/blob/main/blocks/github-actions/.github/workflows/service_docker-build-and-publish.yml) +:: + +::code-panel +--- +label: Reinitialize your project +--- +```bash +spin init laravel-pro +``` +:: + +## Review your pending Git changes +Now is the time to review your pending Git changes. If you're confident everything looks good, you can go ahead and commit your changes and run your deployment. + +Once the deployment is complete, you're ready for Spin v3 and all the exciting new features! 🎉 \ No newline at end of file diff --git a/docs/content/docs/4.command-reference/base64.md b/docs/content/docs/9.command-reference/base64.md similarity index 91% rename from docs/content/docs/4.command-reference/base64.md rename to docs/content/docs/9.command-reference/base64.md index f62e3a8c..91360c7f 100644 --- a/docs/content/docs/4.command-reference/base64.md +++ b/docs/content/docs/9.command-reference/base64.md @@ -3,6 +3,7 @@ head.title: 'base64 | Command Reference - Spin by Server Side Up' title: 'base64' description: 'Command reference for "spin base64"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/base64 --- # spin base64 ::lead-p diff --git a/docs/content/docs/4.command-reference/build.md b/docs/content/docs/9.command-reference/build.md similarity index 86% rename from docs/content/docs/4.command-reference/build.md rename to docs/content/docs/9.command-reference/build.md index 8ef633f0..5dce29e5 100644 --- a/docs/content/docs/4.command-reference/build.md +++ b/docs/content/docs/9.command-reference/build.md @@ -3,6 +3,7 @@ head.title: 'build | Command Reference - Spin by Server Side Up' title: 'build' description: 'Command reference for "spin build"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/build --- # spin build ::lead-p diff --git a/docs/content/docs/9.command-reference/configure.md b/docs/content/docs/9.command-reference/configure.md new file mode 100644 index 00000000..16bf1aad --- /dev/null +++ b/docs/content/docs/9.command-reference/configure.md @@ -0,0 +1,101 @@ +--- +head.title: 'configure | Command Reference - Spin by Server Side Up' +title: 'configure' +description: 'Command reference for "spin configure"' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/configure +--- +# spin configure +::lead-p +Configure various aspects of your project's deployment settings and infrastructure. +:: + +## Usage +::code-panel +--- +label: Usage for "spin configure" +--- +```bash +spin configure [options] +``` +:: + +## Commands + +### GitHub Actions (`gha`) +Configure GitHub Actions settings for deploying your application to a specific environment. + +::code-panel +--- +label: Configure GitHub Actions for an environment +--- +```bash +spin configure gha +``` +:: + +#### What this command does +When configuring GitHub Actions, this command: +1. Validates your project setup and GitHub repository connection +2. Creates or uses an existing deployment SSH key +3. Base64 encodes your environment file +4. Sets up required GitHub Actions secrets +5. Configures server access for deployments + +#### Example +::code-panel +--- +label: Configure GitHub Actions for production +--- +```bash +spin configure gha production +``` +:: + +## Prerequisites +Before running `spin configure gha`, ensure you: +- Have a GitHub repository set up (`git init` and connected to GitHub) +- Are authenticated with GitHub CLI (`spin gh auth login`) +- Have an environment file (e.g., `.env.production` for production) +- Have your server provisioned with `spin provision` + +## Environment Files +The command expects an environment file matching your target environment: +- Production: `.env.production` +- Staging: `.env.staging` +- etc. + +## GitHub Actions Secrets +The following environment variables are set as secrets in GitHub Actions. + +| Variable | Description | Example Value | Required | +| --- | --- | --- | --- | +| `_ENV_FILE_BASE64` | The base64 encoded `.env` file. | `ABCDEFG1234...` | ⚠️ Yes | +| `_SSH_REMOTE_HOSTNAME` | The hostname/IP of your server. | `server01.example.com` | ⚠️ Yes | +| `SSH_DEPLOY_PRIVATE_KEY` | The private SSH key dedicated for the deploy user. | `-----BEGIN OPENSSH PRIVATE KEY-----abc123...` | ⚠️ Yes | +| `SSH_REMOTE_KNOWN_HOSTS` | If provided, the SSH connection will validate the connection against your known_hosts file and remove the "SSH_KNOWN_HOST" warning. ([Learn more](https://github.com/serversideup/github-action-docker-swarm-deploy/?tab=readme-ov-file#removing-the-ssh_remote_known_hosts-warning)) | `github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC...` | no | +| `AUTHORIZED_KEYS` | Makes an `authorized_keys` file containing the public keys of "sudo" users that can be used for authenticating other services via SSH (like database GUI connections). | `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC...` | no | + +To view these environment variables in GitHub Actions, you can follow these steps: + +1. Go to your GitHub repository. +2. Click on `Settings`. +3. Click on `Secrets and variables → Actions`.` + +::responsive-image +--- +src: /images/docs/github-actions/gha-secrets.png +alt: 'Adding a new secret to GitHub Actions' +maxWidth: 500 +--- +:: + +::note +The only way you can update a value of a secret is to overwrite the previous value. GitHub Actions does not allow you to view the value of a secret once it's set. If you need to update a value, just run `spin configure gha ` again. +:: + +## Special Notes +- The command will create a new deployment key (under `.infrastructure/conf/ci`) if one doesn't exist +- The command will connect to your server and set the newly created deployment key as an authorized key for the docker/deploy user on your server +- All secrets are securely stored in your GitHub repository +- The command validates your setup before making any changes \ No newline at end of file diff --git a/docs/content/docs/4.command-reference/debug.md b/docs/content/docs/9.command-reference/debug.md similarity index 85% rename from docs/content/docs/4.command-reference/debug.md rename to docs/content/docs/9.command-reference/debug.md index 8d87f25b..b09887f9 100644 --- a/docs/content/docs/4.command-reference/debug.md +++ b/docs/content/docs/9.command-reference/debug.md @@ -3,6 +3,7 @@ head.title: 'debug | Command Reference - Spin by Server Side Up' title: 'debug' description: 'Command reference for "spin debug"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/debug --- # spin debug ::lead-p diff --git a/docs/content/docs/4.command-reference/deploy.md b/docs/content/docs/9.command-reference/deploy.md similarity index 91% rename from docs/content/docs/4.command-reference/deploy.md rename to docs/content/docs/9.command-reference/deploy.md index c6007ec8..cae80a88 100644 --- a/docs/content/docs/4.command-reference/deploy.md +++ b/docs/content/docs/9.command-reference/deploy.md @@ -3,6 +3,7 @@ head.title: 'deploy | Command Reference - Spin by Server Side Up' title: 'deploy' description: 'Command reference for "spin deploy"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/deploy --- # spin deploy ::lead-p @@ -22,7 +23,7 @@ spin deploy [ -c|--compose-file -p|--port -u|--user ## Before getting started Be sure you have "provisioned" your server before running this command. If you haven't, you can do so by running `spin provision` before running `spin deploy`. -[Learn how to prepare a server →](/docs/getting-started/preparing-your-servers-for-spin) +[Learn how to prepare a server →](/docs/server-configuration/server-configuration-basics) ## Example Deployment Command To deploy your application to the production environment, you can use the following command: @@ -66,6 +67,7 @@ The following options are available to set when running this command. | --- | --- | --- | --- | | `--compose-file` | `-c` | By default, we look for two files `docker-compose.yml, docker-compose.prod.yml` | The name of the compose files. You can provide many of these options to combine many files. | | `--port` | `-p` | `22` | The port to SSH into the server with. | +| `--upgrade` | `-U` | `false` | Force the upgrade of the Ansible collection. | | `--user` | `-u` | The username of your HOST machine (run `whoami` in a new terminal) | The user to SSH into the server with. | ## Change Options with Environment Variables @@ -75,7 +77,7 @@ You can also modify the behavior of the `spin deploy` command by setting environ | --- | --- | --- | | `SPIN_BUILD_PLATFORM` | `linux/amd64` | The platform to build the Docker image with. | | `SPIN_BUILD_TAGS` | `latest` | The tags to use when building the Docker image. | -| `SPIN_INVENTORY_FILE` | `.spin-inventory.ini` | The inventory file to use for the deployment. | +| `SPIN_INVENTORY_FILE` | `/etc/ansible/collections/ansible_collections/serversideup/spin/plugins/inventory/spin-dynamic-inventory.sh` | The inventory file or dynamic inventory script to use for the deployment. | | `SPIN_PROJECT_NAME` | `spin` | The name of the project to use for the deployment. | | `SPIN_REGISTRY_PORT` | `5080` | The port to use on your local machine for the temporary registry. | | `SPIN_TRAEFIK_CONFIG_FILE` | `./.infrastructure/conf/traefik/prod/traefik.yml` | The Traefik configuration file to use for the deployment. | @@ -115,7 +117,7 @@ The script sets default values for various deployment parameters, which can be o - `SPIN_BUILD_PLATFORM`: Platform for building the Docker image (default is "linux/amd64"). - `SPIN_BUILD_IMAGE_PREFIX`: Prefix for the Docker image name (default is "localhost:"). - `SPIN_BUILD_TAG`: Tag for the Docker image (default is "latest"). -- `SPIN_INVENTORY_FILE`: Path to the Ansible inventory file (default is "/ansible/.spin-inventory.ini"). +- `SPIN_INVENTORY_FILE`: Path to the Ansible inventory file or dynamic inventory script (default is "/etc/ansible/collections/ansible_collections/serversideup/spin/plugins/inventory/spin-dynamic-inventory.sh"). - `SPIN_SSH_PORT`: SSH port for connecting to the server. - `SPIN_SSH_USER`: SSH user for connecting to the server (default is "deploy"). - `SPIN_PROJECT_NAME`: Name of the project (default is "spin"). diff --git a/docs/content/docs/4.command-reference/down.md b/docs/content/docs/9.command-reference/down.md similarity index 86% rename from docs/content/docs/4.command-reference/down.md rename to docs/content/docs/9.command-reference/down.md index 5bc5279a..b2505347 100644 --- a/docs/content/docs/4.command-reference/down.md +++ b/docs/content/docs/9.command-reference/down.md @@ -3,6 +3,7 @@ head.title: 'down | Command Reference - Spin by Server Side Up' title: 'down' description: 'Command reference for "spin down"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/down --- # spin down ::lead-p diff --git a/docs/content/docs/4.command-reference/exec.md b/docs/content/docs/9.command-reference/exec.md similarity index 90% rename from docs/content/docs/4.command-reference/exec.md rename to docs/content/docs/9.command-reference/exec.md index 1ec42eb0..349f1efc 100644 --- a/docs/content/docs/4.command-reference/exec.md +++ b/docs/content/docs/9.command-reference/exec.md @@ -3,6 +3,7 @@ head.title: 'exec | Command Reference - Spin by Server Side Up' title: 'exec' description: 'Command reference for "spin exec"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/exec --- # spin exec ::lead-p diff --git a/docs/content/docs/9.command-reference/gh.md b/docs/content/docs/9.command-reference/gh.md new file mode 100644 index 00000000..a0342a42 --- /dev/null +++ b/docs/content/docs/9.command-reference/gh.md @@ -0,0 +1,27 @@ +--- +head.title: 'gh | Command Reference - Spin by Server Side Up' +title: 'gh' +description: 'Command reference for "spin gh"' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/gh +--- +# spin gh +::lead-p +Run the GitHub CLI via Docker with Spin. +:: + +## What this command does +This command will run the GitHub CLI via Docker using [`serversideup/github-cli`](https://github.com/serversideup/docker-github-cli). + +## Usage +::code-panel +--- +label: Usage for "spin gh" +--- +```bash +spin gh [OPTIONS] +``` +:: + +## Official Documentation +The `spin gh` command supports running all the commands supported by the GitHub CLI. See the [official documentation](https://cli.github.com/manual/) for more information. diff --git a/docs/content/docs/4.command-reference/help.md b/docs/content/docs/9.command-reference/help.md similarity index 79% rename from docs/content/docs/4.command-reference/help.md rename to docs/content/docs/9.command-reference/help.md index e7b9446b..c14a2bd8 100644 --- a/docs/content/docs/4.command-reference/help.md +++ b/docs/content/docs/9.command-reference/help.md @@ -3,6 +3,7 @@ head.title: 'help | Command Reference - Spin by Server Side Up' title: 'help' description: 'Command reference for "spin help"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/help --- # spin help ::lead-p diff --git a/docs/content/docs/4.command-reference/init.md b/docs/content/docs/9.command-reference/init.md similarity index 93% rename from docs/content/docs/4.command-reference/init.md rename to docs/content/docs/9.command-reference/init.md index f4c2496d..bf421c84 100644 --- a/docs/content/docs/4.command-reference/init.md +++ b/docs/content/docs/9.command-reference/init.md @@ -3,6 +3,7 @@ head.title: 'init | Command Reference - Spin by Server Side Up' title: 'init' description: 'Command reference for "spin init"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/init --- # spin init diff --git a/docs/content/docs/4.command-reference/kill.md b/docs/content/docs/9.command-reference/kill.md similarity index 83% rename from docs/content/docs/4.command-reference/kill.md rename to docs/content/docs/9.command-reference/kill.md index cd6f3614..49d743f8 100644 --- a/docs/content/docs/4.command-reference/kill.md +++ b/docs/content/docs/9.command-reference/kill.md @@ -3,6 +3,7 @@ head.title: 'kill | Command Reference - Spin by Server Side Up' title: 'kill' description: 'Command reference for "spin kill"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/kill --- # spin kill ::lead-p diff --git a/docs/content/docs/4.command-reference/latest.md b/docs/content/docs/9.command-reference/latest.md similarity index 92% rename from docs/content/docs/4.command-reference/latest.md rename to docs/content/docs/9.command-reference/latest.md index ed46cb67..c44411d8 100644 --- a/docs/content/docs/4.command-reference/latest.md +++ b/docs/content/docs/9.command-reference/latest.md @@ -3,6 +3,7 @@ head.title: 'latest | Command Reference - Spin by Server Side Up' title: 'latest' description: 'Command reference for "spin latest"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/latest --- # spin latest ::lead-p diff --git a/docs/content/docs/4.command-reference/logs.md b/docs/content/docs/9.command-reference/logs.md similarity index 87% rename from docs/content/docs/4.command-reference/logs.md rename to docs/content/docs/9.command-reference/logs.md index 52a32a1e..366cf17b 100644 --- a/docs/content/docs/4.command-reference/logs.md +++ b/docs/content/docs/9.command-reference/logs.md @@ -3,6 +3,7 @@ head.title: 'logs | Command Reference - Spin by Server Side Up' title: 'logs' description: 'Command reference for "spin logs"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/logs --- # spin logs ::lead-p diff --git a/docs/content/docs/9.command-reference/maintain.md b/docs/content/docs/9.command-reference/maintain.md new file mode 100644 index 00000000..2407ca99 --- /dev/null +++ b/docs/content/docs/9.command-reference/maintain.md @@ -0,0 +1,51 @@ +--- +head.title: 'maintain | Command Reference - Spin by Server Side Up' +title: 'maintain' +description: 'Command reference for "spin maintain"' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/maintain +--- +# spin maintain +::lead-p +Apply updates to your inventory of servers. +:: + +## Usage +::note +When you perform maintenance on your server, your server may experience brief downtime if it the updates require a reboot. Be sure to run this command during a communicated maintenance window. +:: + +::code-panel +--- +label: Usage for "spin maintain" +--- +```bash +spin maintain [environment] [ -p|--port -u|--user -U|--upgrade ] +``` +:: + +![Spin Maintain Command](/images/docs/whats-spin/spin-maintain.png) + +## Checklist before executing this command +Before you execute this command, you should have the following completed: + +- You should have a running **Ubuntu 22.04+ server** with properly configured SSH access +- The `.spin.yml` file should be configured + +## What this command does +The above command will: + +- Connect to your server(s) +- Update the all operating system packages +- Update Docker +- Reboot the server (if needed) + +## Options +The following options are available to set when running this command. +| Option | Short | Default | Description | +| --- | --- | --- | --- | +| `environment` | - | `all` | Optional. The target environment to maintain (e.g., `production`, `staging`). | +| `--host` | `-h` | | The hostname or group of hosts you'd like to apply updates to. | +| `--port` | `-p` | `22` | The port to SSH into the server with. | +| `--user` | `-u` | The username of your HOST machine (run `whoami` in a new terminal) | The user to SSH into the server with. | +| `--upgrade` | `-U` | Check for Ansible collection updates once per day. | Force upgrade the Ansible Collection on your machine before applying updates. | \ No newline at end of file diff --git a/docs/content/docs/4.command-reference/mkpasswd.md b/docs/content/docs/9.command-reference/mkpasswd.md similarity index 91% rename from docs/content/docs/4.command-reference/mkpasswd.md rename to docs/content/docs/9.command-reference/mkpasswd.md index c3a9197b..ca8e7c26 100644 --- a/docs/content/docs/4.command-reference/mkpasswd.md +++ b/docs/content/docs/9.command-reference/mkpasswd.md @@ -3,6 +3,7 @@ head.title: 'mkpasswd | Command Reference - Spin by Server Side Up' title: 'mkpasswd' description: 'Command reference for "spin mkpasswd"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/mkpasswd --- # spin mkpasswd ::lead-p diff --git a/docs/content/docs/4.command-reference/new.md b/docs/content/docs/9.command-reference/new.md similarity index 95% rename from docs/content/docs/4.command-reference/new.md rename to docs/content/docs/9.command-reference/new.md index 51c0883b..77f212ac 100644 --- a/docs/content/docs/4.command-reference/new.md +++ b/docs/content/docs/9.command-reference/new.md @@ -3,6 +3,7 @@ head.title: 'new | Command Reference - Spin by Server Side Up' title: 'new' description: 'Command reference for "spin new"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/new --- # spin new ::lead-p diff --git a/docs/content/docs/9.command-reference/provision.md b/docs/content/docs/9.command-reference/provision.md new file mode 100644 index 00000000..d93a7552 --- /dev/null +++ b/docs/content/docs/9.command-reference/provision.md @@ -0,0 +1,65 @@ +--- +head.title: 'provision | Command Reference - Spin by Server Side Up' +title: 'provision' +description: 'Command reference for "spin provision"' +layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/provision +--- +# spin provision +::lead-p +Provision and set up your inventory of servers. Before running this command, make sure you have everything configured and a supported server online with any host of your choice. +:: + +## Usage +::code-panel +--- +label: Usage for "spin provision" +--- +```bash +spin provision [environment] [ -p|--port -u|--user -U|--upgrade ] +``` +:: + +::note +If you're not using our native provider integration, you may need to add `-u root` to the command when you first run it. This will use the `root` user to connect and configure your other users. See our guide on [Using Any Host](/docs/providers/use-any-host) for more details. +:: + +## Checklist before executing this command +Before you execute this command, you should have the following completed: + +- Your [`.spin.yml` file](/docs/server-configuration/server-configuration-basics) should be configured +- You have at least one provider API token set **OR** you have a server with the `address` property set +- If you're using your own server (not through a provider), make sure you have `-u root` set in the command (or whatever your host's default sudo user is) +- If you're using a server with the `address` property, make sure you have SSH access to the server and it meets our [server requirements](/docs/server-configuration/server-requirements) + +## What happens when you run this command? +You can learn more [how servers work with Spin](/docs/server-configuration/server-configuration-basics#how-servers-work-with-spin) but depending on your set up, below shows what will happen when you run this command: + +### If you have a provider API token set +- Create your server(s) with your host +- Update your `.spin.yml` file with the actual IP address of the server that was just created +- Update your server to the latest Linux packages +- Configure the provider's firewall to only allow SSH and HTTP/S traffic and apply it to your server +- Configure your system users for server access +- Harden and secure your server against common attacks +- Install and configure Docker Swarm for zero-downtime deployments + +### If you have a server with the `address` property set +- Connect to your server using SSH +- Update your server to the latest Linux packages +- Configure your system users for server access +- Harden and secure your server against common attacks +- Install and configure Docker Swarm for zero-downtime deployments + +## Options +The following options are available to set when running this command. +| Option | Short | Default | Description | +| --- | --- | --- | --- | +| `environment` | - | `all` | Optional. The target environment to provision (e.g., `production`, `staging`). | +| `--host` | `-h` | | The hostname or group of hosts you'd like to provision. | +| `--port` | `-p` | `22` | The port to SSH into the server with. | +| `--user` | `-u` | The username of your HOST machine (run `whoami` in a new terminal) | The user to SSH into the server with. | +| `--upgrade` | `-U` | Check for Ansible collection updates once per day. | Force upgrade the Ansible Collection on your machine before provisioning. | + +## Learn More +[Configuring your servers for "spin provision" →](/docs/server-configuration/server-configuration-basics) \ No newline at end of file diff --git a/docs/content/docs/4.command-reference/prune.md b/docs/content/docs/9.command-reference/prune.md similarity index 67% rename from docs/content/docs/4.command-reference/prune.md rename to docs/content/docs/9.command-reference/prune.md index f2806723..acc26000 100644 --- a/docs/content/docs/4.command-reference/prune.md +++ b/docs/content/docs/9.command-reference/prune.md @@ -3,10 +3,11 @@ head.title: 'prune | Command Reference - Spin by Server Side Up' title: 'prune' description: 'Command reference for "spin prune"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/prune --- # spin prune ::lead-p -Clear the local Docker cache on your machine. +Clear the local Docker and Spin caches on your machine. :: ## Usage @@ -19,6 +20,12 @@ spin prune [OPTIONS] ``` :: +## Options +The following options are available to set when running this command. +| Option | Short | Default | Description | +| --- | --- | --- | --- | +| `--force` | `-f` | `false` | Force the deletion of the local Docker and Spin caches. | + ## Official Documentation & Additional Options This command is a shortcut for [`docker system prune --all`](https://docs.docker.com/engine/reference/commandline/system_prune/) and can accept additional options that you pass to it. Spin defaults the `--all` for you already, so no need to add that. diff --git a/docs/content/docs/4.command-reference/ps.md b/docs/content/docs/9.command-reference/ps.md similarity index 85% rename from docs/content/docs/4.command-reference/ps.md rename to docs/content/docs/9.command-reference/ps.md index 10e3cf3b..0053b81f 100644 --- a/docs/content/docs/4.command-reference/ps.md +++ b/docs/content/docs/9.command-reference/ps.md @@ -3,6 +3,7 @@ head.title: 'ps | Command Reference - Spin by Server Side Up' title: 'ps' description: 'Command reference for "spin ps"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/ps --- # spin ps ::lead-p diff --git a/docs/content/docs/4.command-reference/pull.md b/docs/content/docs/9.command-reference/pull.md similarity index 86% rename from docs/content/docs/4.command-reference/pull.md rename to docs/content/docs/9.command-reference/pull.md index 1177dfce..77aefa49 100644 --- a/docs/content/docs/4.command-reference/pull.md +++ b/docs/content/docs/9.command-reference/pull.md @@ -3,6 +3,7 @@ head.title: 'pull | Command Reference - Spin by Server Side Up' title: 'pull' description: 'Command reference for "spin pull"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/pull --- # spin pull ::lead-p diff --git a/docs/content/docs/4.command-reference/run.md b/docs/content/docs/9.command-reference/run.md similarity index 94% rename from docs/content/docs/4.command-reference/run.md rename to docs/content/docs/9.command-reference/run.md index da28175d..e1f6a4d3 100644 --- a/docs/content/docs/4.command-reference/run.md +++ b/docs/content/docs/9.command-reference/run.md @@ -3,6 +3,7 @@ head.title: 'run | Command Reference - Spin by Server Side Up' title: 'run' description: 'Command reference for "spin run"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/run --- # spin run ::lead-p diff --git a/docs/content/docs/4.command-reference/stop.md b/docs/content/docs/9.command-reference/stop.md similarity index 84% rename from docs/content/docs/4.command-reference/stop.md rename to docs/content/docs/9.command-reference/stop.md index dc35b7f8..0e3acd97 100644 --- a/docs/content/docs/4.command-reference/stop.md +++ b/docs/content/docs/9.command-reference/stop.md @@ -3,6 +3,7 @@ head.title: 'Stop | Command Reference - Spin by Server Side Up' title: 'stop' description: 'Command reference for "spin stop"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/stop --- # spin stop ::lead-p diff --git a/docs/content/docs/4.command-reference/up.md b/docs/content/docs/9.command-reference/up.md similarity index 97% rename from docs/content/docs/4.command-reference/up.md rename to docs/content/docs/9.command-reference/up.md index 6ea8a38d..832a4a83 100644 --- a/docs/content/docs/4.command-reference/up.md +++ b/docs/content/docs/9.command-reference/up.md @@ -3,6 +3,7 @@ head.title: 'up | Command Reference - Spin by Server Side Up' title: 'up' description: 'Command reference for "spin up"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/up --- # spin up ::lead-p diff --git a/docs/content/docs/4.command-reference/update.md b/docs/content/docs/9.command-reference/update.md similarity index 86% rename from docs/content/docs/4.command-reference/update.md rename to docs/content/docs/9.command-reference/update.md index 5eedaa9b..b12b3cc9 100644 --- a/docs/content/docs/4.command-reference/update.md +++ b/docs/content/docs/9.command-reference/update.md @@ -3,6 +3,7 @@ head.title: 'update | Command Reference - Spin by Server Side Up' title: 'update' description: 'Command reference for "spin update"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/update --- # spin update ::lead-p diff --git a/docs/content/docs/4.command-reference/vault.md b/docs/content/docs/9.command-reference/vault.md similarity index 97% rename from docs/content/docs/4.command-reference/vault.md rename to docs/content/docs/9.command-reference/vault.md index f0534949..b2d3d1ef 100644 --- a/docs/content/docs/4.command-reference/vault.md +++ b/docs/content/docs/9.command-reference/vault.md @@ -3,6 +3,7 @@ head.title: 'vault | Command Reference - Spin by Server Side Up' title: 'vault' description: 'Command reference for "spin vault"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/vault --- # spin vault ::lead-p diff --git a/docs/content/docs/4.command-reference/version.md b/docs/content/docs/9.command-reference/version.md similarity index 81% rename from docs/content/docs/4.command-reference/version.md rename to docs/content/docs/9.command-reference/version.md index fa610fea..cfcbf9be 100644 --- a/docs/content/docs/4.command-reference/version.md +++ b/docs/content/docs/9.command-reference/version.md @@ -3,6 +3,7 @@ head.title: 'version | Command Reference - Spin by Server Side Up' title: 'version' description: 'Command reference for "spin version"' layout: docs +canonical: https://serversideup.net/open-source/spin/docs/command-reference/version --- # spin version ::lead-p diff --git a/docs/content/hub.md b/docs/content/hub.md new file mode 100644 index 00000000..3a7f24d0 --- /dev/null +++ b/docs/content/hub.md @@ -0,0 +1,9 @@ +--- +head.title: 'Spin Hub - Docker Templates for Spin' +title: 'Spin Hub' +layout: hub +description: 'Browse Docker templates for Spin created by the Spin team and the community.' +--- + +::hub-main +:: \ No newline at end of file diff --git a/docs/content/docs/3.project-templates/0.laravel-basic.md b/docs/content/hub/0.laravel-basic.md similarity index 83% rename from docs/content/docs/3.project-templates/0.laravel-basic.md rename to docs/content/hub/0.laravel-basic.md index 0c47fa51..3963de0f 100644 --- a/docs/content/docs/3.project-templates/0.laravel-basic.md +++ b/docs/content/hub/0.laravel-basic.md @@ -1,21 +1,19 @@ --- head.title: 'Laravel Basic - Spin by Server Side Up' title: 'Laravel Basic' -description: 'Learn about the official Laravel template provided by the Spin community.' -layout: docs +description: 'A simple way to get up and running with Laravel + SQLite.' +image: '/images/docs/community-templates/spin-laravel-basic.png' +category: 'Laravel' +author: 'Server Side Up' +authorImage: '/images/docs/community-templates/serversideup.png' +layout: hubdetail --- # Laravel Basic ::lead-p The Spin community maintains an official Laravel template that you can use to get up and running with Laravel quickly. This template is designed to get you up and running with the latest stable version of Laravel with the default configurations running SQLite. :: -::responsive-image ---- -src: /images/docs/community-templates/spin-official-laravel-template.png -alt: 'Official Laravel Template by Spin' -maxWidth: 500 ---- -:: +![Official Laravel Basic Template by Spin](/images/docs/community-templates/spin-laravel-basic.png) ## Project Information Here's information where you can learn more about the project: diff --git a/docs/content/docs/3.project-templates/0.laravel-pro.md b/docs/content/hub/0.laravel-pro.md similarity index 80% rename from docs/content/docs/3.project-templates/0.laravel-pro.md rename to docs/content/hub/0.laravel-pro.md index 23cd54c0..429a8536 100644 --- a/docs/content/docs/3.project-templates/0.laravel-pro.md +++ b/docs/content/hub/0.laravel-pro.md @@ -1,21 +1,20 @@ --- head.title: 'Laravel Pro - Spin by Server Side Up' title: 'Laravel Pro' -description: 'Learn about the official Laravel template provided by the Spin community.' -layout: docs +description: 'An advanced Laravel template with support for Horizon, Reverb, and more.' +image: '/images/docs/community-templates/spin-laravel-pro.png' +author: 'Server Side Up' +category: 'Laravel' +authorImage: '/images/docs/community-templates/serversideup.png' +layout: hubdetail --- + # Laravel Pro ::lead-p The Spin team put together a "Spin Pro" template that is available for a one-time purchase for unlimited projects. You'll get access to running Laravel's most advanced features such as Horizon, Reverb, and more. :: -::responsive-image ---- -src: /images/docs/community-templates/spin-pro-laravel.png -alt: 'Official Laravel Template by Spin' -maxWidth: 500 ---- -:: +![Official Laravel Pro Template by Spin](/images/docs/community-templates/spin-laravel-pro.png) ## Project Information Here's information where you can learn more about the project: diff --git a/docs/content/docs/3.project-templates/0.nuxt.md b/docs/content/hub/0.nuxt.md similarity index 85% rename from docs/content/docs/3.project-templates/0.nuxt.md rename to docs/content/hub/0.nuxt.md index 0bb80285..e68d136c 100644 --- a/docs/content/docs/3.project-templates/0.nuxt.md +++ b/docs/content/hub/0.nuxt.md @@ -2,13 +2,19 @@ head.title: 'Nuxt Template - Spin by Server Side Up' title: 'Nuxt (Coming Soon)' description: 'Learn about the official Nuxt template provided by the Spin community.' -layout: docs +image: '/images/docs/community-templates/spin-nuxt.png' +author: 'Server Side Up' +authorImage: '/images/docs/community-templates/serversideup.png' +category: 'Nuxt' +layout: hubdetail --- # 🚧 Nuxt (coming soon) ::lead-p We're working on a template where you'll be able run your own Nuxt application easily. In the meantime, let us know what you'd like to see! :: +![Official Nuxt Template by Spin](/images/docs/community-templates/spin-nuxt.png) + ## What is Nuxt? [Nuxt](https://nuxt.com/) is a framework built on top of Vue.js that simplifies the development of modern web applications by offering server-side rendering (SSR), static site generation (SSG), and single-page applications (SPAs). It features automatic routing, code splitting, and a modular architecture, making it easy to build performant and SEO-friendly websites with minimal configuration. diff --git a/docs/layouts/hub.vue b/docs/layouts/hub.vue new file mode 100644 index 00000000..e5320c00 --- /dev/null +++ b/docs/layouts/hub.vue @@ -0,0 +1,61 @@ + + + \ No newline at end of file diff --git a/docs/layouts/hubdetail.vue b/docs/layouts/hubdetail.vue new file mode 100644 index 00000000..f801931c --- /dev/null +++ b/docs/layouts/hubdetail.vue @@ -0,0 +1,89 @@ + + + \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index d6b037b6..1c5dc576 100644 --- a/docs/package.json +++ b/docs/package.json @@ -24,6 +24,7 @@ "surge": "^0.23.1" }, "dependencies": { - "@heroicons/vue": "^2.1.1" + "@heroicons/vue": "^2.1.1", + "hotkeys-js": "^3.13.7" } } diff --git a/docs/public/images/docs/community-templates/serversideup.png b/docs/public/images/docs/community-templates/serversideup.png new file mode 100644 index 00000000..33fa5d8f Binary files /dev/null and b/docs/public/images/docs/community-templates/serversideup.png differ diff --git a/docs/public/images/docs/community-templates/spin-laravel-basic.png b/docs/public/images/docs/community-templates/spin-laravel-basic.png new file mode 100644 index 00000000..9ecb0c12 Binary files /dev/null and b/docs/public/images/docs/community-templates/spin-laravel-basic.png differ diff --git a/docs/public/images/docs/community-templates/spin-laravel-pro.png b/docs/public/images/docs/community-templates/spin-laravel-pro.png new file mode 100644 index 00000000..36807954 Binary files /dev/null and b/docs/public/images/docs/community-templates/spin-laravel-pro.png differ diff --git a/docs/public/images/docs/community-templates/spin-nuxt.png b/docs/public/images/docs/community-templates/spin-nuxt.png new file mode 100644 index 00000000..62879b9a Binary files /dev/null and b/docs/public/images/docs/community-templates/spin-nuxt.png differ diff --git a/docs/public/images/docs/community-templates/spin-official-laravel-template.png b/docs/public/images/docs/community-templates/spin-official-laravel-template.png deleted file mode 100644 index 6165c840..00000000 Binary files a/docs/public/images/docs/community-templates/spin-official-laravel-template.png and /dev/null differ diff --git a/docs/public/images/docs/community-templates/spin-pro-laravel.png b/docs/public/images/docs/community-templates/spin-pro-laravel.png deleted file mode 100644 index c0e3eb3c..00000000 Binary files a/docs/public/images/docs/community-templates/spin-pro-laravel.png and /dev/null differ diff --git a/docs/public/images/docs/digitalocean/api-sidebar.png b/docs/public/images/docs/digitalocean/api-sidebar.png new file mode 100644 index 00000000..401e2333 Binary files /dev/null and b/docs/public/images/docs/digitalocean/api-sidebar.png differ diff --git a/docs/public/images/docs/digitalocean/generate-token.png b/docs/public/images/docs/digitalocean/generate-token.png new file mode 100644 index 00000000..bbabefba Binary files /dev/null and b/docs/public/images/docs/digitalocean/generate-token.png differ diff --git a/docs/public/images/docs/digitalocean/token-scopes.png b/docs/public/images/docs/digitalocean/token-scopes.png new file mode 100644 index 00000000..097a7bac Binary files /dev/null and b/docs/public/images/docs/digitalocean/token-scopes.png differ diff --git a/docs/public/images/docs/github-actions/gha-secrets.png b/docs/public/images/docs/github-actions/gha-secrets.png new file mode 100644 index 00000000..9700a5c4 Binary files /dev/null and b/docs/public/images/docs/github-actions/gha-secrets.png differ diff --git a/docs/public/images/docs/hetzner/api-token-page.png b/docs/public/images/docs/hetzner/api-token-page.png new file mode 100644 index 00000000..f806db8a Binary files /dev/null and b/docs/public/images/docs/hetzner/api-token-page.png differ diff --git a/docs/public/images/docs/hetzner/generate-token.png b/docs/public/images/docs/hetzner/generate-token.png new file mode 100644 index 00000000..327aed02 Binary files /dev/null and b/docs/public/images/docs/hetzner/generate-token.png differ diff --git a/docs/public/images/docs/hetzner/select-project.png b/docs/public/images/docs/hetzner/select-project.png new file mode 100644 index 00000000..cbcc8617 Binary files /dev/null and b/docs/public/images/docs/hetzner/select-project.png differ diff --git a/docs/public/images/docs/vultr/api-link.png b/docs/public/images/docs/vultr/api-link.png new file mode 100644 index 00000000..13340b19 Binary files /dev/null and b/docs/public/images/docs/vultr/api-link.png differ diff --git a/docs/public/images/docs/vultr/api-token.png b/docs/public/images/docs/vultr/api-token.png new file mode 100644 index 00000000..3d8c53da Binary files /dev/null and b/docs/public/images/docs/vultr/api-token.png differ diff --git a/docs/public/images/docs/vultr/enable-api.png b/docs/public/images/docs/vultr/enable-api.png new file mode 100644 index 00000000..3b2e6f29 Binary files /dev/null and b/docs/public/images/docs/vultr/enable-api.png differ diff --git a/docs/public/images/docs/whats-spin/spin-deploy.png b/docs/public/images/docs/whats-spin/spin-deploy.png new file mode 100644 index 00000000..09801932 Binary files /dev/null and b/docs/public/images/docs/whats-spin/spin-deploy.png differ diff --git a/docs/public/images/docs/whats-spin/spin-maintain.png b/docs/public/images/docs/whats-spin/spin-maintain.png new file mode 100644 index 00000000..ebe5398a Binary files /dev/null and b/docs/public/images/docs/whats-spin/spin-maintain.png differ diff --git a/docs/public/images/docs/whats-spin/spin-provision.png b/docs/public/images/docs/whats-spin/spin-provision.png new file mode 100644 index 00000000..e8118cf7 Binary files /dev/null and b/docs/public/images/docs/whats-spin/spin-provision.png differ diff --git a/docs/yarn.lock b/docs/yarn.lock index 17320059..31bb345f 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -4284,6 +4284,11 @@ hosted-git-info@^7.0.0: dependencies: lru-cache "^10.0.1" +hotkeys-js@^3.13.7: + version "3.13.7" + resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.13.7.tgz#0188d8e2fca16a3f1d66541b48de0bb9df613726" + integrity sha512-ygFIdTqqwG4fFP7kkiYlvayZppeIQX2aPpirsngkv1xM1lP0piDY5QEh68nQnIKvz64hfocxhBaD/uK3sSK1yQ== + html-tags@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" diff --git a/lib/actions/base64.sh b/lib/actions/base64.sh index 799fa890..6a384c7c 100755 --- a/lib/actions/base64.sh +++ b/lib/actions/base64.sh @@ -9,14 +9,6 @@ action_base64() { local action=$1 local input=$2 - if [[ "$(uname -s)" == "Darwin" ]]; then - local base64_encode_cmd="base64 -i" - local base64_decode_cmd="base64 -D" - else - local base64_encode_cmd="base64" - local base64_decode_cmd="base64 -d" - fi - case "$action" in encode | -e) # Check if the file exists for encoding @@ -25,16 +17,16 @@ action_base64() { return 1 fi # Encode the file with base64 - $base64_encode_cmd "$input" + base64_encode "$input" ;; decode | -d) # Decode the input if [ -f "$input" ]; then # If it's a file, decode the file contents - cat "$input" | $base64_decode_cmd + base64_decode - < "$input" else # If it's not a file, assume it's a base64 string and try to decode it - echo "$input" | $base64_decode_cmd 2>/dev/null + echo "$input" | base64_decode - 2>/dev/null if [ $? -ne 0 ]; then echo "Error: Input is not a valid base64 string." return 1 diff --git a/lib/actions/configure.sh b/lib/actions/configure.sh new file mode 100644 index 00000000..d61314e9 --- /dev/null +++ b/lib/actions/configure.sh @@ -0,0 +1,220 @@ +#!/usr/bin/env bash + +################################# +# Main Action Handler +################################# +action_configure() { + + validate_project_setup + + case "$1" in + gha) + shift + configure_gha "$@" + ;; + *) + show_usage "$1" + exit 1 + ;; + esac + +} + +################################# +# Helper Functions +################################# +configure_gha() { + local deploy_public_key_content='' + local environment_file='' + + # Ensure environment is specified + if [ $# -eq 0 ]; then + echo "${BOLD}${RED}❌ No environment specified${RESET}" + echo "Usage: spin configure gha " + echo "Example: spin configure gha production" + return 1 + fi + + # Check if GitHub CLI image exists locally + if ! docker image inspect "$SPIN_GH_CLI_IMAGE" >/dev/null 2>&1; then + echo "${BOLD}${BLUE}🐳 Pulling GitHub CLI image...${RESET}" + if ! docker pull "$SPIN_GH_CLI_IMAGE"; then + echo "${BOLD}${RED}❌ Failed to pull GitHub CLI image${RESET}" + exit 1 + fi + fi + + # Set and validate environment + gha_environment="$1" + shift # Remove the first argument + gha_environment_uppercase=$(echo "$gha_environment" | tr '[:lower:]' '[:upper:]') + validate_github_repository_setup || exit 1 + environment_file=$(validate_environment_file "$gha_environment") || exit 1 + + # Set ENV_BASE_64 + gh_set_env --base64 --variable "${gha_environment_uppercase}_ENV_FILE_BASE64" --file "$environment_file" + + # Ensure deployment key exists + if [ ! -f "$SPIN_CI_FOLDER/SSH_DEPLOY_PRIVATE_KEY" ]; then + echo "🔑 Generating deployment key" + ssh-keygen -t ed25519 -C "deploy-key" -f "$SPIN_CI_FOLDER/SSH_DEPLOY_PRIVATE_KEY" -N "" + echo "${BOLD}${GREEN}✅ Deployment key generated${RESET}" + else + echo "🔑 Using existing deployment key found at \"$SPIN_CI_FOLDER/SSH_DEPLOY_PRIVATE_KEY\"" + fi + + deploy_public_key_content=$(cat "$SPIN_CI_FOLDER/SSH_DEPLOY_PRIVATE_KEY.pub") + + # Prepare CI variables with Ansible + echo "🔑 Preparing CI variables with Ansible" + prepare_ansible_run "$@" + run_ansible --allow-ssh --mount-path "$(pwd):/ansible" \ + ansible-playbook serversideup.spin.prepare_ci_environment \ + --inventory "$SPIN_INVENTORY_FILE" \ + --extra-vars @./.spin.yml \ + --extra-vars "spin_environment=$gha_environment" \ + --extra-vars "spin_ci_folder=$SPIN_CI_FOLDER" \ + --extra-vars "deploy_public_key='$deploy_public_key_content'" \ + "${SPIN_ANSIBLE_ARGS[@]}" \ + "${SPIN_UNPROCESSED_ARGS[@]}" + + echo "🔑 Adding GitHub Actions secrets..." + # Loop through all files in the CI folder (sorted alphabetically) + find "$SPIN_CI_FOLDER" -type f -maxdepth 1 | sort | while read -r filepath; do + file=$(basename "$filepath") + # Skip files with file extensions and .gitignore + if [[ "$file" != *.* ]]; then + # Convert filename to uppercase for secret name + secret_name=$(echo "$file" | tr '[:lower:]' '[:upper:]') + gh_set_env --variable "$secret_name" --file "$SPIN_CI_FOLDER/$file" + fi + done + + echo "${BOLD}${BLUE}🚀 You're now ready to push to deploy!${RESET}" +} + +gh_set_env() { + local base64_encode=false + local variable="" + local file="" + local value="" + + # Parse arguments + while [[ "$#" -gt 0 ]]; do + case "$1" in + --base64) + base64_encode=true + shift + ;; + --variable) + variable="$2" + shift 2 + ;; + --file) + file="$2" + shift 2 + ;; + --value) + value="$2" + shift 2 + ;; + *) + echo "${BOLD}${RED}❌ Invalid argument: $1${RESET}" + return 1 + ;; + esac + done + + # Validate required arguments + if [ -z "$variable" ] || { [ -z "$file" ] && [ -z "$value" ]; }; then + echo "${BOLD}${RED}❌ Missing required arguments. Need --variable and either --file or --value.${RESET}" + return 1 + fi + + if [ -n "$file" ] && [ -n "$value" ]; then + echo "${BOLD}${RED}❌ Cannot specify both --file and --value.${RESET}" + return 1 + fi + + # Get content from either file or value + local content + if [ -n "$file" ]; then + if [ "$base64_encode" = true ]; then + content=$(base64_encode "$file") + else + content=$(<"$file") + fi + else + if [ "$base64_encode" = true ]; then + content=$(echo -n "$value" | base64_encode -) + else + content="$value" + fi + fi + + # Set the secret using the gh CLI + echo "$content" | run_gh secret set "$variable" + + echo "${BOLD}${GREEN}✅ Successfully set $variable secret for GitHub Actions${RESET}" +} + +is_gh_cli_authenticated() { + run_gh auth status >/dev/null 2>&1 +} + +is_github_repository() { + run_gh repo view --json name >/dev/null 2>&1 +} + +show_usage() { + echo "${BOLD}${RED}❌ Invalid command: $1${RESET}" + echo + echo "Usage: spin configure " + echo + echo "Commands:" + echo " gha Configure GitHub Actions settings for specified environment" +} + +repository_exists() { + git rev-parse --is-inside-work-tree >/dev/null 2>&1 +} + +validate_environment_file() { + local gha_environment="$1" + local env_file=".env.$gha_environment" + + if [ -f "$env_file" ]; then + echo "$env_file" # Return the path if file exists + return 0 + else + echo "${BOLD}${RED}❌ Environment file not found ($env_file)${RESET}" >&2 + echo "Please ensure you have an environment variable file for the \"$gha_environment\" environment." >&2 + echo "Create a file called $env_file and add your environment variables to it." >&2 + echo "You can also change the environment by running \`spin configure gha \`." >&2 + return 1 + fi +} + +validate_github_repository_setup() { + if ! repository_exists; then + echo "${BOLD}${RED}❌ Repository not detected.${RESET}" + echo "Please ensure you're in the root of your project. If you need to create a repository, run \`git init\` then \`spin gh repo create\` to create one." + return 1 + fi + + if ! is_gh_cli_authenticated; then + echo "${BOLD}${RED}❌ GitHub CLI is not authenticated${RESET}" + echo + echo "Please authenticate with GitHub CLI by running \`spin gh auth login\`" + return 1 + fi + + if ! is_github_repository; then + echo "${BOLD}${RED}❌ Repository is not connected to GitHub.${RESET}" + echo "This project must be connected to a GitHub repository to use GitHub Actions." + echo "Add a GitHub remote or run \`spin gh repo create\` to create a GitHub repository." + return 1 + fi + + return 0 +} \ No newline at end of file diff --git a/lib/actions/deploy.sh b/lib/actions/deploy.sh index 4f0169b2..97925dfc 100755 --- a/lib/actions/deploy.sh +++ b/lib/actions/deploy.sh @@ -1,23 +1,147 @@ #!/usr/bin/env bash + +################################################################################ +# Helper functions +################################################################################ +cleanup_on_exit() { + local exit_code=$? + + if [ $exit_code -ne 0 ]; then + echo "❌ Failure detected. Cleaning up local services..." + fi + stop_registry + cleanup_ssh_tunnel + + exit $exit_code +} + +cleanup_ssh_tunnel() { + if [ -n "$tunnel_pid" ]; then + # Check if the process is still running + if ps -p "$tunnel_pid" > /dev/null; then + echo "Stopping local SSH tunnel..." + kill "$tunnel_pid" + echo "Local SSH tunnel stopped." + fi + fi +} + +deploy_docker_stack() { + local manager_host="$1" + local ssh_port="$2" + local compose_args=() + + # Set default compose files if none were provided + if [[ ${#compose_files[@]} -eq 0 ]]; then + compose_files=("docker-compose.yml" "docker-compose.prod.yml") + fi + + # Build the compose arguments + for compose_file in "${compose_files[@]}"; do + if [[ -n "$compose_file" ]]; then + # Compute MD5 hashes if necessary + generate_md5_hashes "$compose_file" + compose_args+=("--compose-file" "$compose_file") + fi + done + + local docker_host="ssh://$ssh_user@$manager_host:$ssh_port" + echo "${BOLD}${BLUE}📤 Deploying Docker stack with compose files: ${compose_files[*]} on $manager_host...${RESET}" + docker -H "$docker_host" stack deploy "${compose_args[@]}" --detach=false --prune "$spin_project_name-$deployment_environment" + if [ $? -eq 0 ]; then + echo "${BOLD}${BLUE}🎉 Successfully deployed Docker stack on $manager_host.${RESET}" + + # Clean up unused images + echo "${BOLD}${BLUE}🧹 Cleaning up unused Docker images on $manager_host...${RESET}" + if docker -H "$docker_host" image prune -f; then + echo "${BOLD}${BLUE}✨ Successfully cleaned up unused Docker images.${RESET}" + else + echo "${BOLD}${YELLOW}⚠️ Warning: Failed to clean up unused Docker images.${RESET}" + fi + else + echo "${BOLD}${RED}❌ Failed to deploy Docker stack on $manager_host.${RESET}" + exit 1 + fi +} + +generate_md5_hashes() { + # Check if the configs key exists + if grep -q 'configs:' "$compose_file"; then + # Extract config file paths + local config_files + config_files=$(awk '/configs:/{flag=1;next}/^[^ ]/{flag=0}flag' "$compose_file" | grep 'file:' | awk '{print $2}') + + for config_file_path in $config_files; do + if [ -f "$config_file_path" ]; then + local config_md5_hash + config_md5_hash=$(get_md5_hash "$config_file_path" | awk '{ print $1 }') + config_md5_var="SPIN_MD5_HASH_$(basename "$config_file_path" | tr '[:lower:]' '[:upper:]' | tr '.' '_')" + + eval "$config_md5_var=$config_md5_hash" + export $config_md5_var + fi + done + fi +} + +stop_registry() { + if docker ps -q -f name="$spin_registry_name" | grep -q .; then + echo "Stopping local Docker registry..." + docker stop "$spin_registry_name" >/dev/null 2>&1 + echo "Local Docker registry stopped." + fi +} + +################################################################################ +# Main deploy action +################################################################################ + action_deploy() { compose_files=() deployment_environment="" + deployment_environment_uppercase="" spin_registry_name="spin-registry" env_file="" + force_ansible_upgrade=false + + validate_project_setup - if is_encrypted_with_ansible_vault ".spin.yml" && \ - [ ! -f ".vault-password" ]; then - echo "${BOLD}${RED}❌Error: .spin.yml is encrypted with Ansible Vault, but '.vault-password' file is missing.${RESET}" - echo "${BOLD}${YELLOW}Please save your vault password in '.vault-password' in your project root and try again.${RESET}" - exit 1 - fi - - # First, find the deployment environment - for arg in "$@"; do - if [[ "$arg" != -* && -z "$deployment_environment" ]]; then - deployment_environment="$arg" - break - fi + # Process arguments + while [[ $# -gt 0 ]]; do + case "$1" in + --user | -u) + SPIN_SSH_USER="$2" + shift 2 + ;; + --compose-file | -c) + if [[ -n "$2" && "$2" != -* ]]; then + compose_files+=("$2") + shift 2 + else + echo "${BOLD}${RED}❌Error: '-c' option requires a Docker Compose file as argument.${RESET}" + exit 1 + fi + ;; + --port | -p) + SPIN_SSH_PORT="$2" + shift 2 + ;; + --upgrade|-U) + SPIN_FORCE_INSTALL_GALAXY_DEPS=true + shift + ;; + -*) + echo "${BOLD}${RED}❌Error: Unknown option $1${RESET}" + exit 1 + ;; + *) + # Only set deployment_environment if it hasn't been set yet + if [[ -z "$deployment_environment" ]]; then + deployment_environment="$1" + fi + shift + ;; + esac done # If no environment specified, default to production @@ -40,6 +164,9 @@ action_deploy() { echo "${BOLD}${YELLOW}Warning: Neither .env.$SPIN_DEPLOYMENT_ENVIRONMENT nor .env found. Proceeding with default values...${RESET}" fi + # Set an uppercase version of the deployment environment + deployment_environment_uppercase=$(echo "$deployment_environment" | tr '[:lower:]' '[:upper:]') + # Source the env file if it exists if [[ -n "$env_file" ]]; then set -a @@ -66,155 +193,21 @@ action_deploy() { # Set default values (can be overridden by .env file or command line arguments) registry_port="${SPIN_REGISTRY_PORT:-5080}" build_platform="${SPIN_BUILD_PLATFORM:-"linux/amd64"}" - image_prefix="${SPIN_BUILD_IMAGE_PREFIX:-"localhost:$registry_port"}" - image_tag="${SPIN_BUILD_TAG:-"latest"}" - inventory_file="${SPIN_INVENTORY_FILE:-"/ansible/.spin-inventory.ini"}" - ssh_port="${SPIN_SSH_PORT:-''}" + image_prefix="${SPIN_BUILD_IMAGE_PREFIX:-"127.0.0.1:$registry_port"}" + image_tag="${SPIN_BUILD_TAG:-$(date +%Y%m%d%H%M%S)}" + ssh_port="${SPIN_SSH_PORT:-22}" ssh_user="${SPIN_SSH_USER:-"deploy"}" spin_project_name="${SPIN_PROJECT_NAME:-"spin"}" - - # Process arguments - while [[ $# -gt 0 ]]; do - case "$1" in - --user | -u) - ssh_user="$2" - shift 2 - ;; - --compose-file | -c) - if [[ -n "$2" && "$2" != -* ]]; then - compose_files+=("$2") - shift 2 - else - echo "${BOLD}${RED}❌Error: '-c' option requires a Docker Compose file as argument.${RESET}" - exit 1 - fi - ;; - --port | -p) - ssh_port="$2" - shift 2 - ;; - *) - if [[ -z "$deployment_environment" ]]; then # capture the first positional argument as environment - deployment_environment="$1" - fi - shift - ;; - esac - done - - stop_registry() { - if docker ps -q -f name="$spin_registry_name" | grep -q .; then - echo "Stopping local Docker registry..." - docker stop "$spin_registry_name" >/dev/null 2>&1 - echo "Local Docker registry stopped." - fi - } - - cleanup_on_exit() { - local exit_code=$? - - if [ $exit_code -ne 0 ]; then - echo "Failure detected. Cleaning up local services..." - fi - stop_registry - cleanup_ssh_tunnel - - exit $exit_code - } - - cleanup_ssh_tunnel() { - if [ -n "$tunnel_pid" ]; then - # Check if the process is still running - if ps -p "$tunnel_pid" > /dev/null; then - echo "Stopping local SSH tunnel..." - kill "$tunnel_pid" - echo "Local SSH tunnel stopped." - fi - fi - } - - generate_md5_hashes() { - # Check if the configs key exists - if grep -q 'configs:' "$compose_file"; then - # Extract config file paths - local config_files - config_files=$(awk '/configs:/{flag=1;next}/^[^ ]/{flag=0}flag' "$compose_file" | grep 'file:' | awk '{print $2}') - - for config_file_path in $config_files; do - if [ -f "$config_file_path" ]; then - local config_md5_hash - config_md5_hash=$(get_md5_hash "$config_file_path" | awk '{ print $1 }') - config_md5_var="SPIN_MD5_HASH_$(basename "$config_file_path" | tr '[:lower:]' '[:upper:]' | tr '.' '_')" - - eval "$config_md5_var=$config_md5_hash" - export $config_md5_var - fi - done - fi - } - - deploy_docker_stack() { - local manager_host="$1" - local ssh_port="$2" - local compose_args=() - - # Set default compose files if none were provided - if [[ ${#compose_files[@]} -eq 0 ]]; then - compose_files=("docker-compose.yml" "docker-compose.prod.yml") - fi - - # Build the compose arguments - for compose_file in "${compose_files[@]}"; do - if [[ -n "$compose_file" ]]; then - # Compute MD5 hashes if necessary - generate_md5_hashes "$compose_file" - compose_args+=("--compose-file" "$compose_file") - fi - done - - local docker_host="ssh://$ssh_user@$manager_host:$ssh_port" - echo "${BOLD}${BLUE}📤 Deploying Docker stack with compose files: ${compose_files[*]} on $manager_host...${RESET}" - docker -H "$docker_host" stack deploy "${compose_args[@]}" --detach=false --prune "$spin_project_name-$deployment_environment" - if [ $? -eq 0 ]; then - echo "${BOLD}${BLUE}🎉 Successfully deployed Docker stack on $manager_host.${RESET}" - else - echo "${BOLD}${RED}❌ Failed to deploy Docker stack on $manager_host.${RESET}" - exit 1 - fi - } - - get_ansible_hosts() { - local host_group="$1" - local output - local exit_code - - # Run the Ansible command to get the list of hosts and capture both output and exit code - output=$(run_ansible --mount-path "$(pwd)" \ - ansible \ - "$host_group" \ - --inventory-file "$inventory_file" \ - --module-name ping \ - --list-hosts \ - $(set_ansible_vault_args) 2>&1) - exit_code=$? - - # Check for errors or no hosts - if echo "$output" | grep -q "No hosts matched, nothing to do" || [ $exit_code -ne 0 ]; then - echo "Error: Failed to retrieve hosts for group '$host_group'." >&2 - echo "Ansible output: $output" >&2 - return 1 - fi - - # Process and return the output if successful - echo "$output" | awk 'NR>1 {gsub(/\r/,""); print $1}' - } + registry_image="${SPIN_REGISTRY_IMAGE:-"registry:2"}" # Clean up services on exit trap cleanup_on_exit EXIT - # Check if any Dockerfiles exist - dockerfiles=$(ls Dockerfile* 2>/dev/null) - if [[ -n "$dockerfiles" ]]; then + # Check if any Dockerfiles exist (using safer glob handling) + shopt -s nullglob + dockerfiles=( Dockerfile* ) + shopt -u nullglob + if [[ ${#dockerfiles[@]} -gt 0 ]]; then # Bring up a local docker registry if [ -z "$(docker ps -q -f name=$spin_registry_name)" ]; then # Ensure the registry cache directory exists with the correct user and group ID @@ -222,66 +215,87 @@ action_deploy() { # Start the registry with the correct user and group ID echo "${BOLD}${BLUE}🚀 Starting local Docker registry...${RESET}" - docker run --rm -d -p "$registry_port:5000" --user "${SPIN_USER_ID}:${SPIN_GROUP_ID}" -v "$SPIN_CACHE_DIR/registry:/var/lib/registry" --name $spin_registry_name registry:2 + docker run --rm -d -p "$registry_port:5000" --user "${SPIN_USER_ID}:${SPIN_GROUP_ID}" -v "$SPIN_CACHE_DIR/registry:/var/lib/registry" --name "$spin_registry_name" "$registry_image" fi - # Prepare the Ansible run - check_galaxy_pull - # Build and push each Dockerfile - for dockerfile in $dockerfiles; do + for dockerfile in "${dockerfiles[@]}"; do # Generate variable name based on Dockerfile name - var_name=$(echo "$dockerfile" | tr '[:lower:].' '[:upper:]_') - var_name="SPIN_IMAGE_${var_name}" + spin_image_var_name=$(echo "$dockerfile" | tr '[:lower:].' '[:upper:]_') + spin_image_var_name="SPIN_IMAGE_${spin_image_var_name}" # Set and export image name - image_name="${image_prefix}/$(echo "$dockerfile" | tr '[:upper:]' '[:lower:]'):${image_tag}" - export "$var_name=$image_name" - - # Build the Docker image - echo "${BOLD}${BLUE}🐳 Building Docker image '$image_name' from '$dockerfile'...${RESET}" - docker buildx build --push --platform "$build_platform" -t "$image_name" -f "$dockerfile" . - if [ $? -eq 0 ]; then - echo "${BOLD}${BLUE}📦 Successfully built '$image_name' from '$dockerfile'...${RESET}" + full_docker_image_name="${image_prefix}/$(echo "$dockerfile" | tr '[:upper:]' '[:lower:]')" + versioned_image="${full_docker_image_name}:${image_tag}" + latest_image="${full_docker_image_name}:latest" + + # Export the versioned image name for other scripts to use + export "$spin_image_var_name=$versioned_image" + + # Build and tag the Docker image with both tags + echo "${BOLD}${BLUE}🐳 Building Docker image '$versioned_image' from '$dockerfile'...${RESET}" + if docker buildx build --push --platform "$build_platform" \ + -t "$versioned_image" \ + -t "$latest_image" \ + -f "$dockerfile" .; then + echo "${BOLD}${BLUE}📦 Successfully built '$versioned_image' from '$dockerfile'...${RESET}" else - echo "${BOLD}${RED}❌ Failed to build '$image_name' from '$dockerfile'.${RESET}" + echo "${BOLD}${RED}❌ Failed to build '$versioned_image' from '$dockerfile'.${RESET}" exit 1 fi done else - echo "${BOLD}${RED} No Dockerfiles found in the directory. Be sure you're running this command from the project root.${RESET}" - exit 1 + echo "${BOLD}${YELLOW}🐳 No Dockerfiles found in the directory. Skipping Docker image build...${RESET}" fi - # Prepare SSH connection - echo "${BOLD}${BLUE}⚡️ Setting up SSH tunnel to Docker registry...${RESET}" - - if [[ -n "$ssh_port" ]]; then - if ! ssh_port=$(get_ansible_variable "ssh_port"); then - echo "${BOLD}${RED}❌ Error: Failed to get SSH port from Ansible variables.${RESET}" >&2 - exit 1 - fi - echo " ℹ️ Using SSH port: $ssh_port" + # Get deployment host information + echo "${BOLD}${BLUE}📡 Getting deployment host information for \"$deployment_environment\"...${RESET}" + prepare_ansible_run "$@" + run_ansible --mount-path "$(pwd):/ansible" \ + ansible-playbook serversideup.spin.prepare_ci_environment \ + --inventory "$SPIN_INVENTORY_FILE" \ + --extra-vars @./.spin.yml \ + --extra-vars "spin_environment=$deployment_environment" \ + --extra-vars "spin_ci_folder=$SPIN_CI_FOLDER" \ + --tags "get-host,get-authorized-keys" \ + "${SPIN_ANSIBLE_ARGS[@]}" \ + "${SPIN_UNPROCESSED_ARGS[@]}" + + docker_swarm_manager=$(cat "$SPIN_CI_FOLDER/${deployment_environment_uppercase}_SSH_REMOTE_HOSTNAME") + + # Read and export authorized keys + if [[ -f "$SPIN_CI_FOLDER/AUTHORIZED_KEYS" ]]; then + # Read the file content and escape newlines for Docker + AUTHORIZED_KEYS=$(awk 1 ORS='\\n' "$SPIN_CI_FOLDER/AUTHORIZED_KEYS" | sed 's/\\n$//') + export AUTHORIZED_KEYS + echo "${BOLD}${BLUE} Authorized keys loaded and exported as AUTHORIZED_KEYS${RESET}" else - echo " ℹ️ Using default SSH port" + echo "${BOLD}${YELLOW}⚠️ Warning: No AUTHORIZED_KEYS file found in $SPIN_CI_FOLDER${RESET}" fi - swarm_manager_group="${SPIN_SWARM_MANAGER_GROUP:-"${deployment_environment}_manager_servers"}" - echo "${BOLD}${BLUE}🔍 Looking for swarm manager in group: $swarm_manager_group${RESET}" - - docker_swarm_manager=$(get_ansible_hosts "$swarm_manager_group" | head -n 1) - if [ $? -ne 0 ] || [ -z "$docker_swarm_manager" ]; then - echo "${BOLD}${RED}❌ Error: Failed to get a valid swarm manager host for group '$swarm_manager_group'.${RESET}" >&2 - echo "${BOLD}${RED}Please check if the environment '$deployment_environment' exists in '$(basename "$inventory_file")'.${RESET}" >&2 + echo "${BOLD}${RED}❌ Error: Failed to get a valid swarm manager host for group '$deployment_environment'.${RESET}" >&2 exit 1 else - echo "${BOLD}${GREEN}✅ Found swarm manager: $docker_swarm_manager${RESET}" + echo "${BOLD}${GREEN}✅ Deploying to Swarm Manager: $docker_swarm_manager${RESET}" fi # Create SSH tunnel to Docker registry echo "${BOLD}${BLUE}🚇 Creating SSH tunnel to Docker registry...${RESET}" - if ssh -f -n -N -R "${registry_port}:localhost:${registry_port}" -p "${ssh_port}" "${ssh_user}@${docker_swarm_manager}" -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -o ServerAliveCountMax=3; then + + # Build SSH command with proper quoting + ssh_cmd=( + ssh -f -n -N + -R "${registry_port}:127.0.0.1:${registry_port}" + -p "${ssh_port}" + "${ssh_user}@${docker_swarm_manager}" + -o ExitOnForwardFailure=yes + -o ServerAliveInterval=60 + -o ServerAliveCountMax=3 + -o StrictHostKeyChecking=accept-new + ) + + if "${ssh_cmd[@]}"; then echo "${BOLD}${GREEN}✅ SSH tunnel created successfully${RESET}" echo "${BOLD}${BLUE}ℹ️ Tunnel details:${RESET}" echo " 🔗 Local port: ${registry_port}" @@ -289,16 +303,17 @@ action_deploy() { echo " 🔌 Remote port: ${registry_port}" echo " 👤 SSH user: ${ssh_user}" echo " 🔢 SSH port: ${ssh_port}" - echo "${BOLD}${BLUE}🔄 The tunnel will forward connections from the remote port ${registry_port} to localhost:${registry_port}${RESET}" + echo "${BOLD}${BLUE}🔄 The tunnel will forward connections from the remote port ${registry_port} to 127.0.0.1:${registry_port}${RESET}" else - echo "${BOLD}${RED}❌ Failed to create SSH tunnel. Exiting...${RESET}" + ssh_exit_code=$? + echo "${BOLD}${RED}❌ Failed to create SSH tunnel (Exit code: $ssh_exit_code)${RESET}" echo "${BOLD}${YELLOW}🔧 Troubleshoot your connection by running:${RESET}" - echo "${BOLD}${YELLOW}ssh -p $ssh_port $ssh_user@$docker_swarm_manager${RESET}" + echo "${BOLD}${YELLOW}ssh -v -p ${ssh_port} ${ssh_user}@${docker_swarm_manager}${RESET}" exit 1 fi - # Get the process ID of the SSH tunnel - tunnel_pid=$(pgrep -f "ssh -f -n -N -R ${registry_port}:localhost:${registry_port}") + # Get the process ID of the SSH tunnel using POSIX-compliant commands + tunnel_pid=$(ps aux | grep "ssh -f -n -N -R ${registry_port}:127.0.0.1:${registry_port}" | grep -v grep | awk '{print $2}') echo "${BOLD}${BLUE}🚀 Deploying Docker stack...${RESET}" deploy_docker_stack "$docker_swarm_manager" "$ssh_port" diff --git a/lib/actions/gh.sh b/lib/actions/gh.sh new file mode 100644 index 00000000..cf61f50c --- /dev/null +++ b/lib/actions/gh.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +action_gh() { + # Run GH CLI via Docker + run_gh "$@" +} diff --git a/lib/actions/init.sh b/lib/actions/init.sh index 626b6b24..27e39a56 100755 --- a/lib/actions/init.sh +++ b/lib/actions/init.sh @@ -39,13 +39,14 @@ action_init() { absolute_project_directory=$(realpath "$SPIN_PROJECT_DIRECTORY") line_in_file --file "$absolute_project_directory/.gitignore" \ - ".vault-password" + ".vault-password" \ + ".spin.yml" line_in_file --file "$absolute_project_directory/.dockerignore" \ ".vault-password" \ ".github" \ ".git" \ ".infrastructure" \ - "!.infrastructure/conf/traefik/dev/certificates/ssu-ca.pem" \ + "!.infrastructure/**/local-ca.pem" \ "Dockerfile" \ "docker-*.yml" \ ".gitlab-ci.yml" \ @@ -54,9 +55,18 @@ action_init() { copy_template_files "$SPIN_TEMPLATE_TEMPORARY_SRC_DIR/template" "$absolute_project_directory" # Download default config and inventory from GitHub - get_file_from_github_release --repo "serversideup/ansible-collection-spin" --release-type "stable" --src ".spin-inventory.example.ini" --dest "$absolute_project_directory/.spin-inventory.ini" - get_file_from_github_release --repo "serversideup/ansible-collection-spin" --release-type "stable" --src ".spin.example.yml" --dest "$absolute_project_directory/.spin.yml" - prompt_to_encrypt_files --path "$absolute_project_directory" --file ".spin.yml" --file ".spin-inventory.ini" + if [[ "$SPIN_ANSIBLE_COLLECTION_NAME" == git+* ]]; then + # Parse git URL format: git+https://github.com/owner/repo.git,branch + local git_url="${SPIN_ANSIBLE_COLLECTION_NAME#git+}" + local repo="${git_url%%,*}" + repo="${repo#https://github.com/}" + repo="${repo%.git}" + local branch="${SPIN_ANSIBLE_COLLECTION_NAME##*,}" + + get_file_from_github_release --repo "$repo" --branch "$branch" --src ".spin.example.yml" --dest "$absolute_project_directory/.spin.yml" + else + get_file_from_github_release --repo "serversideup/ansible-collection-spin" --release-type "stable" --src ".spin.example.yml" --dest "$absolute_project_directory/.spin.yml" + fi # Check if the template has a post-install script and execute it if [ -f "$SPIN_TEMPLATE_TEMPORARY_SRC_DIR/post-install.sh" ]; then diff --git a/lib/actions/maintain.sh b/lib/actions/maintain.sh new file mode 100644 index 00000000..7f81e16d --- /dev/null +++ b/lib/actions/maintain.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +action_maintain(){ + validate_spin_yml + + echo "Preparing Ansible run..." >&2 + prepare_ansible_run "$@" + + # Check if there are any remaining unprocessed args + # If so, use the first one as the target environment + if [ ${#SPIN_UNPROCESSED_ARGS[@]} -gt 0 ]; then + SPIN_ANSIBLE_ARGS+=("--extra-vars" "target=${SPIN_UNPROCESSED_ARGS[0]}") + # Remove the first argument since we've processed it + SPIN_UNPROCESSED_ARGS=("${SPIN_UNPROCESSED_ARGS[@]:1}") + fi + + run_ansible --allow-ssh --mount-path "$(pwd):/ansible" \ + ansible-playbook serversideup.spin.maintain \ + --inventory "$SPIN_INVENTORY_FILE" \ + --extra-vars @./.spin.yml \ + "${SPIN_ANSIBLE_ARGS[@]}" \ + "${SPIN_UNPROCESSED_ARGS[@]}" +} \ No newline at end of file diff --git a/lib/actions/provision.sh b/lib/actions/provision.sh index fd344504..1420322e 100644 --- a/lib/actions/provision.sh +++ b/lib/actions/provision.sh @@ -1,61 +1,22 @@ #!/usr/bin/env bash action_provision(){ - additional_ansible_args=() - ansible_user="$USER" # Default to the current user - force_ansible_upgrade=false - unprocessed_args=() - - # Process arguments - while [[ "$#" -gt 0 ]]; do - case "$1" in - --user|-u) - ansible_user="$2" # Override default user with specified user - shift 2 - ;; - --port|-p) - additional_ansible_args+=("--extra-vars ansible_port=$2") - shift 2 - ;; - --upgrade|-U) - force_ansible_upgrade=true - export force_ansible_upgrade - shift - ;; - *) - unprocessed_args+=("$1") - shift - ;; - esac - done - - echo "Starting Ansible..." - # Check if the Docker image exists and pull if it doesn't - if ! docker image inspect ${SPIN_ANSIBLE_IMAGE} &> /dev/null; then - echo "Docker image ${SPIN_ANSIBLE_IMAGE} not found. Pulling..." - docker pull ${SPIN_ANSIBLE_IMAGE} - fi + validate_spin_yml - # Set Ansible User - additional_ansible_args+=("--extra-vars" "ansible_user=$ansible_user") - local use_passwordless_sudo - if ! use_passwordless_sudo=$(get_ansible_variable "use_passwordless_sudo"); then - echo "Failed to get ansible variable. Exiting." >&2 - exit 1 - fi - use_passwordless_sudo=${use_passwordless_sudo:-"false"} - if [ "$ansible_user" != "root" ] && [ "$use_passwordless_sudo" = 'false' ]; then - additional_ansible_args+=("--ask-become-pass") - fi + echo "Preparing Ansible run..." >&2 + prepare_ansible_run "$@" - # Append vault args to additional ansible args - IFS=' ' read -r -a vault_args < <(set_ansible_vault_args) - additional_ansible_args+=("${vault_args[@]}") + # Check if there are any remaining unprocessed args + # If so, use the first one as the target environment + if [ ${#SPIN_UNPROCESSED_ARGS[@]} -gt 0 ]; then + SPIN_ANSIBLE_ARGS+=("--extra-vars" "target=${SPIN_UNPROCESSED_ARGS[0]}") + # Remove the first argument since we've processed it + SPIN_UNPROCESSED_ARGS=("${SPIN_UNPROCESSED_ARGS[@]:1}") + fi - check_galaxy_pull - run_ansible --allow-ssh --mount-path $(pwd) \ + run_ansible --set-env --allow-ssh --mount-path "$(pwd):/ansible" \ ansible-playbook serversideup.spin.provision \ - --inventory ./.spin-inventory.ini \ + --inventory "$SPIN_INVENTORY_FILE" \ --extra-vars @./.spin.yml \ - "${additional_ansible_args[@]}" \ - "${unprocessed_args[@]}" + "${SPIN_ANSIBLE_ARGS[@]}" \ + "${SPIN_UNPROCESSED_ARGS[@]}" } \ No newline at end of file diff --git a/lib/actions/prune.sh b/lib/actions/prune.sh index 1e5f5438..ce554c81 100755 --- a/lib/actions/prune.sh +++ b/lib/actions/prune.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash action_prune(){ - echo "${BOLD}${YELLOW}🚨 You're about to delete some data.${RESET}" + # Check for force flag + if [[ ! "$*" =~ "-f"|"--force" ]]; then + echo "${BOLD}${YELLOW}🚨 You're about to delete some data.${RESET}" + fi + docker system prune --all $@ echo "${BOLD}${GREEN}✅ Docker cache cleared.${RESET}" diff --git a/lib/actions/vault.sh b/lib/actions/vault.sh index 27b500c3..27c5e81c 100644 --- a/lib/actions/vault.sh +++ b/lib/actions/vault.sh @@ -6,18 +6,18 @@ action_vault(){ "${vault_run_command[@]}" --help | sed 's/ansible-vault/spin vault/g' } - # Read the vault arguments into an array - read -r -a vault_args < <(set_ansible_vault_args) - # Check if ansible-vault is installed locally if [[ $(command -v ansible-vault) ]]; then vault_run_command=("ansible-vault") run_type="local" else - vault_run_command=("run_ansible" "--mount-path" "$(pwd)" "ansible-vault") + vault_run_command=("run_ansible" "--mount-path" "$(pwd):/ansible" "ansible-vault") run_type="docker" fi + # Read the vault arguments into an array + read -r -a vault_args < <(set_ansible_vault_args "$run_type") + # Check if any argument is '--help' for arg in "$@"; do if [[ "$arg" == "--help" ]]; then diff --git a/lib/functions.sh b/lib/functions.sh index cd881b49..0269384e 100755 --- a/lib/functions.sh +++ b/lib/functions.sh @@ -8,6 +8,24 @@ add_user_todo_item() { fi } +base64_encode() { + local input="$1" + if [[ "$(uname -s)" == "Darwin" ]]; then + base64 -i "$input" + else + base64 "$input" + fi +} + +base64_decode() { + local input="$1" + if [[ "$(uname -s)" == "Darwin" ]]; then + base64 -D "$input" + else + base64 -d "$input" + fi +} + check_connection_with_cmd() { local cmd="$1" local api_url="$2" @@ -29,14 +47,6 @@ check_connection_with_cmd() { esac } -check_galaxy_pull(){ - if [[ $(needs_update ".spin-ansible-collection-pull" "1") || "$force_ansible_upgrade" == true ]]; then - run_ansible --allow-ssh --mount-path $(pwd) \ - ansible-galaxy collection install "${SPIN_ANSIBLE_COLLECTION_NAME}" --upgrade - save_current_time_to_cache_file ".spin-ansible-collection-pull" - fi -} - check_for_upgrade() { if needs_update ".spin-last-update" "$AUTO_UPDATE_INTERVAL_IN_DAYS" || [ "$1" == "--force" ]; then if [ "$1" != "--force" ]; then @@ -227,7 +237,7 @@ download_spin_template_repository() { case "$1" in -b|--branch) branch="$2" - shift 2 # Shift both the flag and its value + shift 2 ;; -l|--local) SPIN_TEMPLATE_TEMPORARY_SRC_DIR="$2" @@ -265,12 +275,13 @@ download_spin_template_repository() { case "$template" in laravel) template_type=official - TEMPLATE_REPOSITORY="serversideup/spin-template-laravel-basic" + TEMPLATE_REPOSITORY="serversideup/spin-template-laravel-pro" + branch="${branch:-"main"}" ;; laravel-pro) template_type=official TEMPLATE_REPOSITORY="serversideup/spin-template-laravel-pro" - branch="${branch:-main}" + branch="${branch:-"main"}" ;; nuxt) template_type=official @@ -443,51 +454,62 @@ filter_out_spin_arguments() { get_ansible_variable(){ local variable_name="$1" - local file="${2:-".spin.yml"}" - local vault_args=() - local raw_ansible_output='' - local trimmed_ansible_output='' + local raw_output='' + local clean_output='' + shift - # Check if the file is encrypted and .vault-password is missing - if is_encrypted_with_ansible_vault "$file" && [ ! -f ".vault-password" ]; then - echo "${BOLD}${RED}❌Error: $file is encrypted with Ansible Vault, but '.vault-password' file is missing.${RESET}" >&2 - echo "${BOLD}${YELLOW}Please save your vault password in '.vault-password' in your project root and try again.${RESET}" >&2 - exit 1 + if [[ -z "$variable_name" ]]; then + echo "${BOLD}${RED}❌ No variable name specified.${RESET}" >&2 + return 1 fi - # Set vault args - if [ -f ".vault-password" ]; then - vault_args+=("--vault-password-file" ".vault-password") - elif is_encrypted_with_ansible_vault "$file"; then - echo "${BOLD}${YELLOW}🔐 The file '$file' is encrypted. You will be prompted to enter your vault password.${RESET}" >&2 - vault_args+=("--ask-vault-pass") + # Temporarily disable trace output if it's enabled + local trace_enabled=0 + if [[ $- == *x* ]]; then + set +x + trace_enabled=1 fi - # Run the ansible command - raw_ansible_output=$(run_ansible --mount-path "$(pwd)" \ - ansible localhost -m debug \ - -a "var=${variable_name}" \ - -e "@${file}" \ - "${vault_args[@]}" 2>&1) - - # Check for errors in the output - if echo "$raw_ansible_output" | grep -q "ERROR!"; then - echo "${BOLD}${RED}Error: Failed to retrieve variable. Details:${RESET}" >&2 - echo "$raw_ansible_output" >&2 - exit 1 - fi + # Get vault args if needed + local vault_args=() + IFS=' ' read -r -a vault_args < <(set_ansible_vault_args) - # Check for variable presence - if echo "$raw_ansible_output" | grep -q "${variable_name}"; then - trimmed_ansible_output=$(echo "$raw_ansible_output" | awk -F': ' '/"'"$variable_name"'"/ {print $2}' | tr -d '[:space:]"' | sed 's/\x1b\[[0-9;]*m//g') - # Return the cleaned output - echo "$trimmed_ansible_output" - else - echo "${BOLD}${YELLOW}Warning: Variable ${variable_name} not found in $file${RESET}" >&2 - exit 1 - fi -} + echo "Gathering environment information..." >&2 + + # Run ansible command directly with docker + raw_output=$(docker run --rm -i \ + -e "PUID=${SPIN_USER_ID}" \ + -e "PGID=${SPIN_GROUP_ID}" \ + -e "RUN_AS_USER=$(whoami)" \ + -e "ANSIBLE_STDOUT_CALLBACK=minimal" \ + -e "ANSIBLE_DISPLAY_SKIPPED_HOSTS=false" \ + -e "ANSIBLE_DISPLAY_OK_HOSTS=false" \ + -v "$SPIN_ANSIBLE_COLLECTIONS_PATH:/etc/ansible/collections" \ + -v "$(pwd):/ansible" \ + -w /ansible \ + "$SPIN_ANSIBLE_IMAGE" \ + ansible-playbook \ + serversideup.spin.get_variable \ + --inventory "$SPIN_INVENTORY_FILE" \ + "${vault_args[@]}" \ + --extra-vars @./.spin.yml \ + --extra-vars "variable_name=$variable_name" 2>&1) || { + echo "${BOLD}${RED}❌ Failed to get ansible variable: $variable_name${RESET}" >&2 + echo "${BOLD}${RED}Error: $raw_output${RESET}" >&2 + # Restore trace output if it was enabled + if [[ $trace_enabled -eq 1 ]]; then set -x; fi + return 1 + } + + # Extract just the value from the msg field + clean_output=$(echo "$raw_output" | grep -o '"msg": .*' | sed 's/"msg": //;s/^"//;s/"$//') + + # Restore trace output if it was enabled + if [[ $trace_enabled -eq 1 ]]; then set -x; fi + # Return the cleaned output + echo "$clean_output" +} get_github_release() { release_type="$1" @@ -503,24 +525,30 @@ get_github_release() { } get_file_from_github_release() { + local source_type="release" # Default to release downloads while [[ "$#" -gt 0 ]]; do case "$1" in -r|--repo) local repo="$2" - shift 2 # Shift both the flag and its value + shift 2 ;; -t|--release-type) local release_type="$2" - shift 2 # Shift both the flag and its value + shift 2 + ;; + -b|--branch) + local branch="$2" + source_type="branch" + shift 2 ;; -s|--src) local source_file="$2" - shift 2 # Shift both the flag and its value + shift 2 ;; -d|--dest) local destination_file="$2" - shift 2 # Shift both the flag and its value + shift 2 ;; *) echo "${BOLD}${RED}🛑 Unsupported flag ${1}.${RESET}" @@ -532,12 +560,20 @@ get_file_from_github_release() { destination_filename=$(basename "$destination_file") if [[ -f "$destination_file" ]]; then - trap show_existing_files_warning EXIT - echo "👉 ${MAGENTA}\"$destination_filename\" already exists. Skipping...${RESET}" - return 0 + trap show_existing_files_warning EXIT + echo "👉 ${MAGENTA}\"$destination_filename\" already exists. Skipping...${RESET}" + return 0 fi - curl --silent --location --output "$destination_file" "https://raw.githubusercontent.com/$repo/$(get_github_release "$release_type" "$repo")/$source_file" + # Construct the URL based on source type + local download_url + if [[ "$source_type" == "branch" ]]; then + download_url="https://raw.githubusercontent.com/$repo/$branch/$source_file" + else + download_url="https://raw.githubusercontent.com/$repo/$(get_github_release "$release_type" "$repo")/$source_file" + fi + + curl --silent --location --output "$destination_file" "$download_url" echo "✅ \"$destination_filename\" has been created." } @@ -637,6 +673,7 @@ line_in_file() { local action="ensure" local files=() local args=() + local ignore_missing=false # Parse arguments while [[ $# -gt 0 ]]; do @@ -649,6 +686,10 @@ line_in_file() { action="$2" shift 2 ;; + --ignore-missing) + ignore_missing=true + shift + ;; *) args+=("$1") shift @@ -721,8 +762,12 @@ ${args[1]}" "$file" if grep -qF -- "${args[0]}" "$file"; then sed_inplace "s/${args[0]}/${args[1]}/g" "$file" else - echo "Error: Exact text '${args[0]}' not found in $file" >&2 - return 1 + if [[ "$ignore_missing" == true ]]; then + return 0 + else + echo "Error: Exact text '${args[0]}' not found in $file" >&2 + return 1 + fi fi ;; search) @@ -781,13 +826,75 @@ needs_update() { # Calculate the threshold time for update local threshold_time=$(current_time_minus "$interval") - if (( threshold_time <= last_update_time )); then - return 1 + if (( last_update_time >= threshold_time )); then + return 1 # No update needed - last update is newer than threshold else - return 0 + return 0 # Update needed - last update is older than threshold fi } +prepare_ansible_run() { + # Return values will be stored in these global variables + SPIN_ANSIBLE_ARGS=() + SPIN_UNPROCESSED_ARGS=() + SPIN_REMOTE_USER="$USER" + SPIN_FORCE_INSTALL_GALAXY_DEPS=${SPIN_FORCE_INSTALL_GALAXY_DEPS:-false} + SPIN_INVENTORY_FILE="${SPIN_INVENTORY_FILE:-"/etc/ansible/collections/ansible_collections/serversideup/spin/plugins/inventory/spin-dynamic-inventory.sh"}" + SPIN_ANSIBLE_COLLECTIONS_PATH="$SPIN_CACHE_DIR/collections" + + # Process arguments + while [[ "$#" -gt 0 ]]; do + case "$1" in + --host|-h) + SPIN_ANSIBLE_ARGS+=("--extra-vars" "target=$2") + shift 2 + ;; + --user|-u) + SPIN_REMOTE_USER="$2" + shift 2 + ;; + --port|-p) + SPIN_ANSIBLE_ARGS+=("--extra-vars" "ansible_port=$2") + shift 2 + ;; + --upgrade|-U) + SPIN_FORCE_INSTALL_GALAXY_DEPS=true + shift + ;; + *) + SPIN_UNPROCESSED_ARGS+=("$1") + shift + ;; + esac + done + + if [[ -f ".spin-inventory.ini" ]]; then + SPIN_INVENTORY_FILE="/ansible/.spin-inventory.ini" + fi + + # Create the collections directory if it doesn't exist + if [[ ! -d "$SPIN_ANSIBLE_COLLECTIONS_PATH" ]]; then + mkdir -p "$SPIN_ANSIBLE_COLLECTIONS_PATH" + fi + + # Install Ansible Galaxy dependencies if the flag is set + if [[ $(needs_update ".spin-ansible-collection-pull" "1") || "$SPIN_FORCE_INSTALL_GALAXY_DEPS" == true ]]; then + echo "Installing Ansible Galaxy dependencies..." + docker run --rm -it \ + -e "PUID=${SPIN_USER_ID}" \ + -e "PGID=${SPIN_GROUP_ID}" \ + -e "RUN_AS_USER=$(whoami)" \ + -v "$SPIN_ANSIBLE_COLLECTIONS_PATH:/etc/ansible/collections" \ + "$SPIN_ANSIBLE_IMAGE" \ + ansible-galaxy collection install "${SPIN_ANSIBLE_COLLECTION_NAME}" --force + save_current_time_to_cache_file ".spin-ansible-collection-pull" + fi + + # Append vault args to additional ansible args + IFS=' ' read -r -a vault_args < <(set_ansible_vault_args) + SPIN_ANSIBLE_ARGS+=("${vault_args[@]}") +} + print_version() { # Use the local Git repo to show our version printf "${BOLD}${YELLOW}Spin Version:${RESET} \n" @@ -813,6 +920,9 @@ prompt_and_update_file() { local details="" local success_message="" local prompt="Enter your response" + local output_only=false + local validate="" + local clear_screen=false # Parse arguments while [[ $# -gt 0 ]]; do @@ -841,6 +951,18 @@ prompt_and_update_file() { success_message="$2" shift 2 ;; + --output-only) + output_only=true + shift + ;; + --validate) + validate="$2" + shift 2 + ;; + --clear-screen) + clear_screen=true + shift + ;; *) echo "Unknown option: $1" >&2 return 1 @@ -849,27 +971,73 @@ prompt_and_update_file() { done # Validate required parameters - if [[ ${#files[@]} -eq 0 || -z "$title" || -z "$search_default" ]]; then - echo "Error: Missing required parameters" >&2 + if [[ -z "$title" ]]; then + echo -e "${BOLD}${RED}Error: Missing required parameter 'title' for prompt_and_update_file function.${RESET}" >&2 return 1 fi + if [[ "$output_only" == false ]]; then + if [[ ${#files[@]} -eq 0 ]]; then + echo "Error: No files specified for update. Please use the --file option to specify at least one file or set --output-only to true." >&2 + return 1 + fi + if [[ -z "$search_default" ]]; then + echo -e "${BOLD}${RED}Error: Missing required parameter 'search-default' for file update mode.${RESET}" >&2 + return 1 + fi + fi - echo "${BOLD}${BLUE}$title${RESET}" + if [[ "$clear_screen" == true ]]; then + clear >&2 + fi + + echo -e "${BOLD}${BLUE}$title${RESET}" >&2 if [[ -n "$details" ]]; then - echo "$details" + echo -e "$details" >&2 fi - read -p "${BOLD}${YELLOW}$prompt [$search_default]:${RESET} " user_response - # Use the user's input if provided, otherwise use the search_default - value_to_use="${user_response:-$search_default}" + local value_to_use="" + while true; do + if [[ -n "$search_default" ]]; then + read -p "$(echo -e "${BOLD}${YELLOW}$prompt [$search_default]:${RESET} ")" user_response >&2 + value_to_use="${user_response:-$search_default}" + else + read -p "$(echo -e "${BOLD}${YELLOW}$prompt:${RESET} ")" value_to_use >&2 + fi - # Update each specified file - for file in "${files[@]}"; do - line_in_file --action exact --file "$file" "$search_default" "$value_to_use" + # Validate input if --validate flag is set + if [[ -n "$validate" ]]; then + case "$validate" in + email) + if [[ "$value_to_use" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$ ]]; then + break + else + echo -e "${BOLD}${RED}Invalid email address. Please try again.${RESET}" >&2 + continue + fi + ;; + # Add more validation options here in the future + *) + echo "Unknown validation type: $validate" >&2 + return 1 + ;; + esac + else + break + fi done - if [[ -n "$success_message" ]]; then - echo "✅ $success_message" + if [[ "$output_only" == true ]]; then + # Just output the value to stdout + echo "$value_to_use" + else + # Update each specified file + for file in "${files[@]}"; do + line_in_file --action exact --file "$file" "$search_default" "$value_to_use" + done + + if [[ -n "$success_message" ]]; then + echo -e "✅ $success_message" >&2 + fi fi } @@ -940,11 +1108,19 @@ prompt_to_encrypt_files(){ run_ansible() { local additional_docker_args=() + local ansible_args=() local args_without_options=() - ansible_collections_path="$SPIN_CACHE_DIR/collections" - - # Create the collections directory if it doesn't exist - mkdir -p "$ansible_collections_path" + local allow_ssh=false + local minimal_output=false + local set_env=false + local debug=${SPIN_DEBUG:-false} + SPIN_FORCE_INSTALL_GALAXY_DEPS=${SPIN_FORCE_INSTALL_GALAXY_DEPS:-false} + # List of environment variables that can be forwarded to Ansible container + local env_vars_to_forward=( + "HCLOUD_TOKEN" + "DO_API_TOKEN" + "VULTR_API_KEY" + ) # Create the known_hosts file if it doesn't exist if [[ ! -f "$HOME/.ssh/known_hosts" ]]; then @@ -954,39 +1130,182 @@ run_ansible() { while [[ "$#" -gt 0 ]]; do case "$1" in --allow-ssh) - additional_docker_args+=("-v" "$HOME/.ssh/:/ssh/:ro" "-v" "$HOME/.ssh/known_hosts:/ssh/known_hosts:rw" "-v" "$ansible_collections_path:/etc/ansible/collections") - # Mount the SSH Agent for macOS and Linux (including WSL2) systems - if [ -n "$SSH_AUTH_SOCK" ]; then - case "$(uname -s)" in - Darwin) - # macOS - additional_docker_args+=("-v" "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock" "-e" "SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock") - ;; - Linux) - # Linux (including WSL2) - additional_docker_args+=("-v" "$SSH_AUTH_SOCK:$SSH_AUTH_SOCK" "-e" "SSH_AUTH_SOCK=$SSH_AUTH_SOCK") - ;; - esac - fi + allow_ssh=true shift ;; --mount-path) - additional_docker_args+=("-v" "${2}:/ansible") + additional_docker_args+=("-v" "${2}") + shift 2 + ;; + --minimal-output) + minimal_output=true + shift + ;; + --set-env) + set_env=true + shift + ;; + --container-env) + additional_docker_args+=("-e" "$2") shift 2 ;; *) - args_without_options+=("$1") + ansible_args+=("$1") shift ;; esac done + + if [[ "$allow_ssh" == true ]]; then + additional_docker_args+=("-v" "$HOME/.ssh/:/ssh/:ro" "-v" "$HOME/.ssh/known_hosts:/ssh/known_hosts:rw") + + # Set remote Ansible user + ansible_args+=("--extra-vars" "spin_remote_user=$SPIN_REMOTE_USER") + + # Check if we need to ask for sudo password + local use_passwordless_sudo + use_passwordless_sudo=$(get_ansible_variable "use_passwordless_sudo") + use_passwordless_sudo=${use_passwordless_sudo:-"true"} + if [ "$SPIN_REMOTE_USER" != "root" ] && [ "$use_passwordless_sudo" = 'false' ]; then + ansible_args+=("--ask-become-pass") + fi + + # Mount the SSH Agent for macOS and Linux (including WSL2) systems + if [ -n "$SSH_AUTH_SOCK" ]; then + case "$(uname -s)" in + Darwin) + # macOS + additional_docker_args+=("-v" "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock" "-e" "SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock") + ;; + Linux) + # Linux (including WSL2) + additional_docker_args+=("-v" "$SSH_AUTH_SOCK:$SSH_AUTH_SOCK" "-e" "SSH_AUTH_SOCK=$SSH_AUTH_SOCK") + ;; + esac + fi + fi + + if [[ "$minimal_output" == true ]]; then + additional_docker_args+=( + -e "ANSIBLE_STDOUT_CALLBACK=minimal" + -e "ANSIBLE_DISPLAY_SKIPPED_HOSTS=false" + -e "ANSIBLE_DISPLAY_OK_HOSTS=false" + ) + fi + + if [[ "$set_env" == true ]]; then + additional_docker_args+=("-e" "ANSIBLE_FORCE_COLOR=1") + + # Forward specific environment variables if they exist + for env_var in "${env_vars_to_forward[@]}"; do + if [[ -n "${!env_var}" ]]; then + # Environment variable exists in current shell + additional_docker_args+=("-e" "${env_var}=${!env_var}") + elif [[ -f ".env" ]] && grep -q "^${env_var}=" ".env"; then + # Variable exists in .env file + local env_value + env_value=$(grep "^${env_var}=" ".env" | cut -d '=' -f2- | tr -d '"' | tr -d "'") + additional_docker_args+=("-e" "${env_var}=${env_value}") + fi + done + fi + + # Mount collections directory if it exists + if [[ -d "$SPIN_ANSIBLE_COLLECTIONS_PATH" ]]; then + additional_docker_args+=("-v" "$SPIN_ANSIBLE_COLLECTIONS_PATH:/etc/ansible/collections") + fi + + if [[ "$debug" == true ]]; then + ansible_args+=("-vvvv") + fi + docker run --rm -it \ -e "PUID=${SPIN_USER_ID}" \ -e "PGID=${SPIN_GROUP_ID}" \ -e "RUN_AS_USER=$(whoami)" \ "${additional_docker_args[@]}" \ "$SPIN_ANSIBLE_IMAGE" \ - "${args_without_options[@]}" + "${ansible_args[@]}" +} + +run_gh() { + # Check if gh is installed locally + if command -v gh >/dev/null 2>&1; then + # Execute gh command locally with all arguments + gh "$@" + return $? # Explicitly return the exit code from gh + fi + + # Fall back to Docker implementation if gh is not installed + local additional_docker_args=() + local gh_command=("$@") + local interactive_flag="" + local use_tty=false + local use_interactive=false + local interactive_tty_commands=( + "auth login" "auth refresh" + "issue create" "issue edit" + "pr create" "pr edit" "pr review" + "repo create" "repo fork" + "gist create" "gist edit" + ) + + # Check if there's data being piped in + if [ ! -t 0 ]; then + use_interactive=true # Always use -i when receiving STDIN + fi + + # Check if the command needs interactive TTY + local cmd_string="${gh_command[*]}" + for interactive_cmd in "${interactive_tty_commands[@]}"; do + if [[ "$cmd_string" == "$interactive_cmd"* ]]; then + use_tty=true + use_interactive=true + break + fi + done + + # Ensure the gh config directory exists + if [[ ! -d "$HOME/.config/gh" ]]; then + mkdir -p "$HOME/.config/gh" + fi + + # Mount SSH if available + if [[ -d "$HOME/.ssh" ]]; then + additional_docker_args+=("-v" "$HOME/.ssh/:/ssh/:ro" "-v" "$HOME/.ssh/known_hosts:/ssh/known_hosts:rw") + fi + + # Mount SSH Agent if available + if [ -n "$SSH_AUTH_SOCK" ]; then + case "$(uname -s)" in + Darwin) + # macOS + additional_docker_args+=("-v" "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock" "-e" "SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock") + ;; + Linux) + # Linux (including WSL2) + additional_docker_args+=("-v" "$SSH_AUTH_SOCK:$SSH_AUTH_SOCK" "-e" "SSH_AUTH_SOCK=$SSH_AUTH_SOCK") + ;; + esac + fi + + # Determine interactive/TTY flags + if [ "$use_tty" = true ] && [ "$use_interactive" = true ]; then + interactive_flag="-it" + elif [ "$use_tty" = true ]; then + interactive_flag="-t" + elif [ "$use_interactive" = true ]; then + interactive_flag="-i" + fi + + docker run --rm $interactive_flag \ + -e "PUID=${SPIN_USER_ID}" \ + -e "PGID=${SPIN_GROUP_ID}" \ + -e "RUN_AS_USER=$(whoami)" \ + -v "$(pwd):/app" \ + -v "$HOME/.config/gh:/config/gh:rw" \ + "${additional_docker_args[@]}" \ + "$SPIN_GH_CLI_IMAGE" gh "${gh_command[@]}" } save_current_time_to_cache_file() { @@ -1010,10 +1329,35 @@ send_to_upgrade_script () { set_ansible_vault_args() { local vault_args=() + local variable_file=".spin.yml" + local run_type="${1:-docker}" if [[ -f .vault-password ]]; then - vault_args+=("--vault-password-file" ".vault-password") - elif is_encrypted_with_ansible_vault ".spin.yml" && is_encrypted_with_ansible_vault ".spin-inventory.ini"; then + # Validate the vault password file using Docker + if is_encrypted_with_ansible_vault "$variable_file"; then + set +e # Disable error checking for the duration of this block + docker run --rm -i \ + -e "PUID=${SPIN_USER_ID}" \ + -e "PGID=${SPIN_GROUP_ID}" \ + -e "RUN_AS_USER=$(whoami)" \ + -v "$(pwd):/ansible" \ + "$SPIN_ANSIBLE_IMAGE" \ + ansible-vault view --vault-password-file="/ansible/.vault-password" "$variable_file" > /dev/null 2>&1 + + validation_result=$? + set -e # Re-enable error checking + if [ $validation_result -ne 0 ]; then + echo "${BOLD}${RED}❌ Invalid password provided in '.vault-password' file. Please check your password and try again.${RESET}" >&2 + exit $validation_result + fi + fi + + if [[ "$run_type" == "local" ]]; then + vault_args+=("--vault-password-file" ".vault-password") + else + vault_args+=("--vault-password-file" "/ansible/.vault-password") + fi + elif is_encrypted_with_ansible_vault "$variable_file" || is_encrypted_with_ansible_vault ".spin-inventory.ini"; then echo "${BOLD}${YELLOW}🔐 '.vault-password' file not found. You will be prompted to enter your vault password.${RESET}" >&2 vault_args+=("--ask-vault-pass") fi @@ -1022,6 +1366,9 @@ set_ansible_vault_args() { } setup_color() { + # Disable debug tracing temporarily + { set +x; } 2>/dev/null + RAINBOW=" $(printf '\033[38;5;196m') $(printf '\033[38;5;202m') @@ -1035,6 +1382,11 @@ setup_color() { BOLD=$(printf '\033[1m') RESET=$(printf '\033[m') MAGENTA=$(printf '\033[1;35m') + + # Restore debug tracing if it was enabled + if [[ "${SPIN_DEBUG:-false}" == "true" ]]; then + set -x + fi } show_existing_files_warning() { @@ -1080,4 +1432,43 @@ update_last_pull_timestamp() { # Replace the original .spin-last-pull file with the updated temporary file mv "$file.tmp" "$file" -} \ No newline at end of file +} + +validate_project_setup() { + + validate_spin_yml + + # Validate infrastructure folder is present + if [ ! -d ".infrastructure" ]; then + echo "${BOLD}${RED}❌ Infrastructure folder not found${RESET}" + echo "Please ensure you're in the root of your project." + return 1 + fi + + # Create ci folder if it doesn't exist + if [ ! -d "$SPIN_CI_FOLDER" ] || [ ! -f "$SPIN_CI_FOLDER/.gitignore" ]; then + echo "${BOLD}${YELLOW}⚠️ Warning: The CI folder is missing, we will create this for you.${RESET}" + mkdir -p "$SPIN_CI_FOLDER" + echo "*" > "$SPIN_CI_FOLDER/.gitignore" + echo "!.gitignore" >> "$SPIN_CI_FOLDER/.gitignore" + fi + + return 0 +} + +validate_spin_yml() { + if [ ! -f ".spin.yml" ]; then + echo "${BOLD}${RED}❌ .spin.yml not found${RESET}" + echo "Please ensure you're in the root of your project and a .spin.yml file exists." + return 1 + fi + + if is_encrypted_with_ansible_vault ".spin.yml" && \ + [ ! -f ".vault-password" ]; then + echo "${BOLD}${RED}❌Error: .spin.yml is encrypted with Ansible Vault, but '.vault-password' file is missing.${RESET}" + echo "${BOLD}${YELLOW}Please save your vault password in '.vault-password' in your project root and try again.${RESET}" + return 1 + fi + + return 0 +} diff --git a/tools/install.sh b/tools/install.sh index 1b1c1248..19882a26 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -25,7 +25,7 @@ # # # You can also pass some arguments to the install script to set some these options: -# --beta: use the latest pre-release +# --beta: use the latest release (regardless of pre-release or stable) # For example: # bash install.sh --beta # or: @@ -184,7 +184,7 @@ command_exists() { get_install_version() { if [ ! -z "$BRANCH" ]; then - echo $BRANCH + echo "$BRANCH" return 0 fi if [ "$TRACK" = "beta" ]; then @@ -207,12 +207,12 @@ get_install_version() { } set_configuration_file() { - mkdir -p $SPIN_HOME/conf/ - echo "TRACK=$TRACK" > $SPIN_HOME/conf/spin.conf + mkdir -p "$SPIN_HOME/conf/" + echo "TRACK=$TRACK" > "$SPIN_HOME/conf/spin.conf" } save_last_update_check_time() { - echo $(date +"%s") > $SPIN_CACHE_DIR/.spin-last-update + date +"%s" > "$SPIN_CACHE_DIR/.spin-last-update" } setup_spin() { @@ -371,4 +371,4 @@ main() { print_success } -main $@ \ No newline at end of file +main "$@" \ No newline at end of file diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 9b408f5b..d1a153dd 100644 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -188,19 +188,19 @@ perform_upgrade() { echo "${BLUE}Updating Spin to \"$new_version\"...${RESET}" - git -C $SPIN_HOME fetch --all --tags > /dev/null + git -C "$SPIN_HOME" fetch --all --tags > /dev/null # Set git-config values known to fix git errors # Line endings - git -C $SPIN_HOME config core.eol lf - git -C $SPIN_HOME config core.autocrlf false + git -C "$SPIN_HOME" config core.eol lf + git -C "$SPIN_HOME" config core.autocrlf false # zeroPaddedFilemode fsck errors - git -C $SPIN_HOME config fsck.zeroPaddedFilemode ignore - git -C $SPIN_HOME config fetch.fsck.zeroPaddedFilemode ignore - git -C $SPIN_HOME config receive.fsck.zeroPaddedFilemode ignore - git -C $SPIN_HOME config rebase.autoStash true + git -C "$SPIN_HOME" config fsck.zeroPaddedFilemode ignore + git -C "$SPIN_HOME" config fetch.fsck.zeroPaddedFilemode ignore + git -C "$SPIN_HOME" config receive.fsck.zeroPaddedFilemode ignore + git -C "$SPIN_HOME" config rebase.autoStash true - if ! git -C $SPIN_HOME checkout "tags/$new_version" -b "$new_version"; then + if ! git -C "$SPIN_HOME" checkout "tags/$new_version" -b "$new_version"; then fmt_error 'Update of "spin" failed.' exit 1 fi