feat: add one liner install command (#4613)

* feat: add one liner

* fix: interactive issue & add support for both linux & mac

* feat: move quick start documentation

* feat: catch errors

* feat: check if directory exists

* feat: default to yes for prompt

* feat: open in browser

* fix: format

* feat: do not expose STORAGE_LOCAL_PATH env but handle the case where it would be set

* fix: db reset command wasn't working out of the box

* Update install.sh

Co-authored-by: Darek Desu <4459421+darekdesu@users.noreply.github.com>

* feat: harden the whole UX with one-liner

* fix: small logical order adjustment

* Update packages/twenty-docs/docs/start/self-hosting/docker-compose.mdx

---------

Co-authored-by: Darek Desu <4459421+darekdesu@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Quentin G 2024-03-27 21:28:03 +01:00 committed by GitHub
parent 0391bf65f2
commit d6de380e02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 192 additions and 74 deletions

149
install.sh Executable file
View File

@ -0,0 +1,149 @@
#!/bin/bash
echo "🔧 Checking dependencies..."
if ! command -v docker &>/dev/null; then
echo -e "\t❌ Docker is not installed or not in PATH. Please install Docker first.\n\t\tSee https://docs.docker.com/get-docker/"
exit 1
fi
# Check if docker compose plugin is installed
if ! docker compose version &>/dev/null; then
echo -e "\t❌ Docker Compose is not installed or not in PATH (n.b. docker-compose is deprecated)\n\t\tUpdate docker or install docker-compose-plugin\n\t\tOn Linux: sudo apt-get install docker-compose-plugin\n\t\tSee https://docs.docker.com/compose/install/"
exit 1
fi
# Check if docker is started
if ! docker info &>/dev/null; then
echo -e "\t❌ Docker is not running.\n\t\tPlease start Docker Desktop, Docker or check documentation at https://docs.docker.com/config/daemon/start/"
exit 1
fi
if ! command -v curl &>/dev/null; then
echo -e "\t❌ Curl is not installed or not in PATH.\n\t\tOn macOS: brew install curl\n\t\tOn Linux: sudo apt install curl"
exit 1
fi
# Catch errors
set -e
function on_exit {
# $? is the exit status of the last command executed
local exit_status=$?
if [ $exit_status -ne 0 ]; then
echo "❌ Something went wrong, exiting: $exit_status"
fi
}
trap on_exit EXIT
# Use environment variables VERSION and BRANCH, with defaults if not set
version=${VERSION:-$(curl -s https://api.github.com/repos/twentyhq/twenty/releases/latest | grep '"tag_name":' | cut -d '"' -f 4)}
branch=${BRANCH:-main}
echo "🚀 Using version $version and branch $branch"
dir_name="twenty"
function ask_directory {
read -p "📁 Enter the directory name to setup the project (default: $dir_name): " answer
if [ -n "$answer" ]; then
dir_name=$answer
fi
}
ask_directory
while [ -d "$dir_name" ]; do
read -p "🚫 Directory '$dir_name' already exists. Do you want to overwrite it? (y/N) " answer
if [ "$answer" = "y" ]; then
break
else
ask_directory
fi
done
# Create a directory named twenty
echo "📁 Creating directory '$dir_name'"
mkdir -p "$dir_name" && cd "$dir_name" || { echo "❌ Failed to create/access directory '$dir_name'"; exit 1; }
# Copy the twenty/packages/twenty-docker/prod/docker-compose.yml file in it
echo "\t• Copying docker-compose.yml"
curl -sLo docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/$branch/packages/twenty-docker/prod/docker-compose.yml
# Copy twenty/packages/twenty-docker/prod/.env.example to .env
echo "\t• Setting up .env file"
curl -sLo .env https://raw.githubusercontent.com/twentyhq/twenty/$branch/packages/twenty-docker/prod/.env.example
# Replace TAG=latest by TAG=<latest_release or version input>
if [[ $(uname) == "Darwin" ]]; then
# Running on macOS
sed -i '' "s/TAG=latest/TAG=$version/g" .env
else
# Assuming Linux
sed -i'' "s/TAG=latest/TAG=$version/g" .env
fi
# Generate random strings for secrets
echo "ACCESS_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
echo "LOGIN_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
echo "REFRESH_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
echo "FILE_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
echo "\t• .env configuration completed"
port=3000
# Check if command nc is available
if command -v nc &> /dev/null; then
# Check if port 3000 is already in use, propose to change it
while nc -zv localhost $port &>/dev/null; do
read -p "🚫 Port $port is already in use. Do you want to use another port? (Y/n) " answer
if [ "$answer" = "n" ]; then
continue
fi
read -p "Enter a new port number: " new_port
if [[ $(uname) == "Darwin" ]]; then
sed -i '' "s/$port:$port/$new_port:$port/g" docker-compose.yml
else
sed -i'' "s/$port:$port/$new_port:$port/g" docker-compose.yml
fi
port=$new_port
done
fi
# Ask user if he wants to start the project
read -p "🚀 Do you want to start the project now? (Y/n) " answer
if [ "$answer" = "n" ]; then
echo "✅ Project setup completed. Run 'docker-compose up -d' to start."
exit 0
else
echo "🐳 Starting Docker containers..."
docker compose up -d
# Check if port is listening
echo -n "Waiting for server to start..."
while [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-server-1) = "healthy" ]; do
echo -n "."
sleep 1
done
echo ""
echo "✅ Server is up and running"
fi
function ask_open_browser {
read -p "🌐 Do you want to open the project in your browser? (Y/n) " answer
if [ "$answer" = "n" ]; then
echo "✅ Setup completed. Access your project at http://localhost:$port"
exit 0
fi
}
# Ask user if he wants to open the project
# Running on macOS
if [[ $(uname) == "Darwin" ]]; then
ask_open_browser
open "http://localhost:$port"
# Assuming Linux
else
# xdg-open is not installed, we could be running in a non gui environment
if command -v xdg-open >/dev/null 2>&1; then
ask_open_browser
xdg-open "http://localhost:$port"
else
echo "✅ Setup completed. Your project is available at http://localhost:$port"
fi
fi

View File

@ -15,3 +15,9 @@ SERVER_URL=http://localhost:3000
# FILE_TOKEN_SECRET=replace_me_with_a_random_string_refresh
SIGN_IN_PREFILLED=true
STORAGE_TYPE=local
# STORAGE_S3_REGION=eu-west3
# STORAGE_S3_NAME=my-bucket
# STORAGE_S3_ENDPOINT=

View File

@ -1,11 +1,10 @@
version: '3.8'
name: twenty
services:
server:
image: twentycrm/twenty:${TAG}
volumes:
- server-local-data:/app/.local-storage
- server-local-data:/app/${STORAGE_LOCAL_PATH:-.local-storage}
ports:
- "3000:3000"
environment:
@ -17,8 +16,10 @@ services:
ENABLE_DB_MIGRATIONS: true
SIGN_IN_PREFILLED: ${SIGN_IN_PREFILLED}
STORAGE_TYPE: local
STORAGE_LOCAL_PATH: .local-storage
STORAGE_TYPE: ${STORAGE_TYPE}
STORAGE_S3_REGION: ${STORAGE_S3_REGION}
STORAGE_S3_NAME: ${STORAGE_S3_NAME}
STORAGE_S3_ENDPOINT: ${STORAGE_S3_ENDPOINT}
ACCESS_TOKEN_SECRET: ${ACCESS_TOKEN_SECRET}
LOGIN_TOKEN_SECRET: ${LOGIN_TOKEN_SECRET}
REFRESH_TOKEN_SECRET: ${REFRESH_TOKEN_SECRET}
@ -27,7 +28,7 @@ services:
db:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl --silent --fail http://localhost:3000/healthz | jq -e '.status == \"ok\"' > /dev/null || exit 1"]
test: curl --fail http://localhost:3000/healthz
interval: 5s
timeout: 5s
retries: 10
@ -39,10 +40,8 @@ services:
- db-data:/bitnami/postgresql
environment:
POSTGRES_PASSWORD: ${POSTGRES_ADMIN_PASSWORD}
#POSTGRES_USER: ${POSTGRES_USER}
#POSTGRES_DB: ${POSTGRES_DB}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U twenty -d default"]
test: pg_isready -U twenty -d default
interval: 5s
timeout: 5s
retries: 10

View File

@ -1,7 +1,7 @@
#!/bin/sh
# Check if the initialization has already been done and that we enabled automatic migration
if [ "${ENABLE_DB_MIGRATIONS}" = "true" ] && [ ! -f /app/${STORAGE_LOCAL_PATH}/db_initialized ]; then
if [ "${ENABLE_DB_MIGRATIONS}" = "true" ] && [ ! -f /app/${STORAGE_LOCAL_PATH:-.local-storage}/db_initialized ]; then
echo "Running database setup and migrations..."
# Run setup and migration scripts
@ -10,7 +10,7 @@ if [ "${ENABLE_DB_MIGRATIONS}" = "true" ] && [ ! -f /app/${STORAGE_LOCAL_PATH}/d
# Mark initialization as done
echo "Successfuly migrated DB!"
touch /app/${STORAGE_LOCAL_PATH}/db_initialized
touch /app/${STORAGE_LOCAL_PATH:-.local-storage}/db_initialized
fi
# Continue with the original Docker command

View File

@ -6,7 +6,23 @@ sidebar_custom_props:
---
# Step by step instructions:
1. Copy the [.env.example](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/.env.example) into a `.env` in the same directory where your `docker-compose.yml` file will be
## One command installation
Install the project with the command below. By default, it installs the latest version from the main branch.
```bash
bash <(curl -sL https://git.new/20)
```
## Custom Installation:
Set VERSION for a specific docker image version, BRANCH for a specific clone branch:
```bash
VERSION=x.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.com/twentyhq/twenty/main/install.sh)
```
## Manual installation
1. Copy the [.env.example](https://github.com/twentyhq/twenty/blob/main/packages/twenty-docker/prod/.env.example) into a `.env` in the same directory where your `docker-compose.yml` file will be
2. Run the command `openssl rand -base64 32` three times, make note of the string for each
3. In your .env file, replace the three "replace_me_with_a_random_string_access" with the three random strings you just generated.
@ -17,78 +33,26 @@ REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh
FILE_TOKEN_SECRET=replace_me_with_a_random_string_refresh
```
4. Create a `docker-compose.yml` file from the example below.
4. Copy the [docker-compose.yml](https://github.com/twentyhq/twenty/blob/main/packages/twenty-docker/prod/docker-compose.yml) in the same directory as your `.env` file.
5. Run the command `docker-compose up -d`
6. Go to http://localhost:3001 and see your docker instance.
6. Go to http://localhost:3000 and see your docker instance.
## Troubleshooting
### Not able to login
If you encounter errors, (not able to log into the application after inputting an email) after the inital setup, try running `docker exec -it twenty-backend-1 yarn nx database:reset` and see if that solves your issue.
If you encounter errors, (not able to log into the application after inputting an email) after the inital setup, try running the following commands and see if that solves your issue.
```
docker exec -it twenty-server-1 yarn
docker exec -it twenty-server-1 yarn nx database:reset
```
### Cannot connect to server, running behind a reverse proxy
Complete step three and four with :
Complete step three and four with:
3. Add `SERVER_URL=https://<your-api-url.com>` to your `.env`
4. Uncomment `SERVER_URL=${SERVER_URL}` in your `docker-compose.yml`
3. Update `SERVER_URL=https://<your-api-url.com>` in your `.env`
## Production docker containers
### Persistence
Prebuilt images for both Postgres, frontend, and back-end can be found on [docker hub](https://hub.docker.com/r/twentycrm/). Note that the Postgres container will not persist data if your server is not configured to be stateful (for example Heroku). You probably want to configure a special stateful resource for the database.
## Environment Variables
- Copy this `.env.example` file into a `.env` in the same directory as your `docker-compose.yml` file
- Find the `.env.example` [here](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/.env.example).
## Docker Compose file
We will soon update the documentation with an up-to-date docker compose file.
Here is one that was proposed on Discord by a community member:
```yaml
version: "3.9"
services:
twenty:
image: twentycrm/twenty-front:${TAG}
ports:
- 3001:3000
environment:
- SIGN_IN_PREFILLED=${SIGN_IN_PREFILLED}
- REACT_APP_SERVER_BASE_URL=${LOCAL_SERVER_URL}
depends_on:
- backend
backend:
image: twentycrm/twenty-server:${TAG}
ports:
- 3000:3000
environment:
- SIGN_IN_PREFILLED=${SIGN_IN_PREFILLED}
- PG_DATABASE_URL=${PG_DATABASE_URL}
- FRONT_BASE_URL=${FRONT_BASE_URL}
- PORT=3000
- STORAGE_TYPE=local
- STORAGE_LOCAL_PATH=.local-storage
- ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
- LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
- REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
- FILE_TOKEN_SECRET=${FILE_TOKEN_SECRET}
# Uncomment if behind a reverse proxy
# - SERVER_URL=${SERVER_URL}
depends_on:
- db
db:
image: twentycrm/twenty-postgres:${TAG}
volumes:
- twenty-db-data:/bitnami/postgresql
environment:
- POSTGRES_PASSWORD=${POSTGRES_ADMIN_PASSWORD}
volumes:
twenty-db-data:
```
By default the docker-compose will create volumes for the Database and local storage of the Server. Note that the containers will not persist data if your server is not configured to be stateful (for example Heroku). You probably want to configure a special stateful resource for this purpose.