DEV Community

loading...
Cover image for Create a Laravel & Any Front-end Development StackπŸ±β€πŸ

Create a Laravel & Any Front-end Development StackπŸ±β€πŸ

aeweda
A Slowly Cooked DevOps Engineer
・12 min read

Table Of Contents


Introduction

The purpose of this guide is to make your application as easy as possible to self deploy on the developers side without them bothering with installing mysql, nginx, and a ton of stuff just to start doing what they do best.


Requirements

Before we start, we're gonna need a few dependencies to get this project underway

Docker
Docker-Compose
Node/npm

To install Docker & Docker-Compose simply use the following commands

# Install Docker
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ newgrp docker
$ sudo rm -f get-docker.sh
# Install Docker-Compose
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
Enter fullscreen mode Exit fullscreen mode

Or look them up yourself on the official Docker Documentation Install Docker

For npm go with nvm for ease of use

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
$ nvm install node
Enter fullscreen mode Exit fullscreen mode

Preparing Laravel Backend Api

Rather than go through all the hassle of installing php/composer and running the composer command to generate the laravel project you can simply use the following command to create the laravel project

curl -s https://laravel.build/folder-name | bash
Enter fullscreen mode Exit fullscreen mode

Notice: Change folder-name in the command with any folder name you'd like
Lets delete the package.json file in the root directory since we are not gonna be using it


Folder Structure

add the missing folders from the following structure and leave them empty for now
.
β”œβ”€β”€ tools
β”‚Β Β  β”œβ”€β”€ dockerfiles
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ ci
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dev-images
β”‚Β Β  β”‚Β Β  └── prod-images
β”‚Β Β  └── scripts
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ ci
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ local
β”œβ”€β”€ app
β”œβ”€β”€ bootstrap
β”œβ”€β”€ config
β”œβ”€β”€ database
β”œβ”€β”€ public
β”œβ”€β”€ resources
β”œβ”€β”€ routes
β”œβ”€β”€ server
β”‚Β Β  └── config
β”œβ”€β”€ storage
β”œβ”€β”€ tests
β”œβ”€β”€ client
└── vendor


Choosing Your Favorite Front-end

Or as people say "Pick your poison"
Any of the big 3 will do fine here

lets go with vue.js, inside the root folder run the following command

$ npm install -g @vue/cli
$ vue create client # You can use `vue ui` as an alternative to the cli to generate an interface and guide you through the steps
Enter fullscreen mode Exit fullscreen mode

now you're gonna be greeted with a lot of options to choose from, those are entirely up to you choose with the space bar and I recommend having vuex router

notice: (At the current time of writing!) avoid choosing a unit/e2e/node-sass preprocessor with typescript as it can have some build problems / errors, simply install those after you finish compiling the project with npm like

$ npm install jest
Enter fullscreen mode Exit fullscreen mode

in case the project is created with a repo simply do a

sudo rm -rf client/.git
Enter fullscreen mode Exit fullscreen mode

Alternatively you can follow the official guide yourself on vue.js or go with React or Angular(urgh)


Preparing Docker Images

Okay now for the fun part, lets start creating the images the application will use

  • Backend image

in tools/dockerfiles/dev-images/ create backend.dockerfile

# ! Image
FROM php:8.0.6-fpm-alpine3.12

# ? Set Working Directory
WORKDIR /var/www/html

# ? Install Mysql Extensions
RUN docker-php-ext-install pdo pdo_mysql

# ? Install and enable PHP Redis extension
# ? Redis extension is not provided with the PHP Source
# ? pecl install will download and compile redis
# ? docker-php-ext-enable will enable it
# ? finally apk del to maintain a small image size
RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
 && pecl install redis-5.3.4 \
 && docker-php-ext-enable redis \
 && apk del .build-deps

# ? Install & Configure gd
RUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev && \
  docker-php-ext-configure gd \
    --with-freetype \
    --with-jpeg \
  NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
  docker-php-ext-install -j$(nproc) gd && \
  apk del --no-cache freetype-dev libpng-dev libjpeg-turbo-dev

