Structure is a template for building and deploying node.js projects. It includes:
- Gulp based development already setup to watch everything (static files, media, js, less, server files) and rebuild upon changes.
- Each component is watched separately so only what you change gets rebuilt. It's super fast.
- LiveReload so you don't have to refresh your website to see changes.
- Docker. Only your prod dependencies get installed. Gulp also watches your server files and reboots the docker container after changes, in effect restarting your server. It's also super quick because there's no rebuilding.
- Beautiful, simple project structure that is easy to understand and already setup to work with Semaphore, AWS Elastic Container Registry and Elastic Beanstalk for a seamless deployment pipeline. See instructions further below for setting up Semaphore and AWS.
-
Clone the repo, cd into the base directory, run
npm install --only=dev
-
Create a
config.json
file that looks like this in the root directory:
{
"app": {
"environment": "dev"
}
}
You may change the location of the "environment" variable, but remember to update gulpfile.js
as well. See more about config.json
under Documentation > Files
-
If you wish, you can install the LiveReload plugin to have your browser automatically update after file changes during development.
-
You should now be able to run a development setup as described next. You may need to run
docker-compose up --build
once beforegulp
works.
To start development run gulp
. This will perform all of the following build steps once and then watch their respective source directories and rebuild upon detecting changes.
- ROOT FILES: Copy the
source/root
folder which is where you put any static frontend files (not media or fonts) likeindex.html
,robots.txt
,sitemap.xml
, etc. - CSS: Build Less files to CSS from the
source/less
directory. - JAVASCRIPT: Compile javascript files from the
source/js
directory. - FONTS & IMAGES: Copy fonts from the
source/fonts
directory and images from thesource/images
directory. - BACKEND: Copy files from the
source/server
directory and rundocker-compose up
which will launch a dev server. - BUILD FILES: Copy app files ('config.json', 'package.json', 'Dockerfile').
And now you're ready to develop.
See Documentation for more information on how the above are all compiled and how to add new JS files to the build process
- Make a Semaphore project. Use their docker platform.
- Add your AWS account ID as environment variable
AWS_ACCOUNT_ID
. You probably want to encrypt this. - Add your
config.json
file under "Configuration Files" but make any relevant changes like "environment" which should be set to "prod". - Connect AWS ECR as your Docker Registry. Semaphore has good documentation on how to do this.
- Under "Build Settings" use these five lines for the deployment script:
npm install --dev
PATH=$(npm bin):$PATH
gulp build
docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$SEMAPHORE_PROJECT_NAME:$REVISION .
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$SEMAPHORE_PROJECT_NAME:$REVISION
We are installing dev dependancies. Then adding local node_modules to the PATH so we can call gulp. Then we tell gulp to build the _package
directory. Then we build the docker image which includes the _package
directory. Then we push that to AWS.
- For the Server setup you'll need to edit the deploy script to this:
sed -i -e 's/_version_/'$REVISION'/g' Dockerrun.aws.json
sed -i -e 's/_account-id_/'$AWS_ACCOUNT_ID'/g' Dockerrun.aws.json
echo "Zipping your Dockerrun file"; zip "$EB_APP_NAME".zip Dockerrun.aws.json
export EB_VERSION=`git rev-parse --short HEAD`-`date +%s`
aws s3 cp "$EB_APP_NAME".zip s3://$S3_BUCKET_NAME/"$EB_APP_NAME"/"$EB_VERSION".zip
aws elasticbeanstalk create-application-version --application-name "$EB_APP_NAME" --version-label "$EB_VERSION" --source-bundle S3Bucket=$S3_BUCKET_NAME,S3Key="$EB_APP_NAME/$EB_VERSION.zip" --description "`git show -s --format=%s HEAD | cut -c -200`"
aws elasticbeanstalk update-environment --environment-name "$EB_ENV_NAME" --version-label "$EB_VERSION"
echo "Environment status: `aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME" | grep '"Status"' | cut -d: -f2 | sed -e 's/^[^"]*"//' -e 's/".*$//'`"
echo "Your environment is currently updating"; while [[ `aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME" | grep '"Status"' | cut -d: -f2 | sed -e 's/^[^"]*"//' -e 's/".*$//'` = "Updating" ]]; do sleep 2; printf "."; done
if [[ `aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME" | grep VersionLabel | cut -d: -f2 | sed -e 's/^[^"]*"//' -e 's/".*$//'` = "$EB_VERSION" ]]; then echo "The version of application code on Elastic Beanstalk matches the version that Semaphore sent in this deployment."; echo "Your environment info:"; aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME"; else echo "The version of application code on Elastic Beanstalk does not match the version that Semaphore sent in this deployment. Please check your AWS Elastic Beanstalk Console for more information."; echo "Your environment info:"; aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME"; false; fi
sleep 5; a="0"; echo "Waiting for environment health to turn Green"; while [[ `aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME" | grep '"Health":' | cut -d: -f2 | sed -e 's/^[^"]*"//' -e 's/".*$//'` != "Green" && $a -le 30 ]]; do sleep 2; a=$[$a+1]; printf "."; done; if [[ `aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME" | grep '"Health":' | cut -d: -f2 | sed -e 's/^[^"]*"//' -e 's/".*$//'` = "Green" ]]; then echo "Your environment status is Green, congrats!"; else echo "Your environment status is not Green, sorry."; false; fi;
echo "Your environment info:"; aws elasticbeanstalk describe-environments --environment-names "$EB_ENV_NAME"
It's very easy:
- source - All your source files go here
fonts
images
js - frontend js
less - less, or turn this into sass if you prefer
root - for static files like index.html, robots.txt
server - for the express backend server
- _package - Where everything gets built to. Ignored by git.
- node_modules - Where your development node modules live. Ignored by git.
- Other build related files
We ignore the node_modules
and _package
directories as they are build time artifacts.
config.json
is ignored because it can contain any sensitive information your backend needs to connect to databases, apis, etc. Semaphore allows you to upload a config file for each environment, perfectly replicating this local development setup.
This is where you can store any environment specific information for your backend. Gulp also uses it to get the environment for building the project.
One Dockerfile for every environment. Its simple.
Used only for local development so we can do any port mapping we want, easily attach our _package
directory with our build result as a volume to the docker machine and not pollute the Dockerfile
with environment specific setup.
The simplest possible file to get AWS Elastic Beanstalk to put up a new Docker image.
_account-id_
and _version_
are both replaced at build time on Semaphore.
Used to build source
to _package
and for local development to watch files.
Usual node app package file.
This is used by the gulpfile to get a list of frontend JS files to compile for builds. It is also watched by gulp, so when you create a new JS file for development as soon as you add it to sources.json, your JS will be rebuilt. You can also tell it to compile everything in a directory in which case you don't even have to add individual js files to this sources file.