Skip to content

Commit dd05687

Browse files
Spin v3 Release (#131)
* Added .spin.yml to gitignore * Remove encryption * Shellcheck fixes * Shellcheck improvments * Improved error message * Shellcheck improvements * Cleaned up Ansible variable retrieval and prompts * Allow force upgrade of collection * Set inventory * Added force flag to prune * Set remote user and target * Added Spin maintain and restructured provision * Revert collection name * Added cursor rules * Regoranized and centralized functions * Restructured Ansible functionality * Reorganized file * A-z function names * Restructured to support smooth upgrades from Spin v2 * Reset temporary collection source * Added other projects * Added extra space * Added other projects * Added Spin Hub * Fixed laravel basic link * Improved Ansible vault support * Initial docs update * Fix 404 links * Fix responsive images * Set latest pre-release track only * Fix date command * Revert install behavior * Fix quotes * Added notes * Add 'Get Help' documentation page * Add documentation for testing new releases of Spin * Added environments to spin provision and maintain * Added help notes * Added canonical URLs * Refactor validation logic in project setup - Moved CI folder creation logic into the validate_project_setup function. - Added validate_spin_yml function to check for the existence of .spin.yml. - Updated action_maintain and action_provision to call validate_spin_yml for improved validation flow. * Added canonical URLs to documentation files for improved SEO and accessibility. * Added "gh" documentation * Added comparison table * Reorganized the "Automated Deployments with GitHub Actions" section in the deployment strategy documentation for clarity and consistency. The content was moved to improve the flow of information, ensuring users can easily access deployment guides. * Enhance troubleshooting documentation for service management in Docker - Added guidelines for diagnosing connection issues between services, including checks for environment variables and service passwords. - Included instructions for stopping services, removing volumes, configurations, and networks using Docker commands. - Added a section on pruning unused resources to free up disk space. These updates aim to provide clearer guidance for users managing Docker services and troubleshooting common issues. * Added maintenance image * Remove canonical * Added explanation of spin maintain * Added firewall notes * Update volume mount path in run_gh function from /workdir to /app for improved clarity in Docker configuration * Add notes on spin provison (#138) * Fix example * Update "Use Any Host" documentation to clarify Spin's compatibility with various hosting environments and link to server requirements * Improve argument parsing. Fixes #136 * Documentation UX updates (#139) * Keeps the active page in view on the navigation * Installed hotkeys for FF support fixes #107 * Fix typo in comment * Set authorized keys for deployment (if set) * Support deployments without dockerfiles (Ref #118) * Fix array * Fix doc titles * Remove unnecessary blank line in NPM/Yarn installation documentation * Enhance deploy script to allow customizable Docker registry image. Default to 'registry:2' if not specified. This improves flexibility for deployments. * Fix Docker command in troubleshooting documentation to ensure 'sudo' is used consistently for executing commands in containers. * Added Spin v2 to v3 migration guide * Enhance deploy script: add cleanup for unused Docker images, update image tagging to use timestamp, and improve variable naming for clarity. * Refactor Ansible vault password validation in functions.sh to improve error handling and user feedback. The script now checks if the variable file is encrypted before validating the password, and provides clearer error messages for invalid passwords. * Refactor deploy script to improve variable naming for SSH user and port. Updated variables from 'ssh_user' and 'ssh_port' to 'SPIN_SSH_USER' and 'SPIN_SSH_PORT' for better clarity and consistency. * Changed docker builds from localhost for 127.0.0.1 so it matches Docker's insecure registry defaults without requiring https * Enhance Spin debugging capabilities: added SPIN_DEBUG environment variable to enable debug logs in the spin script. Updated documentation to include instructions for using SPIN_DEBUG for troubleshooting and added examples for Docker health check failures. * Fix path references to Ansible vault when running with Docker * Refactor Ansible vault argument handling to support local and Docker runs. The `set_ansible_vault_args` function now accepts a `run_type` parameter, allowing for different vault password file paths based on the execution context. This improves flexibility and clarity in vault management. * Ready for v3 stable 🥳 --------- Co-authored-by: Dan Pastori <[email protected]>
1 parent 5ea024d commit dd05687

File tree

110 files changed

+4197
-942
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+4197
-942
lines changed

.cursorrules

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
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.
2+
3+
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.
4+
5+
Code Style and Structure
6+
- Write clean, maintainable and technically accurate code.
7+
- All bash must be POSIX compliant.
8+
- All bash must be compatible with Linux, WSL2, and MacOS (Bash v3)
9+
- Never use an approach you're not confident about. If you're unsure about something, ask for clarity.
10+
- Always follow best practices for Bash, Ansible, and Docker.
11+
- This project should work on Linux, WSL2, and MacOS without installing any additional dependencies other than Docker.
12+
13+
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.

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ Spin serves as a collection of open source technologies, put together in one sim
9191
- [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.
9292
- [serversideup/php](https://serversideup.net/open-source/docker-php/) - PHP Docker images highly optimized to work with Laravel + Spin.
9393
- [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.
94+
- [serversideup/docker-ansible](https://github.com/serversideup/docker-ansible) - A lightweight docker image that runs Ansible.
95+
- [serversideup/docker-github-cli](https://github.com/serversideup/docker-github-cli) - A lightweight docker image that runs GitHub CLI.
9496

9597
## Resources
9698
- **[Website](https://serversideup.net/open-source/spin/)** overview of the product.

bin/spin

+24-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ set -e
33

44
# Default Environment
55
SPIN_ENV=${SPIN_ENV:-dev}
6+
SPIN_DEBUG=${SPIN_DEBUG:-false}
7+
8+
if [[ "$SPIN_DEBUG" == "true" ]]; then
9+
set -x
10+
fi
611

712
# Set up our structure for our re-used commands
813
export COMPOSE_CMD=${COMPOSE_CMD:-"docker compose"}
@@ -16,17 +21,21 @@ export SPIN_GROUP_ID
1621
# Default Images
1722
SPIN_PHP_IMAGE=${SPIN_PHP_IMAGE:-"serversideup/php:cli"}
1823
SPIN_NODE_IMAGE=${SPIN_NODE_IMAGE:-"node:20"}
19-
SPIN_ANSIBLE_IMAGE=${SPIN_ANSIBLE_IMAGE:-"docker.io/serversideup/ansible-core:2.17-alpine"}
20-
SPIN_ANSIBLE_COLLECTION_NAME=${SPIN_ANSIBLE_COLLECTION_NAME:-"serversideup.spin"}
24+
SPIN_ANSIBLE_IMAGE=${SPIN_ANSIBLE_IMAGE:-"docker.io/serversideup/ansible-core:2.18-alpine"}
25+
SPIN_GH_CLI_IMAGE=${SPIN_GH_CLI_IMAGE:-"docker.io/serversideup/github-cli:alpine"}
2126

2227
# Script Configuration
2328
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
2429
SPIN_HOME=$(dirname "$SCRIPT_DIR") #Assume the parent directory of this script is the home
2530
SPIN_CACHE_DIR=${SPIN_CACHE_DIR:-$SPIN_HOME/cache}
2631
SPIN_CONFIG_FILE_LOCATION=${SPIN_CONFIG_FILE_LOCATION:-"$SPIN_HOME/conf/spin.conf"}
32+
SPIN_CI_FOLDER=${SPIN_CI_FOLDER:-".infrastructure/conf/ci"}
2733
AUTO_UPDATE_INTERVAL_IN_DAYS=${AUTO_UPDATE_INTERVAL_IN_DAYS:-14}
2834
AUTO_PULL_INTERVAL_IN_DAYS=${AUTO_PULL_INTERVAL_IN_DAYS:-1}
2935

36+
# Ansible Variables
37+
SPIN_ANSIBLE_COLLECTION_NAME="${SPIN_ANSIBLE_COLLECTION_NAME:-"serversideup.spin"}"
38+
3039
# Import common functions
3140
source "$SPIN_HOME/lib/functions.sh"
3241
setup_color
@@ -44,7 +53,7 @@ case "$1" in
4453
"" | base64 | debug | help | update | version | --version | -v)
4554
: # Silent output for the user, but we're skipping the compose check too.
4655
;;
47-
init | kill | mkpasswd | new | prune | provision | vault)
56+
configure | gh | init | kill | maintain | mkpasswd | new | prune | provision | vault)
4857
check_if_docker_is_running
4958
;;
5059
*)
@@ -67,6 +76,10 @@ if [ $# -gt 0 ]; then
6776
source "$SPIN_HOME/lib/actions/build.sh"
6877
action_build "$@"
6978
;;
79+
configure)
80+
source "$SPIN_HOME/lib/actions/configure.sh"
81+
action_configure "$@"
82+
;;
7083
debug)
7184
source "$SPIN_HOME/lib/actions/debug.sh"
7285
action_debug "$@"
@@ -87,6 +100,10 @@ if [ $# -gt 0 ]; then
87100
source "$SPIN_HOME/lib/actions/help.sh"
88101
action_help
89102
;;
103+
gh)
104+
source "$SPIN_HOME/lib/actions/gh.sh"
105+
action_gh "$@"
106+
;;
90107
init)
91108
source "$SPIN_HOME/lib/actions/init.sh"
92109
action_init "$@"
@@ -103,6 +120,10 @@ if [ $# -gt 0 ]; then
103120
source "$SPIN_HOME/lib/actions/logs.sh"
104121
action_logs "$@"
105122
;;
123+
maintain)
124+
source "$SPIN_HOME/lib/actions/maintain.sh"
125+
action_maintain "$@"
126+
;;
106127
mkpasswd)
107128
source "$SPIN_HOME/lib/actions/mkpasswd.sh"
108129
action_mkpasswd "$@"

docs/components/Docs/Navigation.vue

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<nav>
2+
<nav id="docs-navigation">
33
<ul role="list">
44
<li class="relative my-6">
55
<h2 class="text-xs font-semibold text-white">
@@ -39,12 +39,6 @@
3939
'md:mt-0': groupIndex === 0
4040
}"
4141
/>
42-
43-
<li class="sticky bottom-0 z-10 mt-6 min-[416px]:hidden">
44-
<AppLink :href="'#'" :variant="'filled'" class="w-full">
45-
Sign in
46-
</AppLink>
47-
</li>
4842
</ul>
4943
</nav>
5044
</template>
@@ -53,4 +47,13 @@
5347
const route = useRoute();
5448
5549
const { navigation, toc } = useContent();
50+
51+
onMounted(() => {
52+
console.log(route.path);
53+
const element = document.querySelector(`[data-attr-link-id="${route.path}"]`);
54+
if (element) {
55+
// Can we scroll to this element without using smooth?
56+
element.scrollIntoView();
57+
}
58+
})
5659
</script>

