musings between the lines

there's more to life than code

project bootstrapping

| Comments

The worst part about getting started with a project is that, well, you need to actually get started. And that’s always easier said than done. You have your ideas in your head about what it should be, and you have some inkling of the tools you want and where to start, but sometimes that initial setup and the technology choices that you need to make upfront are just a tad overwhelming, even with all of today’s tools and bootstrap kits.

So I thought I’d write myself a bootstrap outline that I can use to just get going. The neat thing about this is since it uses Yeoman, I can just run it as I want with whatever options and technology depth I want and Yeoman can take care of the nitty gritty. I just need to outline all the steps I need to take to get to the point where I can invoke Yeoman and then take care of the cleanup and preparation post Yeoman install.

Using something like Yeoman also means that I can try things out incrementally. The first step might be to configure a project that allows me to just have a html workspace to frame out the site. Once that’s done, I can then restart the project with a more interactive setup, perhaps like a single page application with AngularJS. After that, when I’m ready, I can hook in a backend and have a full fledged site.

This isn’t going to be a project starter pack like you might see on GitHub since I really wont know what I want to do yet. All I want is something basic to sandbox in and just get experimenting so that perhaps I can grow it into something more significant, dump it and restart with some different tools, or just use it for what it is and play around. Plus, making myself write out and execute each of the steps will help me improve my grasp on the various tools used in the process.

These are the step I have in place as needed for my projects on Ubuntu. It’s still a work in progress so as I figure out new and better ways to do things, I’ll keep it updated. Your steps may differ slightly depending on setup and platform, but hopefully not too much.

So here we go.

Prepare

Create and enter your project directory:

mkdir <project_name>
cd <project_name>

Check your versions:

yo --version && bower --version && grunt --version
1.1.2
1.3.5
grunt-cli v0.1.13
grunt v0.4.5

If you don’t see the last line listed, install grunt locally to the project

npm install grunt

If that throws some errors, you may have to make sure you actually own your .npm and tmp directories:

sudo chown -R `whoami`.`whoami` ~/.npm
sudo chown -R `whoami`.`whoami` ~/tmp

Rerun the version check and you should see all 4 items listed.

Scaffold

Now go ahead and run yeoman:

yo

Pick whatever options you want. For my first run at a project, I usually just go with the AngularJS settings and its defaults. The one thing I’ve noticed with Yeoman and a globally installed npm is that it will fail during the actual module fetch process of the installation. I’m guessing this is because npm was installed as root and the module directories are not user accessible. Yeoman also doesn’t really like running as root, and I don’t like giving user level write permissions to system directories, so I just let it fail. It doesn’t seem to be a problem as the proper configuration files are written out.

So once Yeoman fails, I do a manual npm installation of the modules:

sudo npm update -g

The -g in this case just tells the system to install them globally so that every project has access to it. If it makes you feel uncomfortable to work in a directory that Yeoman failed to automatically complete (I tend to be like that), you can now nuke your project directory, and redo the above steps since now npm already has the required modules, Yeoman should be happy dandy and work properly to the end.

Oh, if you run Chromium, you will also want to make sure that the CHROME_BIN env variable is pointing to the chromium binary so that “things will just work (tm)”.

echo 'export CHROME_BIN="/usr/bin/chromium-browser"' >> ~/.bashrc && source ~/.bashrc

At this point, you probably want to also setup git. Just add what’s there and commit it.

git init                        # setups the project to use git
git add .                       # add initial content
git status                      # check status
git commit -m "Initial commit"  # first commit
git status                      # should be all clear

Customize

Bower is a great little tool for downloading whatever web module you’ll need for your project. Use it to get the modules you want to play with from the repository.

Here’s a list of commands for Bower that I use most often:

bower list                          # shows what bower has installed
bower cache clean                   # cleans bowers cache for a fresh full download
bower update                        # update your modules based on bower.json settings
bower install <module>        # installs module
bower install <module> --save # installs and adds to bower.json
bower uninstall <module>      # uninstalls module
bower info <module>           # shows information
bower prune                         # clean up unused stuff

Here are some of the Bower modules I use for my projects, which at the moment is Angular heavy. Of course, necessity of these will be up to you:

