Jason Sultana

Follow this space for writings (and ramblings) about interesting things related to software development.

Simple scripted deployments

12 Sep 2020 » bash, blogging, deployments

G’day guys!

Developers love to automate. It’s a fact, like Koalas love eucalyptus, or Australians love beer. Some things though get much more attention than others when it comes to automation. Testing, for example, has become so mainstream that it would be hard to be a developer in 2020 and not be familiar with automated tests and testing frameworks. Automated deployments, on the other hand? Not so much. Sure, enterprise systems might be wired up to use robust tools like Octopus Deploy or AWS CodeDeploy. But what about an indie developer deploying something small, like this blog for example? The aforementioned tools would definitely be overkill. But does that mean that we yield to the monotinous and error-prone monster that is the act of manual deployment?

Hold up, what’s wrong with doing deployments by hand anyway?

If you’re part of the minority of developers nowadays that aren’t convinced that automated deployments are good, here are just a few reasons why you might want to change your mind:

  1. They’re slow. At one of my previous workplaces, we did deployments by hand. Our applications typically consisted of an MVC web application, backing windows service and WCF service. Deploying an entire application, including a database migration, would typically take about 30 minutes. To me, that’s an unacceptable amount of time.

  2. They’re error prone. Just imagine the possibility of accidentally copying a folder to prod instead of test. Or starting/stopping a service for the wrong application. Or (shudders) accidentally shutting down a remote server instead of logging out. Probably the most convincing case study of the dangers of manual deployments is the Tragedy of Knight Capital Group Holdings. Long story short, a new version of an application was manually deployed to 7 servers instead of 8, meaning that one of the old servers was still using an old version of the software. This human error caused a loss of around $7 billion dollars. The firm was eventually acquired by a competitor, presumably for peanuts.

  3. They’re difficult. If you are still deploying applications manually, you likely have a printed, laminated and framed procedure that is worshipped by the developers, to which a human sacrifice is made before each deployment. Only the most trusted engineers are permitted to perform deployments, and usually never alone. All it takes is for a newcommer to mess up one step, and your organisation may become another Case Study like Knight Capital. It may not even be a newcommer; even seasoned engineers that have done the deployment a thousand times before may still make a mistake.

Okay, so what do you suggest?

Of course, if you’re working for a large firm, you probably already have your software in the cloud, so you should probably be using something robust like Octopus Deploy. But if you’re an indie developer looking to automate deployments of your own small applications (like myself, with this blog) then read on.

Bash, to the rescue

Alrighty, let’s create a little bash script that we can use to build our app and deploy it somewhere. First, open a new terminal. If you’re using VS Code to build your app, an integrated terminal will work fine.

  1. Create a new ssh user and give them read/write access to the app directory on the remote server. I recommend creating a new user for this, instead of running the following commands as the root user. This way, if you get any of the paths wrong, you don’t risk accidentally overwriting any existing files. I think a good practice is to create test and prod users for the same application for this reason. You’ll also want to set up ssh public key authentication, so that you don’t have to enter your password each time (no!) or include your password in the deploy script (hell no!)
  2. Create a new shell file using touch deploy.sh.
  3. Make it executable using chmod +x ./deploy.sh.
  4. Set up some variables for easy modification. We’ll be transferring the app as a zip, which should be much quicker than transferring files one by one.
    #!/bin/bash
    local_zip=~/Desktop/site.zip
    remote_zip=/home/remoteuser/app/deployment.zip
    remote_dir=/home/remoteuser/app
    echo "Deploying to $remote_dir"
    
  5. Perform the build. This can be via npm, like npm run build-my-app, or anything else, so long as it’s synchronous.
  6. Zip up the contents of the build. Replace dist with the name of your build folder. This also assumes that the build folder is a subfolder of your original working directory.
    cd dist
    zip -r -q "$local_zip" ./
    echo "Zip completed."
    
  7. Deploy the zip to the remote server
    scp "$local_zip" "user@server.com:$remote_zip"
    echo "Zip copied to server."
    
  8. Unzip the app on the remote server, and remove the zip. Notice that you can execute multiple bash commands with a single ssh statement by delimiting them with a semicolon. This could come in handy if you’re deploying anything beyond a static app, like an SPA. Also note that we should explicitly pass in the target directory via the -d option, otherwise the contents will be unzipped in the current users home directory.
    ssh user@server.com "unzip -o -q $remote_zip -d $remote_dir; rm $remote_zip"
    echo "Done"
    
  9. Run the deployment with ./deploy.sh

For your convenience, here’s the full contents of the deployment script.

#!/bin/bash

local_zip=~/Desktop/site.zip
remote_zip=/home/remoteuser/app/deployment.zip
remote_dir=/home/remoteuser/app
echo "Deploying to $remote_dir"

npm run build-my-app
cd dist
zip -r -q "$local_zip" ./
echo "Zip completed."

scp "$local_zip" "user@server.com:$remote_zip"
echo "Zip copied to server."

ssh user@server.com "unzip -o -q $remote_zip -d $remote_dir; rm $remote_zip"
echo "Done"

Anyway, I hope you found this little script useful. That’s all from me! Catch ya!