docs/components/Docs/NavigationGroup.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
}">
1616
<NuxtLink
1717
:to="link._path"
18+
:data-attr-link-id="link._path"
1819
class="flex justify-between gap-2 py-1 pr-3 text-sm transition"
1920
:class="{
2021
'text-white': link._path === route.path,
@@ -55,4 +56,5 @@ const route = useRoute();
5556
const scrollTo = (id) => {
5657
document.getElementById( id.replace('#', '') ).scrollIntoView(true, {behavior: "smooth"})
5758
}
58-
</script>
59+
60+
</script>

docs/components/Global/MobileMenu.vue

+12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313
Search
1414
</button>
1515

16+
<NuxtLink
17+
:href="'/docs'"
18+
class="flex items-center font-inter font-bold text-slate-300 text-xl mb-6">
19+
<div class="flex items-center justify-center w-5 h-5 mr-2">
20+
<svg width="18" height="23" viewBox="0 0 18 23" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-full max-h-full">
21+
<path d="M17 18.4468V15.4468H4C2.34315 15.4468 1 16.79 1 18.4468M5.8 21.4468H13.8C14.9201 21.4468 15.4802 21.4468 15.908 21.2289C16.2843 21.0371 16.5903 20.7311 16.782 20.3548C17 19.927 17 19.3669 17 18.2468V4.64684C17 3.52673 17 2.96668 16.782 2.53886C16.5903 2.16253 16.2843 1.85657 15.908 1.66483C15.4802 1.44684 14.9201 1.44684 13.8 1.44684H5.8C4.11984 1.44684 3.27976 1.44684 2.63803 1.77382C2.07354 2.06144 1.6146 2.52038 1.32698 3.08487C1 3.7266 1 4.56668 1 6.24684V16.6468C1 18.327 1 19.1671 1.32698 19.8088C1.6146 20.3733 2.07354 20.8322 2.63803 21.1199C3.27976 21.4468 4.11984 21.4468 5.8 21.4468Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
22+
</svg>
23+
</div>
24+
25+
Docs
26+
</NuxtLink>
27+
1628
<NuxtLink
1729
:href="'/docs'"
1830
class="flex items-center font-inter font-bold text-slate-300 text-xl mb-6">

