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)
Building an App or Site?
Just run the following line in your terminal to get startred ;)
bash <(curl -sL startr.sh)
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.
#!/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
#