Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
193 lines
7.2 KiB
Bash
Executable File
193 lines
7.2 KiB
Bash
Executable File
#!/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
|
||
|
||
# Check if docker compose version is >= 2
|
||
if [ "$(docker compose version --short | cut -d' ' -f3 | cut -d'.' -f1)" -lt 2 ]; then
|
||
echo -e "\t❌ Docker Compose is outdated. Please update Docker Compose to version 2 or higher.\n\t\tSee https://docs.docker.com/compose/install/linux/"
|
||
exit 1
|
||
fi
|
||
# Check if docker-compose is installed, if so issue a warning if version is < 2
|
||
if command -v docker-compose &>/dev/null; then
|
||
if [ "$(docker-compose version --short | cut -d' ' -f3 | cut -d'.' -f1)" -lt 2 ]; then
|
||
echo -e "\n\t⚠️ 'docker-compose' is installed but outdated. Make sure to use 'docker compose' or to upgrade 'docker-compose' to version 2.\n\t\tSee https://docs.docker.com/compose/install/standalone/\n"
|
||
fi
|
||
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
|
||
|
||
# Download a file, failing loudly instead of writing an HTTP error page to disk.
|
||
# curl retries 429/5xx on its own; raw.githubusercontent.com throttles by IP.
|
||
function download {
|
||
local url=$1
|
||
local dest=$2
|
||
if ! curl -fsSL --retry 3 --retry-delay 2 -o "$dest.tmp" "$url"; then
|
||
rm -f "$dest.tmp"
|
||
echo -e "\t❌ Failed to download $url"
|
||
echo -e "\t\tIf this is a 404, the twenty/<version> ref does not exist: releases that predate the twenty/ tag namespace (v2.9.0 and older) are not supported."
|
||
echo -e "\t\tAnything else is usually GitHub rate limiting your network, in which case retrying in a minute will work."
|
||
exit 1
|
||
fi
|
||
mv "$dest.tmp" "$dest"
|
||
}
|
||
|
||
# Use environment variables VERSION and BRANCH, with defaults if not set
|
||
release_tag_pattern='^v[0-9]+\.[0-9]+\.[0-9]+$'
|
||
if [[ -n "$VERSION" && ! "$VERSION" =~ $release_tag_pattern ]]; then
|
||
echo -e "\t❌ VERSION must be a full release tag such as v2.38.1, omit it to install the latest release."
|
||
exit 1
|
||
fi
|
||
version=${VERSION:-$(curl -fsS --retry 3 --retry-delay 2 "https://hub.docker.com/v2/repositories/twentycrm/twenty/tags?page_size=100" | grep -o '"name":"[^"]*"' | cut -d'"' -f4 | grep -E "$release_tag_pattern" | sort -V | tail -n1)}
|
||
if [ -z "$version" ]; then
|
||
echo -e "\t❌ Unable to resolve the latest release from Docker Hub. Check your network, or set VERSION explicitly."
|
||
exit 1
|
||
fi
|
||
# Deriving the branch from the image tag keeps docker-compose.yml and the image in lockstep.
|
||
branch=${BRANCH:-twenty/$version}
|
||
|
||
echo "🚀 Using docker version $version and Github 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 twenty/packages/twenty-docker/docker-compose.yml in it
|
||
echo -e "\t• Copying docker-compose.yml"
|
||
download "https://raw.githubusercontent.com/twentyhq/twenty/$branch/packages/twenty-docker/docker-compose.yml" docker-compose.yml
|
||
|
||
# Copy twenty/packages/twenty-docker/.env.example to .env
|
||
echo -e "\t• Setting up .env file"
|
||
download "https://raw.githubusercontent.com/twentyhq/twenty/$branch/packages/twenty-docker/.env.example" .env
|
||
|
||
# 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 "# === Randomly generated secret ===" >> .env
|
||
echo "ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env
|
||
|
||
echo "" >> .env
|
||
echo "PG_DATABASE_PASSWORD=$(openssl rand -hex 32)" >> .env
|
||
|
||
echo -e "\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
|
||
sed -E -i '' "s|^SERVER_URL=http://localhost:[0-9]+|SERVER_URL=http://localhost:$new_port|g" .env
|
||
else
|
||
sed -i'' "s/$port:$port/$new_port:$port/g" docker-compose.yml
|
||
sed -E -i'' "s|^SERVER_URL=http://localhost:[0-9]+|SERVER_URL=http://localhost:$new_port|g" .env
|
||
fi
|
||
port=$new_port
|
||
done
|
||
fi
|
||
|
||
# Ask user if they want 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 "Waiting for server to be healthy, it might take a few minutes while we initialize the database..."
|
||
# Tail logs of the server until it's ready
|
||
docker compose logs -f server &
|
||
pid=$!
|
||
while [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-server-1) = "healthy" ]; do
|
||
sleep 1
|
||
done
|
||
kill $pid
|
||
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 they want 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
|