docs/components/content/HubMain.vue

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<template>
2+
<div class="w-full">
3+
<!-- Hero Section -->
4+
<div class="text-center mb-12">
5+
<h1 class="text-4xl font-bold text-white mb-4">Spin Hub</h1>
6+
<p class="text-slate-400 text-lg">
7+
A place to share and build templates together.
8+
<NuxtLink to="/docs/advanced/create-your-own-template" class="text-[#1CE783] hover:underline">
9+
Build your own template →
10+
</NuxtLink>
11+
</p>
12+
</div>
13+
14+
<!-- Grid of Templates -->
15+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
16+
<NuxtLink
17+
v-for="template in templates"
18+
:key="template._path"
19+
:to="template._path"
20+
class="bg-[#151A1F] rounded-lg overflow-hidden hover:ring-1 hover:ring-[#1CE783] transition-all"
21+
>
22+
<div class="aspect-[1.905/1] relative overflow-hidden">
23+
<img
24+
:src="template.image"
25+
class="absolute inset-0 w-full h-full object-cover"
26+
:alt="template.title"
27+
/>
28+
</div>
29+
<div class="p-4">
30+
<div class="flex items-center gap-2 mb-2">
31+
<span
32+
:class="[
33+
'text-xs px-2 py-1 rounded',
34+
categoryStyles[template.category]?.classes || defaultCategoryStyle
35+
]"
36+
>
37+
{{ template.category }}
38+
</span>
39+
</div>
40+
<h3 class="text-white font-semibold text-xl mb-2">{{ template.title }}</h3>
41+
<p class="text-slate-400 text-sm mb-4">{{ template.description }}</p>
42+
<div class="flex items-center gap-2">
43+
<img :src="template.authorImage" class="w-8 h-8 rounded-full" :alt="template.author" />
44+
<div>
45+
<p class="text-white text-sm">{{ template.author }}</p>
46+
</div>
47+
</div>
48+
</div>
49+
</NuxtLink>
50+
</div>
51+
</div>
52+
</template>
53+
54+
<script setup>
55+
const { data: templates } = await useAsyncData('templates', () => {
56+
return queryContent('/hub')
57+
.where({ _path: { $ne: '/hub' } })
58+
.find()
59+
})
60+
61+
// Category styling configuration
62+
const categoryStyles = {
63+
'Laravel': {
64+
classes: 'bg-orange-500/20 text-orange-400'
65+
},
66+
'Nuxt': {
67+
classes: 'bg-green-500/20 text-green-400'
68+
},
69+
'Vue': {
70+
classes: 'bg-emerald-500/20 text-emerald-400'
71+
},
72+
'WordPress': {
73+
classes: 'bg-blue-500/20 text-blue-400'
74+
},
75+
'Node.js': {
76+
classes: 'bg-yellow-500/20 text-yellow-400'
77+
}
78+
}
79+
80+
// Default style for unconfigured categories
81+
const defaultCategoryStyle = 'bg-slate-500/20 text-slate-400'
82+
</script>

