Skip to content

Commit

Permalink
CI/CD fix (#40)
Browse files Browse the repository at this point in the history
Fixed deployment script to handle multiple instances.

---------

Co-authored-by: froala-travis-bot <[email protected]>
  • Loading branch information
froala-travis-bot and Bharatcel authored Jul 6, 2023
1 parent fe85c41 commit 3d497de
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 148 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ RUN npm install -g bower
RUN npm install
RUN bower install
FROM nginx:alpine
copy --from=build /app /usr/share/nginx/html
COPY --from=build /app /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
317 changes: 172 additions & 145 deletions deploy_sdk.sh
Original file line number Diff line number Diff line change
@@ -1,155 +1,182 @@
#!/bin/bash
if [ ${TRAVIS_PULL_REQUEST} != "false" ]; then echo "Not deploying on a pull request !!!" && exit 0; fi
PACKAGE_VERSION=`jq '.version' version.json | tr -d '"'`
export IMAGE_NAME=`echo "froala-${BUILD_REPO_NAME}_${TRAVIS_BRANCH}:${PACKAGE_VERSION}" | tr '[:upper:]' '[:lower:]'`
export BASE_DOMAIN="froala-infra.com"
export SDK_ENVIRONMENT=""
export DEPLOYMENT_SERVER=""
#!/usr/bin/env bash

# Steps
# Identify the build agent. Check whether build agent is same as deployment server
# Login to build server and build, run, check the new changes.
# --force-recreate for docker-compose
# --no-cache for docker build
# -f for npm install
# -v --rmi all for docker compose down

if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then echo "Not deploying on a pull request !!!" && exit 0; fi

# Define the global variables
BRANCH_NAME=$(echo "${TRAVIS_BRANCH}" | tr '[:upper:]' '[:lower:]')
PACKAGE_VERSION="$(jq '.version' version.json | tr -d '"')"
IMAGE_NAME="$(echo "froala-${BUILD_REPO_NAME}_${TRAVIS_BRANCH}:${PACKAGE_VERSION}" | tr '[:upper:]' '[:lower:]')"
BASE_DOMAIN="froala-infra.com"
AO_IDENTIFIER="${TRAVIS_BRANCH}"
BRANCH_LENGHT=$(echo "${TRAVIS_BRANCH}" |awk '{print length}')
LW_REPO_NAME=$(echo "${BUILD_REPO_NAME}" | tr '[:upper:]' '[:lower:]' | sed -e 's/-//g' -e 's/\.//g' -e 's/_//g')
CT_INDEX=0
MAX_DEPLOYMENTS_NR=0
SDK_ENVIRONMENT=""
DEPLOYMENT_SERVER=""
SERVICE_NAME=""
CONTAINER_NAME=""
CT_INDEX=0
AO_IDENTIFIER=`echo ${TRAVIS_BRANCH}`
echo "${AO_IDENTIFIER}"
OLDEST_CONTAINER=""

# Copy the ssh key
echo "${SSH_KEY}" | base64 --decode > /tmp/sshkey.pem
chmod 400 /tmp/sshkey.pem
export MAX_DEPLOYMENTS_NR=0
function get_max_deployments_per_env(){
local ENVIRONMENT=$1
echo "getting max deployments for environment ${ENVIRONMENT}"
MAX_DEPLOYMENTS_NR=`jq --arg sdkenvironment ${ENVIRONMENT} '.[$sdkenvironment]' version.json | tr -d '"'`
echo "detected max deployments: ${MAX_DEPLOYMENTS_NR}"
}
function generate_container_name(){
local LW_REPO_NAME=$1
local LW_SHORT_TRAVIS_BRANCH=$2
local SDK_ENVIRONMENT=$3
local DEPLOYMENT_SERVER=$4
echo "searching for ${LW_REPO_NAME} depl..."
sleep 1
RUNNING_DEPL=`ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} " sudo docker ps | grep -i ${LW_REPO_NAME}"`
echo "running depl var: ${RUNNING_DEPL}"
echo "looking for ${LW_REPO_NAME} deployments"
echo "getting indexes for oldest and latest deployed container"
DEPL='ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem '
DEPL="${DEPL} ${SSH_USER}@${DEPLOYMENT_SERVER} "
REL=' " sudo docker ps | grep -i '
DEPL="${DEPL} ${REL} "
DEPL="${DEPL} ${LW_REPO_NAME}-${AO_IDENTIFIER} "
REL='"'
DEPL="${DEPL} ${REL} "
echo "show docker containers ssh cmd: $DEPL"
echo ${DEPL} | bash > file.txt
echo "running conatiners: "
cat file.txt
CT_LOWER_INDEX=`cat file.txt | awk -F'-' '{print $NF }' | sort -nk1 | head -1`
CT_HIGHER_INDEX=`cat file.txt | awk -F'-' '{print $NF }' | sort -nk1 | tail -1`
echo "lowest index : ${CT_LOWER_INDEX} ; and highest index : ${CT_HIGHER_INDEX}"
if [ -z "${RUNNING_DEPL}" ]; then
echo "first deployment"
CT_INDEX=1
CONTAINER_NAME="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_INDEX}"
SERVICE_NAME="${LW_REPO_NAME}-${LW_SHORT_TRAVIS_BRANCH}"
else
echo "multiple deployments"
CT_INDEX=${CT_HIGHER_INDEX} && CT_INDEX=$((CT_INDEX+1))
OLDEST_CONTAINER="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_LOWER_INDEX}"
CONTAINER_NAME="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_INDEX}"
SERVICE_NAME="${LW_REPO_NAME}-${LW_SHORT_TRAVIS_BRANCH}-${CT_INDEX}"
echo "new index: ${CT_INDEX} & oldest horse out there: ${OLDEST_CONTAINER}"
fi
}
echo " Container port: ${CONTAINER_SERVICE_PORTNO}"
export BRANCH_NAME=`echo "${TRAVIS_BRANCH}" | tr '[:upper:]' '[:lower:]'`

# Select the deployment server based on the branch.
case "${BRANCH_NAME}" in
dev*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER=${FROALA_SRV_DEV} ;;
ao-dev*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER=${FROALA_SRV_DEV} ;;
ao*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER=${FROALA_SRV_DEV} ;;
qa*) SDK_ENVIRONMENT="qa" && DEPLOYMENT_SERVER=${FROALA_SRV_QA} ;;
qe*) SDK_ENVIRONMENT="qe" && DEPLOYMENT_SERVER=${FROALA_SRV_QE} ;;
rc*) SDK_ENVIRONMENT="stg" && DEPLOYMENT_SERVER=${FROALA_SRV_STAGING} ;;
release-master*) SDK_ENVIRONMENT="stg" && DEPLOYMENT_SERVER=${FROALA_SRV_STAGING} ;;
ft*) echo "Building only on feature branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;;
bf*) echo "Building only on bugfix branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;;
test*) echo "Building only on bugfix branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;;
*) echo "Not a deployment branch" && exit -1;;
dev*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER="${FROALA_SRV_DEV}";;
ao-dev*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER="${FROALA_SRV_DEV}";;
qa*) SDK_ENVIRONMENT="qa" && DEPLOYMENT_SERVER="${FROALA_SRV_QA}";;
qe*) SDK_ENVIRONMENT="qe" && DEPLOYMENT_SERVER="${FROALA_SRV_QE}";;
rc*) SDK_ENVIRONMENT="stg" && DEPLOYMENT_SERVER="${FROALA_SRV_STAGING}";;
release-master*) SDK_ENVIRONMENT="stg" && DEPLOYMENT_SERVER=${FROALA_SRV_STAGING};;
ft*) echo "Building only on feature branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;;
bf*) echo "Building only on bugfix branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;;
*) echo "Not a deployment branch" && exit 1;;
esac
get_max_deployments_per_env $SDK_ENVIRONMENT
echo "deploying on environment :${SDK_ENVIRONMENT}, on server ${DEPLOYMENT_SERVER}, max deployments: ${MAX_DEPLOYMENTS_NR}"
export BASE_DOMAIN="froala-infra.com"
SHORT_REPO_NAME="${BUILD_REPO_NAME:0:17}"
BRANCH_LENGHT=`echo ${TRAVIS_BRANCH} |awk '{print length}'`
if [ ${BRANCH_LENGHT} -lt 18 ]; then
SHORT_TRAVIS_BRANCH=${TRAVIS_BRANCH}

