Project Bootstrapping

April 07, 2019


So you are starting off with your first project. You are planning on sharing your amazing work with others or you are starting something for your current job. Most, of not all projects require some level of bootstrapping. This includes that are language/platform agnostic and those that are specific and opinionated. Often the bootstrapping aligns with company guidelines. In any case, doing this requires some work that is repetitive. And as anyone who writes software knows, you automate that shit after the 3rd time. This article will walk through the thought process of splitting out the platform agnostic elements from the opinionated ones. I will step through a shell script I created as part of the process.

Name The Things

There are some initial pieces of data for the project that will make more sense as we step through

  1. Project Name
  2. Author
  3. Author Handle (gitlab/github/twitter)
  4. Author Website
  5. git ignore expectations
  6. Code Init string

The first four are for documentation purposes. You are allowed spaces in your project name, but convention is all lower case or camel, or hyphenated.

./bootstrap.sh ProjectKit "ANewProject" "Cavelle Benjamin" thecb4 https://thecb4.io swift "touch hello-world.swift"

The last two items are where the opinions start to form.

I use git for source control management. Git is extremely powerful toolset for managing code source. As part of your project development you should choose some form of source control. Part of source control is determining what will not be controlled. A .gitignore file is a simple text file that designates files and folders that should not be included when the project is put under control. This includes build directories and secrets for example. Since what you want to ignore varies based on what you are developing, this is an input. The website gitignore is a wonderful site that produces ignore files based on an operating system, a language, or even an IDE. I use a Mac for development so my macOS as a default value that I pass in addition to what is passed at the command line.

curl -o .gitignore "https://www.gitignore.io/api/macOS,$GIT_IGNORE"

This pulls from the website directly into a git ignore file that will be used once I initialize my repository.

The next big step in the process is initializing the repository. Simply running git init is an option, but it doesn’t provide you with any form of workflow for your source. Enter in git-flow Git-Flow is an opinionated way of using git to manage how you manage your source with git. The opinion here is that as you work your project, you do so on feature branches that merge back into a development branch. As you develop releases, this is done from the development branch and then merged into master branch. Master ever only contains releases. Releases are tagged with a version. I use Semantic Versioning for my versioning. I also have a set of hooks that provide even more opinion for git flow.

This includes:

  1. Cannot commit directly to the development or master branch, must be a merge from a feature or release branch respectively
  2. Force semantic versioning and allow for “major”, “minor”, “patch” nouns to be used when starting a release and the version auto increments.
git flow init -d && \
git config gitflow.prefix.feature "feature/" && \
git config gitflow.prefix.release "release/" && \
git config gitflow.prefix.hotfix "hotfix/"   && \
git config gitflow.prefix.support "support/" && \
cp -R $GIT_FLOW_HOOKS .git/hooks && \
git flow feature start bootstrap && \

Once the code is under version control, I start a new feature branch called “bootstrap” where I add all the necessary initial files.

  1. LICENSE
  2. README
  3. CHANGELOG
  4. CONTRIBUTING

Defining the license for your code is critical when using open source. The web has made that process significantly easier. Apache, MIT, and BSD are just a few examples. Picking one defines how people may interact with your code and how people can expect to interact with you if something goes wrong with the code. This step includes the author and copright year.

curl -o LICENSE.md "$LICENSE_SOURCE" && \
sed -i "" 's|\$(YEAR)|'$YEAR'|g' LICENSE.md && \
sed -i "" "s|\$(AUTHOR)|'$AUTHOR'|g" LICENSE.md && \

The next file is the README file. There are plenty of blog posts about why you should write one and what some good best practices are. The short of it is, the README file operates as the summaries documentation for the code you are writing. It’s the “Quick Start” guide for someone to pick up your code and use/integrate. The next step in the bootstrap process copies my version of a README structure and replaces key info that is passed in at the command line.

# README
curl -o README.md "$README_SOURCE" && \
sed -i "" 's|\$(PROJECT)|'$PROJECT'|g' README.md && \
sed -i "" "s|\$(AUTHOR)|'$AUTHOR'|g" README.md && \
sed -i "" 's|\$(LICENSE)|'$LICENSE'|g' README.md && \
sed -i "" 's|\$(AUTHOR_HANDLE)|'$AUTHOR_HANDLE'|g' README.md && \
sed -i "" "s|\$(AUTHOR_WEBSITE)|$AUTHOR_WEBSITE|g" README.md && \

The next item goes hand in hand with the versioning and git flow. A changelog forces discuss the work you’ve done in a structured manner. I like it because it helps me think through the feature branches I am working and helps me remember the things I’ve done, when my commit messages aren’t that clear. This is another form of documentation for those using your code but also for yourself. I treat it as a summary of all the work done on feature branches.

# CHANGELOG
curl -o CHANGELOG.md "$CHANGELOG_SOURCE" && \
sed -i "" 's|\$(PROJECT)|'$PROJECT'|g' CHANGELOG.md && \
sed -i "" 's|X.Y.Z|0.0.1|g' CHANGELOG.md && \

Next is telling everyone how they can contribute to your code. This provides guidelines that enforce some opinions you’ve made about your code onto those who would want to assist directly with your project. This may include code format, tests that must be run, how code changes are submitted, and how people may comment on the code.

# CONTRIBUTING
curl -o CONTRIBUTING.md "$CONTRIBUTING_SOURCE" && \

The last component of the script is to run whatever initialization you want. This allows you to init any project type with a consistent set of initial documents. This could be npm, swift package, or even another shell script.

# init code
eval "$CODE_INIT" && \

One of the tenants of writing good code is that you don’t repeat yourself (DRY). This bootstrap script has allowed me to consistently create project structures so that I can focus on writing features. Hopefully you will find this thought process and the script itself useful. If you see anything that should be changed, please feel free to contact me on twitter at @_thecb4