docs/components/content/MarketingHeader.vue

+12
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@
6666
</div>
6767

6868
Docs
69+
</NuxtLink>
70+
71+
<NuxtLink
72+
:href="'/hub'"
73+
class="flex items-center font-inter font-bold text-slate-300 text-sm rounded py-[6px] px-2 hover:bg-gray-900 xl:text-lg">
74+
<div class="flex items-center justify-center w-5 h-5 mr-2">
75+
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
76+
<path d="M12.8324 15.2572L9.8324 12.2572M12.8324 15.2572C14.2292 14.7259 15.5693 14.0559 16.8324 13.2572M12.8324 15.2572V20.2572C12.8324 20.2572 15.8624 19.7072 16.8324 18.2572C17.9124 16.6372 16.8324 13.2572 16.8324 13.2572M9.8324 12.2572C10.3645 10.8766 11.0346 9.55327 11.8324 8.30721C12.9976 6.44419 14.62 4.91025 16.5454 3.8513C18.4708 2.79234 20.6351 2.24358 22.8324 2.25721C22.8324 4.97721 22.0524 9.75721 16.8324 13.2572M9.8324 12.2572H4.8324C4.8324 12.2572 5.3824 9.22721 6.8324 8.25721C8.4524 7.17721 11.8324 8.25721 11.8324 8.25721M5.3324 16.7572C3.8324 18.0172 3.3324 21.7572 3.3324 21.7572C3.3324 21.7572 7.0724 21.2572 8.3324 19.7572C9.0424 18.9172 9.0324 17.6272 8.2424 16.8472C7.8537 16.4762 7.34169 16.2618 6.80462 16.2452C6.26756 16.2286 5.74327 16.4109 5.3324 16.7572Z" stroke="#CBD5E1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
77+
</svg>
78+
</div>
79+
80+
Hub
6981
</NuxtLink>
7082