# Set the short branch name
if [ "${BRANCH_LENGHT}" -lt 18 ]; then
SHORT_TRAVIS_BRANCH="${TRAVIS_BRANCH}"
else
SHORT_TRAVIS_BRANCH="${TRAVIS_BRANCH:0:8}${TRAVIS_BRANCH: -8}"
fi
echo " short branch name : ${SHORT_TRAVIS_BRANCH}"
SHORT_TRAVIS_BRANCH=`echo ${SHORT_TRAVIS_BRANCH} | sed -r 's/-//g'`
SHORT_TRAVIS_BRANCH=`echo ${SHORT_TRAVIS_BRANCH} | sed -r 's/\.//g'`
SHORT_TRAVIS_BRANCH=`echo ${SHORT_TRAVIS_BRANCH} | sed -r 's/_//g'`
echo " short branch name : ${SHORT_TRAVIS_BRANCH}"
DEPLOYMENT_URL="${SHORT_REPO_NAME}-${SHORT_TRAVIS_BRANCH}.${SDK_ENVIRONMENT}.${BASE_DOMAIN}"
echo " deployment URL: https://${DEPLOYMENT_URL}"
cp docker-compose.yml.template docker-compose.yml
LW_REPO_NAME=`echo "${BUILD_REPO_NAME}" | tr '[:upper:]' '[:lower:]'`
LW_REPO_NAME=`echo ${LW_REPO_NAME} | sed -r 's/_//g'`
LW_REPO_NAME=`echo ${LW_REPO_NAME} | sed -r 's/-//g'`
LW_REPO_NAME=`echo ${LW_REPO_NAME} | sed -r 's/\.//g'`
LW_SHORT_TRAVIS_BRANCH=`echo "${SHORT_TRAVIS_BRANCH}" | tr '[:upper:]' '[:lower:]'`
generate_container_name ${LW_REPO_NAME} ${LW_SHORT_TRAVIS_BRANCH} ${DEPLOYMENT_SERVER} ${DEPLOYMENT_SERVER}
echo "service name : ${SERVICE_NAME} & container name : ${CONTAINER_NAME}"
sed -i "s/ImageName/${NEXUS_CR_TOOLS_URL}\/${IMAGE_NAME}/g" docker-compose.yml
sed -i "s/UrlName/${DEPLOYMENT_URL}/g" docker-compose.yml
sed -i "s/ServiceName/${SERVICE_NAME}/g" docker-compose.yml
sed -i "s/PortNum/${CONTAINER_SERVICE_PORTNO}/g" docker-compose.yml
sed -i "s/ContainerName/${CONTAINER_NAME}/g" docker-compose.yml
cat docker-compose.yml
LW_REPO_NAME_LENGTH=`echo ${LW_REPO_NAME} |awk '{print length}'`
SHORT_SERVICE_NAME="${SERVICE_NAME:0:$LW_REPO_NAME_LENGTH}"
echo "short service name: ${SHORT_SERVICE_NAME}"
function deploy_service(){
ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} "if [ -d /services/${SERVICE_NAME} ]; then rm -rf /services/${SERVICE_NAME}; fi && mkdir /services/${SERVICE_NAME}"
scp -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem docker-compose.yml ${SSH_USER}@${DEPLOYMENT_SERVER}:/services/${SERVICE_NAME}/docker-compose.yml
ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} " cd /services/${SERVICE_NAME}/ && sudo docker-compose pull"
ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} " cd /services/${SERVICE_NAME}/ && sudo docker-compose up -d"
sleep 10 && ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} " sudo docker ps -a | grep -i ${SERVICE_NAME}"
echo "Docker-compose is in : /services/${SERVICE_NAME} "
sleep 30
RET_CODE=`curl -k -s -o /tmp/notimportant.txt -w "%{http_code}" https://${DEPLOYMENT_URL}`
echo "validation code: $RET_CODE for https://${DEPLOYMENT_URL}"
if [ $RET_CODE -ne 200 ]; then
echo "Deployment validation failed!!! Please check pipeline logs."
exit -1
else
echo " Service available at URL: https://${DEPLOYMENT_URL}/demo/index.html"