# ? Install git (required by Composer)
RUN apk add git

# *** Install Composer
RUN php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer
Enter fullscreen mode Exit fullscreen mode
  • Client image

in tools/dockerfiles/dev-images/ create client.dockerfile

# ! Image
FROM node:14-alpine

# ? Set Working Directory
WORKDIR /var/www/client

# * Install PM2 to serve the app
RUN npm install pm2 -g

# ? Serve the application on start
CMD command pm2 serve ./dist/ 8080 --spa --watch && pm2 log
Enter fullscreen mode Exit fullscreen mode

those are the images we'll need for now


php & php-fpm config

whether or not you want to change something in php.ini or www.conf you should at least have them exposed in case you wanna edit them later
we'll do that by creating them and linking them inside the containers

in server/config create php.ini & www.conf files and paste the following gist content accordingly


nginx Config

We are going to need a self signed certificate, inside the project's root directory run the following command

$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./server/key.pem -out ./server/cert.pem
Enter fullscreen mode Exit fullscreen mode

notice: skip all the prompts with enter
once generated, lets open up key.pem permissions so that we can add it to the repo

sudo chmod 644 server/key.pem
Enter fullscreen mode Exit fullscreen mode

for nginx config, create a default.conf file inside the server folder and paste the following content

server
{
    # Listen To HTTPS port
    listen                  443 ssl http2;
    listen                  [::]:443 ssl http2;

    # Define Domain Name
    server_name             localhost;
    server_tokens           off;

    # Security Headers
    add_header X-Frame-Options              "SAMEORIGIN";
    add_header X-XSS-Protection             "1; mode=block";
    add_header X-Content-Type-Options       "nosniff";
    add_header Referrer-Policy              "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy      "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src https: wss:" always;
    add_header Strict-Transport-Security    "max-age=31536000; includeSubDomains" always;

    # Index Fallback
    index index.html index.htm index.php;

    # Default Charset
    charset utf-8;

    # Redirect Everything to Front-end
    location / {
        proxy_pass                    http://client:8080/;
        proxy_redirect                off;
        proxy_set_header              Host $host;
        proxy_set_header              X-Real-IP $remote_addr;
        proxy_set_header              X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header              X-Forwarded-Host $server_name;
    }

    # Redirect everything after /api/ to Backend
    location /api/ {
        try_files $uri $uri/ public/index.php?$query_string;
    }

    # Handle PHP Files
    location ~ \.php$ {
        fastcgi_pass                  backend:9000;
        fastcgi_index                 index.php;
        fastcgi_buffers               8 16k;
        fastcgi_buffer_size           32k;
        fastcgi_param                 SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_hide_header           X-Powered-By;
        include                       fastcgi_params;
    }

    ###################### Content #########################

    # Deny Access to Files
    location ~ /\.(?!well-known).* {
        deny all;
    }

    # robots.txt
    location = /robots.txt {
        log_not_found off;
        access_log    off;
    }

    ######################## Compression ####################

    # Add global gzip compression to all other files
    gzip                on;
    gzip_comp_level     5;
    gzip_min_length     256;
    gzip_proxied        any;
    gzip_vary           on;
    gzip_types
        application/atom+xml
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rss+xml
        application/vnd.geo+json
        application/vnd.ms-fontobject
        application/x-font-ttf
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xml
        font/opentype
        image/bmp
        image/svg+xml
        image/x-icon
        text/cache-manifest
        text/css
        text/plain
        text/vcard
        text/vnd.rim.location.xloc
        text/vtt
        text/x-component
        text/x-cross-domain-policy
        application/octet-stream;

    ######################## SSL ###########################

    # SSL Certificates
    ssl_certificate         conf.d/cert.pem;
    ssl_certificate_key     conf.d/key.pem;

    # SSL Config
    ssl_session_timeout  1d;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_tickets  off;

    # Mozilla Intermediate configuration
    ssl_protocols        TLSv1.2 TLSv1.3;
    ssl_ciphers          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
}