7183
<NuxtLink

docs/components/content/MarketingVideo.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="px-4">
33
<div class="rounded-[10px] mx-auto max-w-[940px] my-8 md:my-16 overflow-hidden">
44
<video autoplay muted loop playsinline>
5-
<source src="https://spin-public-assets.serversideup.net/spin-demo_spin-up.mp4"/>
5+
<source src="https://spin-public-assets.serversideup.net/spin-demo_spin-up-pro.mp4"/>
66
</video>
77
</div>
88
</div>

docs/components/content/Search.vue

+7-5
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,19 @@ import DocsIcon from './DocsIcon.vue';
106106
import HeartIcon from './HeartIcon.vue';
107107
import GitHubIcon from './GitHubIcon.vue';
108108
109+
import hotkeys from 'hotkeys-js';
110+
109111
/**
110112
* CMD + K shortcut for activating the modal
111113
*/
112114
const show = ref(false);
113-
const { meta, k } = useMagicKeys();
114115
115-
watchEffect(() => {
116-
if (meta.value && k.value) {
116+
if( import.meta.client ){
117+
hotkeys('ctrl+k,command+k', (event, handler) => {
117118
show.value = true;
118-
}
119-
});
119+
event.preventDefault();
120+
});
121+
}
120122
121123
/**
122124
* Event handler for opening the modal

docs/content/docs/1.index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ layout: docs
99

1010
<p>
1111
<video autoplay muted loop playsinline>
12-
<source src="https://spin-public-assets.serversideup.net/spin-demo_spin-up.mp4"/>
12+
<source src="https://spin-public-assets.serversideup.net/spin-demo_spin-up-pro.mp4"/>
1313
</video>
1414
</p>
1515

docs/content/docs/1.installation/1.install-macos.md

+7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ head.title: 'Install to macOS - Spin by Server Side Up'
33
title: 'Install to macOS'
44
description: 'Learn how to install Spin on macOS. No root access required.'
55
layout: docs
6+
canonical: https://serversideup.net/open-source/spin/docs/installation/install-macos
67
---
8+
9+
# Install to macOS
10+
::lead-p
11+
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`.
12+
::
13+
714
## Install Docker Desktop
815
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.
916

docs/content/docs/1.installation/2.install-windows.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ head.title: 'Install to Windows - Spin by Server Side Up'
33
title: 'Install to Windows'
44
description: 'Learn how to install Spin on Windows using the Linux Subsystem.'
55
layout: docs
6+
canonical: https://serversideup.net/open-source/spin/docs/installation/install-windows
67
---
78

89
# Install to Windows

docs/content/docs/1.installation/3.install-linux.md

+6
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ head.title: 'Install to Linux - Spin by Server Side Up'
33
title: 'Install to Linux'
44
description: 'Learn how to install Spin on Linux.'
55
layout: docs
6+
canonical: https://serversideup.net/open-source/spin/docs/installation/install-linux
67
---
78

9+
# Install to Linux
10+
::lead-p
11+
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`.
12+
::
13+
814
## Prerequisites
915
You must have a working installation of Docker.
1016

docs/content/docs/1.installation/4.install-composer.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ head.title: 'Install via Composer - Spin by Server Side Up'
33
title: 'Install via Composer'
44
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.'
55
layout: docs
6+
canonical: https://serversideup.net/open-source/spin/docs/installation/install-composer
67
---
78

8-
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)
9+
# Install via Composer
10+
::lead-p
11+
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.
12+
::
913

1014
## Add `spin` to your project with Composer
15+
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)
16+
1117
We can use Docker to run `composer` and install it on your project. **Run this command from the parent folder of your project.**
1218

1319
::code-panel

docs/content/docs/1.installation/5.install-npm-yarn.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ head.title: 'Install via NPM/Yarn - Spin by Server Side Up'
33
title: 'Install via NPM/Yarn'
44
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.'
55
layout: docs
6+
canonical: https://serversideup.net/open-source/spin/docs/installation/install-npm-yarn
67
---
78

8-
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)
9+
# Install via NPM/Yarn
10+
::lead-p
11+
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.
12+
::
913

1014
## Add `spin` to your project with Yarn/NPM
15+
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)
16+
1117
We can use Docker to run install `spin` on your project. **Run this command from the parent folder of your project.**
1218

1319
::note

0 commit comments

Comments
 (0)