#!/bin/bash -e < Startr.sh
Logo

Startr. sh

Startr.sh

Building an App or Site?

We are the Best way to Build and Launch

Just run the following line in your terminal to get startred ;)

bash <(curl -sL startr.sh)

Follow industry best practices.

With Startr.sh, creating Docker images for your projects is streamlined and efficient.

Follow best practices and use popular frameworks like Python, Django, Flask, Node, and Startr.Style CSS.

After countless projects and years of experience, we provide you the tools and insights to launch your projects quickly and reliably.

Start the best way possible!

Docker CLI interface
What is Startr.sh? Startr.sh is both a script and a page that tells you about the script. - Learn more here.
How can I modify the script? At the bottom of this page is startr.sh. It's easier to understand and modify the code here.
#!/bin/bash
#
# Version 1.8.0
#
# Copyright (c) Startr LLC. All rights reserved.
# This script is licensed under the GNU Affero General Public License v3.0.
# For more information, see https://www.gnu.org/licenses/agpl-3.0.en.html
#
# Combined Build and Build 'n' Run Script
# This script builds a Docker image for the 
# current project and optionally runs it.
#
# `curl -fsSL startr.sh|bash` to run this script
# or pass arguments such as `build` or `run`
#
# `curl -fsSL startr.sh|bash -s build` to build
#
# Answers are read from the terminal, since `curl | bash` feeds this script on stdin.
# 

