= Happy Deployment
😃 🚀
						Russell Heimlich
(like the maneuver)
					
Lead Developer at Spirited Media
We need to get that software to other places
But we don't want to break things
				Continuous Integration / Continuous Deployment
Deployment Should Be Boring
				Add a .circleci/config.yml file to the root of your project
				
					
				Steps in the build process are called jobs
One or more jobs makeup a workflow
Workflows can run sequentially or in parallel
(fan in/out)
					
				Our build process is straightforward
				config.ymlversion: 2
				references:
  # Default container configuration
  docker_config: &docker_config
    docker:
      - image: circleci/php:7.0-node-browsers
					Pre-built CircleCI Docker Images
				jobs:
  build:
    <<: *docker_config
    steps:
      - checkout
				- restore_cache:
    keys:
      - v1-dependency-cache-{{ checksum "yarn.lock" }}
      - v1-dependency-cache
- restore_cache:
    keys:
      - v1-composer-cache-{{ checksum "composer.lock" }}
      - v1-composer-cache
					A lock file is a snapshot of the current dependency tree and allows for reproducible builds between machines
				- run:
  name: Remove previous Yarn
  command: sudo rm /usr/local/bin/yarn*
- run:
  name: Install Yarn
  command: sudo npm install -g yarn
- run:
  name: Install Grunt CLI
  command: sudo npm install -g grunt-cli
				- run:
  name: Install NPM Dependencies
  command: yarn install
- run:
  name: Install Composer Dependencies
  command: composer install -o --no-dev
				- save_cache:
  key: v1-dependency-cache-{{ checksum "yarn.lock" }}
  paths: node_modules
- save_cache:
  key: v1-composer-cache-{{ checksum "composer.lock" }}
  paths: vendor
				- run:
    name: Building
    command: |
      if [ "${CIRCLE_BRANCH}" == "staging" ]; then
        grunt
      else
        grunt build
      fi
				- run:
    name: Maybe Deploying?
    command: |
      if [ "${CIRCLE_BRANCH}" == "staging" ]; then
        .circleci/deploy.sh
      fi
      if [ "${CIRCLE_TAG}" ]; then
        .circleci/deploy.sh
      fi
					CircleCI Enviornment Variables
				workflows:
    version: 2
    build-and-deploy:
      jobs:
        - build:
            filters:
              branches:
                ignore: master
              tags:
                only: /^v[0-9]+(\.[0-9]+)*/
				deploy.sh#!/bin/bash
echo "Starting Deploy..."
git config --global user.name "CircleCI"
git config --global user.email "[email protected]"
				# Make a new README.md with build status details
rm README.md
cat > README.md EOF
Build [#$CIRCLE_BUILD_NUM]($CIRCLE_BUILD_URL) by $CIRCLE_USERNAME at $TIMESTAMP
[$CIRCLE_COMPARE_URL]($CIRCLE_COMPARE_URL)
EOF
				Build #6608 by kingkool68 at 2019-02-07 04:00 AM UTC
spiritedmedia/spiritedmedia/compare/fc0e1c7f394a...52af362adfde
				
					
						See this gist for details about parsing a Git log
					
				# This should be ignored in .gitignore-build
# but let's try and remove it just to be safe
rm -rf node_modules/
# Find all .git/ directories and remove them.
# If we commit directories with .git in them then they are
# treated like sub-modules and screw that.
find . | grep -w ".git" | xargs rm -rf
# Remove all .gitignore files in
# the wp-content/ and vendor/ directories
find wp-content/ vendor/ -name ".gitignore" | xargs rm
rm .gitignore
mv .gitignore-build .gitignore
				git clone [email protected]:spiritedmedia/spiritedmedia-build.git tmp/
mv tmp/.git .
rm -rf tmp/
				# If no branch is set then assume master
if [ ! $CIRCLE_BRANCH  ]; then
  CIRCLE_BRANCH="master"
fi
# Switch branches... maybe?
if [ ! `git branch --list $CIRCLE_BRANCH` ]; then
  # Branch doesn't exist. Create it and check it out.
  # See http://stackoverflow.com/a/21151276
  git checkout -b $CIRCLE_BRANCH
else
  git checkout $CIRCLE_BRANCH
fi
				# Add everything and commit
git add -A
git commit -m "Build #$CIRCLE_BUILD_NUM by $CIRCLE_USERNAME on $TIMESTAMP"
git push origin $CIRCLE_BRANCH --force
echo "Code changes pushed!"
				🎉 Hooray!
				🤔 Now how do we get this code to servers?
AWS CodePipeline is the glue between GitHub and AWS CodeDeploy
				AWS CodeDeploy talks to our servers and tells them to update
(Requires the AWS CodeDeploy Agent to be installed)
				
				
				
				CodeDeploy is also configured by a YAML file, appspec.yml.
					
				version: 0.0
os: linux
files:
hooks:
  BeforeInstall:
    - location: bin/codedeploy.sh
      timeout: 600
      runas: root
				codedeploy.sh#!/bin/bash
# The appsec.yml file prohibits calling arbitrary scripts.
# This stub shell script downloads and executes
# the shell script specified in the
# EC2 instance User Data field.
# The user data shell script calls the deploy script
# baked in to the AMI running on the server.
# Fetch the user data script associated with this
# type of instance and save it into a temporary shell script
sudo curl http://169.254.169.254/latest/user-data > temp.sh
# Execute the shell script to run the update
sudo bash temp.sh
# Clean up
sudo rm temp.sh
				
				“You can specify user data to configure an instance or run a configuration script during launch.”
🆒
deploy-production.sh#!/bin/bash
# Shell script to update the app with
# the latest changes from GitHub (ex. when called form AWS CodeDeploy)
# Should be placed in /var/www/spiritedmedia.com/scripts/
# and run as root
cd /var/www/spiritedmedia.com/htdocs/
# Force git pull
git fetch --all
git reset --hard origin/master
# Reset file ownership
chown -R www-data:www-data /var/www/spiritedmedia.com/htdocs/
				
				Make a separate GitHub user and use that for managing credentials to different servers.
(GitHub calls these "Machine Users")
				
					
				
				Pros of CircleCI
Cons of CircleCI
Pros of AWS CodeBuild
Cons of AWS CodeBuild
Continuous Integration / Continuous Deployment is like playing dominos with YAML files and shell scripts.
But it's so awesome when it all works! 🚀