# Redirect HTTP to HTTPs
server
{
    listen                  80;
    listen                  [::]:80;
    server_name             localhost;

    location / {
        return 301 https://localhost$request_uri;
    }

}
Enter fullscreen mode Exit fullscreen mode

.env As a Blueprint

Before we proceed any further we must adapt the mentality of having our .env file as the true source of configurations, or at least the ones we want to mess with

This will be reflected in the next steps and emphasized upon


Connecting Everything

first of all, we need to stop our front-end dev server from intercepting anything after /api/ and redirecting the traffic to nginx

we can do that by modifying the vue.config.js file or the file that houses your webpack.config, if the file doesn't exist simply create it yourself

const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
    .BundleAnalyzerPlugin;

const plugins = [];

plugins.push(new BundleAnalyzerPlugin({ analyzerMode: "disabled" }));

module.exports = {
    devServer: {
        proxy: {
            "^/api": {
                target: "https://webserver/",
                ws: true,
                secure: false
            },
        },
        port: 8081
    },
    configureWebpack: {
        plugins
    },
};
Enter fullscreen mode Exit fullscreen mode

next we are going to need to add some variables to our .env.example file to use in our docker-compose file and other stuff

# Define Application Specific Keys
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

# Log Channel Variables
LOG_CHANNEL=stack
LOG_LEVEL=debug

# Database Variables
DB_CONNECTION=mysql
DB_HOST=database
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=someuser
DB_PASSWORD="lapassord@100"
DB_ROOT_PASSWORD="password@9000"

# Nginx Variables
HTTP_PORT=80:80
HTTPS_PORT=443:443

# BroadCast, Queue, Session Variables
BROADCAST_DRIVER=log
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

# Cache Variables
CACHE_DRIVER=redis
REDIS_HOST=cache
REDIS_PASSWORD=null
REDIS_PORT=6379

# Mail Variables
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

# Pusher Server Variables
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
Enter fullscreen mode Exit fullscreen mode

create the docker-compose.yml file in the root directory and paste in the following content

# ! Docker-Compose Specification
version: "3.8"

# ! Define Project Network
networks:
  StackName:

# ? App Stack
services:
  # ? Bring up Nginx After Backend and Link Volumes
  webserver:
    image: nginx:1.19.6-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "${HTTP_PORT}"
      - "${HTTPS_PORT}"
    volumes:
      - ./server/:/etc/nginx/conf.d
    networks:
      - StackName

  # ? Bring up Mysql and configure the Database
  database:
    image: mysql:8.0.21
    container_name: database
    restart: unless-stopped
    tty: true
    expose:
      - "3306"
    environment:
      MYSQL_DATABASE: "${DB_DATABASE}"
      MYSQL_USER: "${DB_USERNAME}"
      MYSQL_PASSWORD: "${DB_PASSWORD}"
      MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
      SERVICE_TAG: dev
      SERVICE_NAME: database
    volumes:
      - ./storage/database:/var/lib/mysql
    networks:
      - StackName

  # ? Build, Bring up Backend Container
  backend:
    build:
      context: .
      dockerfile: tools/dockerfiles/dev-images/backend.dockerfile
    container_name: backend
    volumes:
      - ./:/var/www/html
      - ./server/config/php.ini:/usr/local/etc/php/php.ini
      - ./server/config/www.conf:/usr/local/etc/php-fpm.d/www.conf
    expose:
      - "9000"
    depends_on:
      - database
      - cache
    networks:
      - StackName

  # ? Bring Up Client UI
  client:
    build:
      context: .
      dockerfile: tools/dockerfiles/dev-images/client.dockerfile
    container_name: client
    volumes:
      - ./client/:/var/www/client
    expose:
      - "8080"
    networks:
      - StackName

  # ? Bring Up Caching Layer
  cache:
    image: redis:6.0.9
    container_name: cache
    expose:
      - "6379"
    networks:
      - StackName

  # * Helper Container for Serving the Front-end
  npm:
    image: node:14
    container_name: npm
    volumes:
      - ./client:/var/www/client
    working_dir: /var/www/client
    entrypoint: ["npm"]
    networks:
      - StackName