fi
}
DEPLOYMENT_IS_RUNNING=`echo "${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_LOWER_INDEX}" | tr '[:upper:]' '[:lower:]'`
REDEPLOYMENT=`ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} " sudo docker ps -a | grep -i "${DEPLOYMENT_IS_RUNNING}" | wc -l" `
echo "${DEPLOYMENT_IS_RUNNING}"
echo "checking if this PRD exists & do redeploy: ${REDEPLOYMENT}"
if [ ${REDEPLOYMENT} -eq 1 ]; then
echo "Redeploying service: ${SERVICE_NAME} ..."
deploy_service
SHORT_TRAVIS_BRANCH="${TRAVIS_BRANCH:0:8}${TRAVIS_BRANCH: -8}"
fi
EXISTING_DEPLOYMENTS=`ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem ${SSH_USER}@${DEPLOYMENT_SERVER} " sudo docker ps | grep -i "${LW_REPO_NAME}-${AO_IDENTIFIER}" | wc -l" `
if [ ${EXISTING_DEPLOYMENTS} -gt ${MAX_DEPLOYMENTS_NR} ]; then
echo "Maximum deployments reached on ${SDK_ENVIRONMENT} environment for ${BUILD_REPO_NAME} ; existing deployments: ${EXISTING_DEPLOYMENTS} ; max depl: ${MAX_DEPLOYMENTS_NR} "
echo "Stopping container ${OLDEST_CONTAINER} ..."
RCMD='ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem '
RCMD="${RCMD} ${SSH_USER}@${DEPLOYMENT_SERVER} "
REM='" sudo docker stop '
RCMD="${RCMD} $REM ${OLDEST_CONTAINER}"'"'
echo $RCMD | bash
sleep 12
RCMD='ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem '
RCMD="${RCMD} ${SSH_USER}@${DEPLOYMENT_SERVER} "
REM='" sudo docker rm '
RCMD="${RCMD} $REM ${OLDEST_CONTAINER}"'"'
echo $RCMD | bash
else
echo "Deploying service ..."
deploy_service
LW_SHORT_TRAVIS_BRANCH="$(echo "${SHORT_TRAVIS_BRANCH}" | sed -e 's/-//g' -e 's/\.//g' -e 's/_//g' | tr '[:upper:]' '[:lower:]')"