bower install angular-ui-router --save                  # replacement for the standard angular router
bower install angulartics --save                        # analytics plugin for angular
bower install chieffancypants/angular-hotkeys --save    # hotkey for keyboard browsing and auto help screen
bower install angular-bootstrap --save                  # angularized bootstrap elements
bower install angular-touch --save                      # for touch based devices

The --save just makes sure that the installation information is saved to bower.json. If you’re still experimenting and deciding if you really want to keep the module, just leave off the --save. It makes it easier to just delete it once you decide against keeping it. When ready, you can just rerun the install with the --save flag.

The nice thing about Yeoman is that it now installs the grunt-wiredep module for you. Which means that in order to install the scripts and css of the modules downloaded by bower, all you need to do is to run grunt and during that process, grunt will autoinsert the right scripts and css into the right places in your html files. Very convenient.

NPM similarly offer some useful modules. These are the ones I load up:

npm install grunt-build-control --save  # For build and deployment
npm install grunt-targethtml --save     # For dynamic construction of HTML based on parameters

Configure

.gitignore: There are some changes you need to manually make to some of the configuration files to get the most out of them. One is to make sure you have a decent .gitignore file so that you’re not checking in things you don’t need to. The defaults should suffice, but who knows what files get stored in your project, so be sure to keep it up to date.

bower.json: You’ll also want to modify the bower.json file. I usually remove the need for strict versions and instead allow the system to update to at least the major and minor versions, letting the patch version get updated freely. There’s also a version number for your app at the top which you’ll want to keep up to date. Update your modules by running bower update.

...
    "angular": "^1.3",
...

package.json: There’s also another place for version numbers in package.json for the server side npm modules. You can also do the same with version numbers here as with bower if you’re confident enough. There’s also a space for your app’s version number here too. Not quite sure why we need it in both bower.json and package.json.

Gruntfile.js: This is probably the place you’ll end up mucking around the most. You’re also going to have to manually add in the new npm modules that were installed into the package.json into the Gruntfile.js file. Also be aware that with something like targethtml, which is used to render pages differently for development and production, the order where it gets executed does matter, so just be aware of that.

...
// For filerev, I've had to comment out the fonts revisioning since it didnt work well with my font loading done in the css file.
    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
          // '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },
...
// This is to make the changes to the index.html file in place once it's reached the dist directory.
    targethtml: {
      dist: {
        files: {
          '<%= yeoman.dist %>/index.html': '<%= yeoman.dist %>/index.html'
        }
      }
    },
...
// I added the last 3 items to the copy routine since it was missing.  I sometimes use a data dir for static data content.
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            ...
            'sitemap.xml',
            'styles/fonts/*',
            'data/*'
          ]
...
// This is for deployment via a remote git repository.  You'll have to fill in your own repository and credential information.
    // For deployment: http://curtisblackwell.com/blog/my-deploy-method-brings-most-of-the-boys-to-the-yard
    buildcontrol: {
      options: {
        dir: '<%= yeoman.dist %>',
        commit: true,
        push: true,
        message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
      },
      production: {
        options: {
          remote: '<user>@<domain>.com:/path/to/repository/production.git',
          branch: 'master',
          tag: appConfig.app.version
        }
      }
    }
...
// htmlmin does not play nice with angular, so it gets commented out.
// Also note where 'targethtml' is placed, right after 'copy:dist'
  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'targethtml',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin'
//    'htmlmin'
  ]);
...
// Registering the deploy command
  grunt.registerTask('deploy', [
    'buildcontrol:production'
  ]);

Deployment

Ah, this is still causing me some trouble, but I can get it to work well enough, so may as well.

If you already know where you will be deploying to, you can setup your production/staging git repository and link it to your development environment. Check out Curtis Blackwell’s setup instructions to get the nitty gritty details on how to do this. It’s almost complete, but with one change. Do not link your local git repository to the remote one. The grunt-build-control plugin will contain all the information needed about the remote repository so you don’t have to explicitly link it. In fact, the way it works (I think) is that the build process uses the git repository information in the Gruntfile.js and pulls from it to your local dist/ dir. Then it does the local build to update it with the newest content, commits, and pushes back to the upstream repository.