Enter fullscreen mode Exit fullscreen mode

Change all instances of StackName with your application name


Create a The init Script

Finally we get to the whole point of this guide, having the user start everything by running a single script which is what we are gonna do here

create init.sh file in the root directory of the project, run the following command from the terminal to give it execute permissions

$ chmod +x init.sh
Enter fullscreen mode Exit fullscreen mode

paste in the following content

#!/bin/bash

#?###################################################################################################
#?                                                                                                  #
#?                                      Output Helper Methods                                       #
#?                                                                                                  #
#?###################################################################################################

trap "exit" INT

function blue_text_box()
{
  echo " "
  local s="$*"
  tput setaf 3
  echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 4)$s$(tput setaf 3) |
| ${s//?/ } |
 -${s//?/-}-"
  tput sgr 0
  echo " "
}

function red_text_box()
{
  echo " "
  local s="$*"
  tput setaf 3
  echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 1)$s$(tput setaf 3) |
| ${s//?/ } |
 -${s//?/-}-"
  tput sgr 0
  echo " "
}

function green_text_box()
{
  echo " "
  local s="$*"
  tput setaf 3
  echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 2)$s$(tput setaf 3) |
| ${s//?/ } |
 -${s//?/-}-"
  tput sgr 0
  echo " "
}

#!###################################################################################################
#!                                                                                                  #
#!                                       Script Start                                               #
#!                                                                                                  #
#!###################################################################################################

# ! Add .env file
cp .env.example .env

# ! bring down any service instance if it exists
red_text_box 'Removing Old Stack if It Exists'
docker-compose down

# ? Change Permissions for Artisan
chmod +x artisan

# ? Remove everything in the storage/database & bootstrap/cache directory
sudo rm -rf storage/database/*
sudo rm -rf bootstrap/cache/*.php

# TODO: Start & Build Container Stack
blue_text_box 'Rebuilding the docker images & Starting them'
docker-compose up -d --build

# * Install Laravel Dependencies
green_text_box 'Installing Laravel Dependencies'
docker exec -i backend composer install

# * Install Front-end Dependencies & Build
green_text_box 'Installing Dependencies & Build for User UI'
docker exec -i client npm i
docker exec -i client npm run build

# ! Generate Key & Caching/Optimizing Config
red_text_box 'Generating Laravel'
docker exec -i backend php artisan key:generate


# ! Migrate and Generate Passport Encryption Key
red_text_box 'Migrating & Seeding'
sleep 10
docker exec -i backend composer dump-autoload
docker exec -i backend php artisan migrate:fresh --seed
docker exec -i backend chmod o+w ./storage/ -R

Enter fullscreen mode Exit fullscreen mode

Create The CLI

Lets make interacting the stack easy for developers by creating a simple psuedo-cli

in our tools/scripts/local lets create a cli.sh file and run

$ chmod +x cli.sh
Enter fullscreen mode Exit fullscreen mode

paste in the following content or modify the naming scheme to fit your needs by modifying "stack helper instances" and/or changing the container names

#!/bin/bash

#?###################################################################################################
#?                                                                                                  #
#?                                      Output Helper Methods                                       #
#?                                                                                                  #
#?###################################################################################################

# ! little helpers for terminal print control and key input
select_option (){
  ESC=$(printf '%b' "\033")

  cursor_blink_on() {
    printf '%s' "$ESC[?25h"
  }

  cursor_blink_off() {
    printf '%s' "$ESC[?25l"
  }

  cursor_to() {
    printf '%s' "$ESC[$1;${2:-1}H"
  }

  print_option() {
    printf '   %s ' "$1"
  }

  print_selected() {
    printf '  %s' "$ESC[7m $1 $ESC[27m"
  }

  get_cursor_row() {
    IFS=';' read -sdR -p $'\E[6n' ROW COL; printf '%s' ${ROW#*[}
  }

  key_input() {
    read -s -n3 key 2>/dev/null >&2
    if [[ $key = $ESC[A ]]; then
      echo up
    fi
    if [[ $key = $ESC[B ]]; then
      echo down
    fi
    if [[ $key = ""  ]]; then
      echo enter
    fi
  }

   # initially print empty new lines (scroll down if at bottom of screen)
   for opt; do
     printf "\n"
   done

   # determine current screen position for overwriting the options
   local lastrow=$(get_cursor_row)
   local startrow=$(($lastrow - $#))

   # ensure cursor and input echoing back on upon a ctrl+c during read -s
   trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
   cursor_blink_off

   local selected=0
   while true; do
     # print options by overwriting the last lines
     local idx=0
     for opt; do
       cursor_to $((startrow + idx))
       if [[ $idx == $selected ]]; then
         print_selected "$opt"
       else
         print_option "$opt"
       fi
       ((idx++))
     done

     # user key control
     case $(key_input) in
       enter) break;;
       up)    ((selected--));
         if (( $selected < 0 )); then selected=$(($# - 1)); fi;;
         down)  ((selected++));
           if (( selected > $# )); then selected=0; fi;;
         esac
       done

       # cursor position back to normal
       cursor_to $lastrow
       printf "\n"
       cursor_blink_on

       return "$selected"
}

#!###################################################################################################
#!                                                                                                  #
#!                                       Script Start                                               #
#!                                                                                                  #
#!###################################################################################################

# Print Instructions
printf '\n> %s\n\n' "$(tput setaf 3)Please Choose your Shell$(tput sgr 0)":

# Options
options=("Bash" "Zsh" "IDK")

select_option "${options[@]}"
choice=$?

index=$choice
value=${options[$choice]}

case $value in 
  Bash)  ## User selected Bash
   shellrc=".bashrc"
   ;;
  Zsh) ## User Selected zsh
   shellrc=".zshrc"
   ;;
  IDK) ## User doesn't know
   shellrc=".bashrc"
   ;;
esac

####################################################################################################

# ? Remove Prexisting CLI Script
sed -n -i '1,/# StackHelper CLI START/p;/# StackHelper CLI END/,$p' $HOME/$shellrc
sed -i '/# StackHelper CLI START/d' $HOME/$shellrc
sed -i '/# StackHelper CLI END/d' $HOME/$shellrc

####################################################################################################

# ? Echo the CLI in the user's .shellrc
echo '
# StackHelper CLI START
# This function serves to integrate
# the dependency-less CLI for 
# Interactive Events Platform

shelper() {
    if [[ $@ == "build" ]]; then
        command docker exec -i client npm run build
    elif [[ $@ == "serve" ]]; then
        command docker-compose run --rm -p "8081:8081" npm run serve
    elif [[ $@ == "install client" || $@ == "i client" ]]; then
        command docker exec -i client npm install
    elif [[ $@ == "install api" || $@ == "i api" ]]; then
        command docker exec -i backend composer install &&
        command docker exec -i backend composer dump-autoload &&
        command docker exec -i backend php artisan key:generate &&
        command docker exec -i backend php artisan migrate:fresh
    elif [[ $@ == "api migrate" ]]; then
        command docker exec -i backend php artisan migrate
    elif [[ $@ == "api seed" ]]; then
        command docker exec -i backend php artisan db:seed
    elif [[ $@ == "api ms" ]]; then
        command docker exec -i backend php artisan migrate:fresh --seed
    elif [[ $@ == "api refresh" ]]; then
        command docker exec -i backend composer dump-autoload &&
        command docker exec -i backend php artisan migrate:fresh --seed
    elif [[ $@ == "stack refresh" || $@ == "stack r" ]]; then
        command docker-compose down && command docker-compose up -d
    elif [[ $@ == "-h" || $@ == "--help" ]]; then
        echo " 
$(tput setaf 3)Stack Helper CLI$(tput sgr 0)

$(tput setaf 3)Usage:$(tput sgr 0)
    shelper [options] [arguments]

$(tput setaf 3)Options:$(tput sgr 0)

-h, --help          Displays this help page

$(tput setaf 3)Arguments:$(tput sgr 0)
    $(tput setaf 2)build$(tput sgr 0)                 Builds the Front-end
    $(tput setaf 1)serve$(tput sgr 0)                 Serves the front-end through port $(tput setaf 5)8081$(tput sgr 0)
    $(tput setaf 3)install client$(tput sgr 0)        Installs the npm Dependencies Shorthand -> $(tput setaf 5)i ui$(tput sgr 0)
    $(tput setaf 3)install api$(tput sgr 0)           Installs Laravel Dependencies, Generate Keys & Migrate DB Shorthand -> $(tput setaf 5)i api$(tput sgr 0)
    $(tput setaf 4)api migrate$(tput sgr 0)           Pushes Laravel Migrations to DB
    $(tput setaf 4)api seed$(tput sgr 0)              Seed the Database
    $(tput setaf 4)api ms$(tput sgr 0)                Migrate & Seed
    $(tput setaf 4)api refresh$(tput sgr 0)           Composer dump-autoload, Migrate & Seed
    $(tput setaf 6)stack refresh$(tput sgr 0)         Refresh the docker stack down/up Shorthand -> $(tput setaf 5)stack r$(tput sgr 0)
"
    else
        echo "
$(tput setaf 1)Please Input a Valid Argument$(tput sgr 0)
OR 
Check the Proper Syntax with
$(tput setaf 3)$ shelper -h$(tput sgr 0)
"
    fi
}

# For Tab-Completion (Basic/Rudimentary)
_shelper_completions()
{
  COMPREPLY+=("build")
  COMPREPLY+=("serve")
  COMPREPLY+=("install")
  COMPREPLY+=("api")
  COMPREPLY+=("stack")
}

complete -F _shelper_completions shelper
# StackHelper CLI END
' >> $HOME/$shellrc
Enter fullscreen mode Exit fullscreen mode

Now lets add our cli script execution to the init.sh script by adding the following lines to the script file

# ! Install CLI
red_text_box 'Installing CLI'
./tools/scripts/local/cli.sh
Enter fullscreen mode Exit fullscreen mode

note: after installing the pseudo cli you'll need to refresh your shell instance with something like

$ bash
$ zsh
Enter fullscreen mode Exit fullscreen mode

Create The Repo

At this point we're technically done lets create the repo and push it to your favorite repo hosting service (hub,lab,bucket...whatever) run the following command in the project's root directory

$ git init
$ git add .
$ git commit -m "First commit or whatever commit message you want"
$ git remote add origin  <REMOTE_URL> 
$ git push origin <main/master/whatever>
Enter fullscreen mode Exit fullscreen mode

now anyone clones the project, simply runs

$ ./init
Enter fullscreen mode Exit fullscreen mode

and he's up and running


Future Plans

With your request I can add a couple of follow up guides that cover the following

  • Creating a CI/CD Pipeline for the Project
  • Building Production Images
  • Deploying Production Builds

so let me know if you'd like that


TL;DR

We've successfully create an easy to use development stack that starts up by simply cloning and running ./init.sh, a small pseudo helper cli that helps developers interact with the with the containers

If you understand everything and would just like a repo that has all this instead of going through all this as a learning experience
you can find it here

if you find any typos or any problems in the process or having some comments about something that you don't like please let me know so that I can fix it or improve upon it, whichever the case may be

Discussion (0)