# Get the maximum allowed deployment for given environment
function max_allowed_deployment(){
echo "getting max deployments for environment ${SDK_ENVIRONMENT}"
MAX_DEPLOYMENTS_NR=$(jq --arg sdkenvironment "${SDK_ENVIRONMENT}" '.[$sdkenvironment]' version.json | tr -d '"')
echo "Max allowed deployments: ${MAX_DEPLOYMENTS_NR}"
}
max_allowed_deployment

# Get the total numbers of deployed container for given environment
function existing_deployments(){
echo "Checking the existing number of running container(s)"
EXISTING_DEPLOYMENTS_NR=$(ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "sudo docker ps | grep -i ${LW_REPO_NAME}-${AO_IDENTIFIER}" | wc -l)
echo "Number of existing deployment: ${EXISTING_DEPLOYMENTS_NR}"
}
existing_deployments

# Get the old container name, no of deployments, and generate the new index and container name
function generate_container_name(){

DEPL=$(ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" sudo docker ps | grep -i "${LW_REPO_NAME}"-"${AO_IDENTIFIER}")
echo "Containers running for ${AO_IDENTIFIER}: ${DEPL}"
echo "${DEPL}" > file.txt

echo "Getting indexes of oldest and latest deployed containers for ${AO_IDENTIFIER}"
CT_LOWER_INDEX=$(awk -F'-' '{print $NF }' < file.txt | sort -nk1 | head -1)
CT_HIGHER_INDEX=$(awk -F'-' '{print $NF }' < file.txt | sort -nk1 | tail -1)
echo "Lowest index : ${CT_LOWER_INDEX} ; and Highest index : ${CT_HIGHER_INDEX}"

if [ -z "${DEPL}" ]; then
echo "First deployment. Setting the container name."
CT_INDEX=1
CONTAINER_NAME="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_INDEX}"
SERVICE_NAME="${LW_REPO_NAME}-${LW_SHORT_TRAVIS_BRANCH}"
else
echo "Multiple deployments detected. Setting the container name (old and new)"
CT_INDEX=${CT_HIGHER_INDEX} && CT_INDEX=$((CT_INDEX+1))
OLDEST_CONTAINER="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_LOWER_INDEX}"
CONTAINER_NAME="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_INDEX}"
SERVICE_NAME="${LW_REPO_NAME}-${LW_SHORT_TRAVIS_BRANCH}-${CT_INDEX}"
echo "New index: ${CT_INDEX}"
fi
}
generate_container_name

# Print useful details.
echo -e "\n"
echo "----------------------------------------------------------------------"
echo " Selected environment: ${SDK_ENVIRONMENT}. "
echo " Deployment server: ${DEPLOYMENT_SERVER}. "
echo " Max allowed deployments: ${MAX_DEPLOYMENTS_NR}. "
echo " Number of existing deployment: ${EXISTING_DEPLOYMENTS_NR} "
echo " Oldest container name: ${OLDEST_CONTAINER} "
echo " Container name for this deployment: ${CONTAINER_NAME} "
echo "----------------------------------------------------------------------"
echo -e "\n"

# Set the deployment URL
DEPLOYMENT_URL="${CONTAINER_NAME}.${SDK_ENVIRONMENT}.${BASE_DOMAIN}"

# Modify the compose file and run the docker-compose.
function deploy(){

# Copy the docker-compose template to docker-compose.yml
cp docker-compose.yml.template docker-compose.yml

# Replace the sample values
sed -i "s/ImageName/${NEXUS_CR_TOOLS_URL}\/${IMAGE_NAME}/g" docker-compose.yml
sed -i "s/UrlName/${DEPLOYMENT_URL}/g" docker-compose.yml
sed -i "s/ServiceName/${SERVICE_NAME}/g" docker-compose.yml
sed -i "s/PortNum/${CONTAINER_SERVICE_PORTNO}/g" docker-compose.yml
sed -i "s/ContainerName/${CONTAINER_NAME}/g" docker-compose.yml

echo -e "\n"
echo "Below is the content of docker-compose.yml"
echo "-------------------------------------------------"
cat docker-compose.yml
echo "-------------------------------------------------"
echo -e "\n"

# Remove the old docker-compose from deployment_server
ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "if [ -d /services/${SERVICE_NAME} ]; then rm -rf /services/${SERVICE_NAME}; fi && mkdir /services/${SERVICE_NAME}"

# Copy the latest docker-compose file to deployment_server
scp -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem docker-compose.yml "${SSH_USER}"@"${DEPLOYMENT_SERVER}":/services/"${SERVICE_NAME}"/docker-compose.yml

# Run docker-compose pull on deployment_server
ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "cd /services/${SERVICE_NAME}/ && sudo docker-compose pull"
sleep 10

# Run docker-compose up on deployment_server
ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "cd /services/${SERVICE_NAME}/ && sudo docker-compose up -d --force-recreate"
sleep 370

RET_CODE=$(curl -k -s -o /tmp/notimportant.txt -w "%{http_code}" https://"${DEPLOYMENT_URL}")
echo "validation code: $RET_CODE for https://${DEPLOYMENT_URL}"
if [ "${RET_CODE}" -ne 200 ]; then
echo "Deployment validation failed!!! Please check pipeline logs."
exit 1
else
echo -e "\n\tService available at URL: https://${DEPLOYMENT_URL}/demo/index.html"
fi
}

# If existing deployment less than max deployment then just deploy don't remove old container.
if [ "${EXISTING_DEPLOYMENTS_NR}" -lt "${MAX_DEPLOYMENTS_NR}" ]; then
deploy
fi


# If existing deployment equals max deployment then delete oldest container.
if [ "${EXISTING_DEPLOYMENTS_NR}" -ge "${MAX_DEPLOYMENTS_NR}" ]; then

echo "Maximum deployments reached on ${SDK_ENVIRONMENT} environment for ${BUILD_REPO_NAME}."
echo "Stopping container ${OLDEST_CONTAINER} ..."

if ! ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" sudo docker stop "${OLDEST_CONTAINER}"; then
echo "Failed to stop the ${OLDEST_CONTAINER} container"
fi
echo "Successfully stopped the ${OLDEST_CONTAINER} container."

if ! ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" sudo docker rm -f "${OLDEST_CONTAINER}"; then
echo "Failed to remove the ${OLDEST_CONTAINER} container"
fi
echo "Successfully removed the ${OLDEST_CONTAINER} container."

echo "Deploying the service: ${SERVICE_NAME}"
deploy && sleep 30
echo "Deployment completed."
fi
4 changes: 2 additions & 2 deletions version.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"comment": " Please update this file for any new branch with core libary name - package name and version uploaded to nexus",
"name": "froala-editor-QA241122",
"version": "4.0.16",
"name": "froala-editor-RC-v4.0.18",
"version": "4.0.17",
"comment-2": "Please specify the maximum allowed deployments per environment",
"dev": "1",
"qa": "2",
Expand Down

0 comments on commit 3d497de

Please sign in to comment.