ascii_art="
                        _____  _                 _                  _     
                       /  ___|| |               | |                | |    
                       \  --. | |_   __ _  _ __ | |_  _ __     ___ | |__  
                        \`--. \| __| / _\` || '__|| __|| '__|   / __|| '_ \ 
                       /\__/ /| |_ | (_| || |   | |_ | |    _ \__ \| | | |
                       \____/  \__| \__,_||_|    \__||_|   (_)|___/|_| |_|

   _____ _             _   _                               _           _              _       _     _   
  / ____| |           | | (_)                             (*)         | |            (H)     | |   | |  
 | (___ | |_ __ _ _ __| |_ _ _ __   __ _   _ __  _ __ ___  _  ___  ___| |_ ___   _ __ _  __ _| |__ | |_ 
  \___ \| __/ _\` | '__| __| | '_ \ / _\` | | '_ \| '__/ _ \| |/ _ \/ __| __/ __| | '__| |/ _\` | '_ \| __|
  ____) | || (_| | |  | |_| | | | | (_| | | |_) | | | (_) | |  __/ (__| |_\__ \ | |  | | (_| | | | | |_ 
 |_____/ \__\__,_|_|   \__|_|_| |_|\__, | | .__/|_|  \___/| |\___|\___|\__|___/ |_|  |_|\__, |_| |_|\__|
                                    __/ | | |            _/ |                            __/ |          
                                   |___/  |_|           |__/                            |___/           
"
about_startr="

Startr is a command line tool that streamlines your project setup, 
ensuring everything is in place so you can focus on development.


- Project Folder Check
    Verifies your project folder is correctly set up.

- Dockerfile Check and Creation
    Ensures a Dockerfile exists in your project folder. 
    If not, we creates one automatically.

  "


# Function to convert RGB to ANSI escape code
rgb_to_ansi() {
  printf "\033[38;2;%d;%d;%dm" "$1" "$2" "$3"
}

# Function to interpolate between two values
interpolate() {
  local start=$1
  local end=$2
  local factor=$3
  echo "$(( start + (end - start) * factor / 100 ))"
}

# Function to apply gradient coloring to ASCII art
apply_gradient() {
  local lines=()
  while IFS= read -r line; do
    lines+=("$line")
  done

  local total_lines=${#lines[@]}
  local start_colors=(255 0 0)    # Red
  local mid_colors=(128 128 255)      # Blue
  local end_colors=(128 0 128)    # Purple

  for i in "${!lines[@]}"; do
    local factor=$(( i * 200 / (total_lines - 1) ))  # factor range: 0 to 200
    local r g b

    if (( factor <= 100 )); then
      r=$(interpolate "${start_colors[0]}" "${mid_colors[0]}" "$factor")
      g=$(interpolate "${start_colors[1]}" "${mid_colors[1]}" "$factor")
      b=$(interpolate "${start_colors[2]}" "${mid_colors[2]}" "$factor")
    else
      factor=$(( factor - 100 ))
      r=$(interpolate "${mid_colors[0]}" "${end_colors[0]}" "$factor")
      g=$(interpolate "${mid_colors[1]}" "${end_colors[1]}" "$factor")
      b=$(interpolate "${mid_colors[2]}" "${end_colors[2]}" "$factor")
    fi

    local color
    color=$(rgb_to_ansi "$r" "$g" "$b")
    echo -e "${color}${lines[i]}\033[0m"
  done
}



# Apply gradient to the ASCII art and display it
echo "$ascii_art" | apply_gradient

# Print the about message
echo -e "$about_startr"



# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Emojis
BUILD_EMOJI='🛠️'
RUN_EMOJI='🚀'
SUCCESS_EMOJI='✅'
ERROR_EMOJI='❌'

# Function to ask the user for input
ask_for_input() {
    echo -e "${YELLOW}You haven't specified runtime arguments. Let's set them up interactively.${NC}"
    echo -e "${BLUE}Choose one of the following options:${NC}"
    echo -e "${BLUE}1. build               - Build the Docker image only.${NC}"
    echo -e "${BLUE}2. build_and_run       - (Default) Build the Docker image and run the container.${NC}"
    echo -e "${BLUE}3. run                 - Run the container using the existing image.${NC}"
    echo -e "${BLUE}Enter your choice (build/build_and_run/run) [default: build_and_run]:${NC}"
    read -r JOB_INPUT < /dev/tty
    # Default to "build_and_run" if nothing is entered
    if [ -z "$JOB_INPUT" ]; then
        JOB="build_and_run"
    else
        JOB=$JOB_INPUT
    fi
    while [[ "$JOB" != "build" && "$JOB" != "build_and_run" && "$JOB" != "run" ]]; do
        echo -e "${RED}Invalid input. Please enter 'build', 'build_and_run', or 'run':${NC}"
        read -r JOB < /dev/tty || exit 1
    done

    echo -e "${BLUE}Do you want to specify a platform? (Enter the platform or leave blank for none):${NC}"
    read -r PLATFORM < /dev/tty
}

# Function to provide information on repositories
explain_repo() {
    echo -e "${YELLOW}A git repository is a virtual storage of your project. It allows you to save versions of your code, which you can access when needed.${NC}"
    echo -e "${YELLOW}For more information, visit: https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control${NC}"
    echo -e "${BLUE}Would you like to clone the example repository 'https://github.com/Startr/WEB-flask'? (y/n):${NC}"
    read -r CLONE_REPO < /dev/tty
    if [ "$CLONE_REPO" == "y" ]; then
        git clone https://github.com/Startr/WEB-flask
        if ! cd WEB-flask; then
            echo -e "${RED}Failed to clone the repository. Please check your internet connection and try again.${NC}"
            exit 1
        fi
        echo -e "${GREEN}Repository cloned and switched to WEB-flask directory.${NC}"
    else
        echo -e ""
        echo -e "${BLUE}Would you like to set up a git flow repository in this directory? (y/n):${NC}"
        echo -e ""
        echo -e "${YELLOW}Note: Git flow is a branching model for Git, which helps with managing your repository.${NC}"
        echo -e "${YELLOW}      It needs git-flow-next: curl -fsSL startr.sh/scripts/tools.sh | bash -s -- git-flow-next${NC}" 
        read -r SETUP_REPO < /dev/tty
        echo -e ""
        if [ "$SETUP_REPO" == "y" ]; then
            # master, never main: the Startr Makefile's release flow expects it
            git flow init -d -m master -e develop -t v || exit 1
        else
            echo -e "OK... You'll need to set up a git repository before running this script."
            echo -e "${RED}Please set up a git repository before running this script.${NC}"

            exit 1
        fi

    fi
}

# Check if in a git repository
if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
    echo -e "${RED}You are not in a git repository.${NC}"
    explain_repo
fi

# Check if arguments are provided
if [ "$#" -lt 1 ]; then
    ask_for_input
else
    JOB=$1
    if [ "$JOB" != "build" ] && [ "$JOB" != "build_and_run" ] && [ "$JOB" != "run" ]; then
        echo -e "${RED}Invalid argument for JOB: $JOB. Use 'build', 'build_and_run', or 'run'.${NC}"
        exit 1
    fi
    PLATFORM=$2
fi


# Function to create a default Dockerfile based on project type
create_dockerfile() {
    PROJECT_TYPE=$1
    if [ "$PROJECT_TYPE" == "python-pipenv" ]; then
        cat > Dockerfile << EOF
# Python Dockerfile
FROM python:3.8-slim

WORKDIR /project
COPY Pipfile Pipfile.lock ./
RUN pip install pipenv && pipenv install --deploy --ignore-pipfile

COPY . .

EXPOSE 5000
CMD ["pipenv", "run", "python", "your_script.py"]
EOF
        DEFAULT_PORT=5000
    elif [ "$PROJECT_TYPE" == "node-bun" ]; then
        cat > Dockerfile << EOF
# Node.js (Bun) Dockerfile
FROM oven/bun:latest

WORKDIR /project
COPY . .
RUN bun install

EXPOSE 8080
CMD ["bun", "start"]
EOF
        DEFAULT_PORT=8080
    elif [ "$PROJECT_TYPE" == "node" ]; then
        cat > Dockerfile << EOF
# Node.js Dockerfile
FROM node:14

WORKDIR /project
COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 80
CMD ["node", "your_script.js"]
EOF
        DEFAULT_PORT=8080
    else
        echo -e "${RED}Unsupported project type: ${PROJECT_TYPE}. Please create a Dockerfile manually.${NC}"
        exit 1
    fi
}

# Check if a Dockerfile exists
if [ ! -f Dockerfile ]; then
    echo -e "${RED}No Dockerfile found.${NC}"

    # Detect project type and create Dockerfile
    if [ -f Pipfile ]; then
        echo -e "${BLUE}Python project with Pipenv detected.${NC}"
        create_dockerfile "python-pipenv"
    elif [ -f package.json ]; then
        if [ -f bun.lockb ]; then
            echo -e "${BLUE}Node.js project with Bun detected.${NC}"
            create_dockerfile "node-bun"
        else
            echo -e "${BLUE}Node.js project detected.${NC}"
            create_dockerfile "node"
        fi
    else
        echo -e "${RED}Could not detect project type. Please create a Dockerfile manually.${NC}"
        exit 1
    fi

    echo -e "${YELLOW}The Dockerfile exposes the default port ${DEFAULT_PORT}. For production, you may need to change this port.${NC}"
    echo -e "${BLUE}Please acknowledge that you understand this by typing 'yes':${NC}"
    read -r ACKNOWLEDGMENT < /dev/tty
    if [ "$ACKNOWLEDGMENT" != "yes" ]; then
        echo -e "${RED}Acknowledgment not received. Exiting.${NC}"
        exit 1
    fi

    echo -e "${BLUE}If you want more examples, visit our site and replace the Dockerfile as needed.${NC}"
fi

# Echo the chosen options
echo -e "${GREEN}JOB: $JOB${NC}"
if [ -n "$PLATFORM" ]; then
    echo -e "${GREEN}PLATFORM: $PLATFORM${NC}"
else
    echo -e "${YELLOW}No platform specified${NC}"
fi


# A container engine must be running. The offer asks before installing anything.
if ! docker info > /dev/null 2>&1; then
    echo -e "${YELLOW}Docker is not running.${NC}"
    STARTR_URL="${STARTR_URL:-https://startr.sh}"
    TOOLS_TMP="$(mktemp)"
    if curl -fsSL "$STARTR_URL/scripts/tools.sh" -o "$TOOLS_TMP" && grep -qF 'tools.sh: offers' "$TOOLS_TMP"; then
        bash "$TOOLS_TMP" runtime
    fi
    rm -f "$TOOLS_TMP"
    if ! docker info > /dev/null 2>&1; then
        echo -e "${RED}No running container engine, so nothing was built.${NC}"
        exit 1
    fi
fi

# Set PROJECTPATH to the path of the current directory
PROJECTPATH="$(git rev-parse --show-toplevel)"
# Set PROJECT to the lowercase version of the name of this directory
PROJECT=$(echo ${PROJECTPATH##*/} | awk '{print tolower($0)}')
# Set FULL_BRANCH to the name of the current Git branch
FULL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# remove everything after the last forward slash removed
BRANCH=${FULL_BRANCH##*/}
# Make BRANCH lowercase using bash parameter expansion
BRANCH="${BRANCH,,}"
# Set TAG to the output of the git describe --always --tag command, which returns a "unique identifier" for the current commit
TAG=$(git describe --always --tag)

# Print the values of the variables
echo -e "${BLUE}### Variables ###${NC}"
echo -e "${BLUE}PROJECTPATH=${NC}$PROJECTPATH"
echo -e "${BLUE}PROJECT=${NC}$PROJECT"
echo -e "${BLUE}FULL_BRANCH=${NC}$FULL_BRANCH"
echo -e "${BLUE}BRANCH=${NC}$BRANCH"

# Check if the RUN variable is set to 'run'
if [ "$JOB" == "run" ]; then
    echo -e "${YELLOW}${BUILD_EMOJI} Building Docker image...${NC}"
else
    echo -e "${YELLOW}${BUILD_EMOJI} Building Docker image without running...${NC}"
fi

# Check if PLATFORM is set and not empty
if [ -n "$PLATFORM" ]; then
    echo -e "${YELLOW}Building with specified platform: ${GREEN}$PLATFORM${NC}"
    PLATFORM_ARG="--platform $PLATFORM"
    BUILD_ARG="--build-arg PLATFORM=$PLATFORM"
    # Replace '/' with '-' in PLATFORM to create FLATPLATFORM
    FLATPLATFORM=$(echo "$PLATFORM" | tr '/' '-')
    # Use Dockerfile.$FLATPLATFORM
    DOCKERFILE="Dockerfile.$FLATPLATFORM"
    if [ ! -f "$DOCKERFILE" ]; then
        echo -e "${RED}Dockerfile for platform $FLATPLATFORM not found!${NC}"
        exit 1
    fi
else
    echo -e "${YELLOW}Building without specifying platform.${NC}"
    PLATFORM_ARG=""
    BUILD_ARG=""
    # If PLATFORM is not set, build the default Dockerfile
    DOCKERFILE="Dockerfile"
    FLATPLATFORM="default"
fi

echo -e "${BLUE}docker build -t startr/$PROJECT-$BRANCH:$TAG .${NC}"
echo -e "${BLUE}docker tag startr/$PROJECT-$BRANCH:$TAG startr/$PROJECT-$BRANCH:latest${NC}"

# Build step: run only if job is "build" or "build_and_run"
if [ "$JOB" == "build" ] || [ "$JOB" == "build_and_run" ]; then
    echo -e "${YELLOW}${BUILD_EMOJI} Building Docker image...${NC}"
    # ... (the docker build command remains unchanged) ...
    if docker build --load $PLATFORM_ARG $BUILD_ARG \
      -f $DOCKERFILE \
      -t startr/$PROJECT-$BRANCH:$TAG \
      -t startr/$PROJECT-$BRANCH:$FLATPLATFORM \
      -t startr/$PROJECT-$BRANCH:latest \
      .; then
        echo -e "${GREEN}${SUCCESS_EMOJI} Build successful!${NC}"
    else
        echo -e "${RED}${ERROR_EMOJI} Build failed!${NC}"
        exit 1
    fi
fi

# Check if running in an interactive shell
if [ -t 1 ]; then
    DOCKER_RUN_FLAGS="-it --rm"
else
    DOCKER_RUN_FLAGS=""
fi

# Run step: run if job is "build_and_run" or "run"
if [ "$JOB" == "build_and_run" ] || [ "$JOB" == "run" ]; then
    echo -e "${YELLOW}${RUN_EMOJI} Running the Docker container:${NC}"
    echo -e "${BLUE}docker run $DOCKER_RUN_FLAGS -p 8888:8888 \
        -p 8000:8000 \
        -p 8080:8080 \
        -p 9000:5000 \
        -v \"$PROJECTPATH:/project\" \
        --name $PROJECT-$BRANCH \
        startr/$PROJECT-$BRANCH:latest${NC}"
    if docker run $DOCKER_RUN_FLAGS \
      -p 8888:8888 \
      -p 8000:8000 \
      -p 8080:8080 \
      -p 9000:5000 \
      -v "$PROJECTPATH:/project" \
      --name $PROJECT-$BRANCH \
      startr/$PROJECT-$BRANCH:latest; then
        echo -e "${GREEN}${SUCCESS_EMOJI} Docker container launched!${NC}"
        echo -e "${YELLOW}Note: Port 9000 is mapped to 5000, 8080 is mapped to 8080, and 8888 is mapped to 8888.${NC}"
        echo -e "${YELLOW}Note: If you've exited the container it is no longer running.${NC}"
    else
        echo -e "${RED}${ERROR_EMOJI} Failed to run Docker container!${NC}"
        exit 1
    fi
else
    echo -e "${GREEN}${SUCCESS_EMOJI} Build completed. Not running the Docker container.${NC}"
fi

#
# That's all folks 
#