The process is atomic so you don’t have to worry about git state. You can even empty the dist/ directory (and remove the .git directory in there) and it will simply get rebuilt and reprocessed during the next build.

But read Curtis’ instructions anyway for the setup needed server side: My Deploy Method Brings Most of the Boys to the Yard

If you do link your local git repo to the remote deployment repository, you’re linking your uncompiled project repository to a compiled distribution repository which will not be compatible with each other. Lots of fun errors will eventually ensue especially if you accidentally push or pull. Not that I would do such a mistake…

Caveat: For some reason, when deploying, the system will create a remote repository branch and add that as a remote to my main git development repository. The result will be that git will start to complain that the commits are not in sync and you need to pull the content from the remote to stay in sync. Don’t do it. Instead, you can just remove the remote repository git remote remove remote-fc9047. Get that repo name by doing a git remote and finding the remote-xx0000 named remote branch. Then be sure to delete the .git directory from the dist/ dir.

I have no idea why this happens, and how to prevent it, but at least the fix is reasonableish.

Now would also be a good time to link you repo to your actual source code repository. Something like:

git remote add origin @:/path/to/repository.git   # Link your repositories
git push --set-upstream origin master                               # So that you can just "git push"

Now you’re set to push your source code to one repository, and your compiled deployment files will automatically get pushed to your production repository.

Run

Pretty simple to run your project. Everything is controlled by Grunt:

grunt               # runs through all the tests and checks everything
grunt clean         # cleans and reset things
grunt build         # does all the minification and optimizations in preparation for a distributable
grunt deploy        # deploys your project to your remote location (needs the grunt-build-control module and setup in Gruntfile.js)
grunt serve         # runs the project
grunt serve:dist    # runs the project with production code, a must to do some last minute checks to make sure Grunt compiled everything right

Since Yeoman preconfigures live reload support, I usually have a dedicated terminal open that’s running grunt serve.

Fixes

OptiPNG

I’ve run into an issue with opipng during imagemin’s Grunt task:

Warning: Running "imagemin:dist" (imagemin) task
Warning: Command failed: ** Error: Lossy operations are not currently supported
Use --force to continue.

I have no in depth clue as to what is really the issue, only that this seems to happen with OptiPNG 0.6.4, which for some reason is the current version on my Ubuntu (13.04). No idea why it isn’t automatically updating to the 0.7 track, but you can remove it to get around the block (or update it I guess):

sudo apt-get remove optipng

Empty vendor.js

As of this writing, there’s a severe bug in how Yeoman generates the Gruntfile.js and the index.html. Basically the left hand doesn’t know what the right hand is doing and you end up with “angular not found” type errors when doing a “grunt serve:dist”. Basically, the generation script is fubar. What happens is that the bower_components directory is now placed in the project root instead of the app/ dir and while that works fine for local testing, when Grunt builds the system, it scans the html and sees that the html is referencing app/bower_components and appends it as the path to the bower files. This causes the usemin script in Grunt to try to look for the files in the wrong place. The end result is empty vendor.js and vendor.css files in the dist/ dir.

Anyway, more about it here and the fix is here. Basically you need to adjust the build statements in your index.html:

    <!-- build:css ... -->
becomes
    <!-- build:css(.) ... -->

I’m assuming an egregious mistake like this will be fixed soon so all this may be a moot point sooner or later.

Workflow

So once everything is setup and working, my workflow usually consists of this:

* Make code changes
* git add .
* git commit -m ""
* git tag vx.y.z                # If the code requires a version bump, tag it and also change bower.json and package.json
* git status                    # Just to make sure we're clean and ready
* grunt build                   # Builds and tests everything
* grunt serve:dist              # Runs the production code.  A good last minute check to make sure Grunt generated everything correctly.
* grunt deploy                  # If the code is production ready, deploy it.

Well, and the occasional:

* rm -rf dist/.git
* git remove remote remote-xx0000

Which I can live with till I figure out what’s going on.

Feedback

So that’s about it for now. It’s a touch complicated to read over, but hopefully it can help you out in getting started with using Yeoman to bootstrap your project to your liking. Let me know of any feedback, questions, suggestions and so on.

Thanks!

Comments