During the previous adventures, the project Ionic Boilerplate has been re-organized for a better development engagement, such as:

  • The Modularize AngularJS code structure enables you to freely define your app functionality and add modules if needed.
  • Based on that, the adding Browerify gulp plugin helps bundle the code into one single JS package so that you do not need to include every JS files into index.html file.
  • A folder-based Gulp tasks management system facilitates the Gulp task building and management.
  • The adding testing framework supports both Unit Test and E2E Test out-of-box.

However, if looking into the whole application development process, there are still a lot of aspects that we have not covered yet. For example:

  • Simplify Terminal interface (CLI) to avoid the use of bothgulp and ionic.
  • Enable the project code structure to accommodate different development and building environments, such as dev, staging, testing, prod, etc.
  • Aim for more automatic product building system, especially for auto-versioning.
  • Make the development process CI(Continuous Integration)-friendly and CD(Continuous Deployment)-friendly.

So that, we are introducing Gulp Flow – the streamline application development process to help tackle these points.

Please Note: Gulp Flow here is different from the NPM lib “gulp-flow“.

Special Thanks

The concept of Gulp Flow was heavily inspired by the following Github projects:

  • angularjs-gulp-browserify-boilerplate – It helps refine the idea of module auto-registration on modularized AngularJS and Gulp Task folder structure.
  • Ionic Gulp Seed – This gulp toolchain for Ionic starter project is the root of Gulp Flow, especially for the basic task structure (Gulp extended CLI) and the Ionic related Gulp tasks.
  • ionic-continuous-delivery-blog – This blog / Github repo provides a hug inspiration on Setting up Travis CI and HockeyApp.

Also, Google, Stack Overflow and personal blogs are always the best resources to get cool ideas and help solve issues. Thank all of the Gulp and Ionic/AngularJS community members to help us finish the initial building of Gulp Flow.

Gulp Flow

Gulp Flow has been verified on Mac OSX and Linux system. Windows has not been tested yet.

Gulp Flow streamlines the entire application development process, starting from project initial setup, to active development, to local and remote unit and e2e test. and then finally application building / releasing system. During the whole process, Gulp Task is the key to help conduct each step.

Gulp Task Folder Structure

Inside the Gulp Task Folder, each Gulp tasks have been categorized into different folders based on its focus, such as:

cliCLI wrapper for other CLI tools, like Ionic.
appinfoApp Info related tasks, such as “project init setup”, “versioning”.
envEnvironment related tasks, such as “setting env. variables”, “versioning”, “local server”, “livereload”, etc.
assetsImages and App Icons.
fontsIcon Fonts and Fonts.
stylesStyles files, such as Sass and CSS.
scriptsMain app JS files.
htmlsindex.html Injection and Template files.
testsUnit Test and E2E Test.
utilUtility Helpers, such as “error handler”, “logger”, “file path builder”, etc.
cli.jsExtended Gulp CLI.
config.jsThe CORE config file supports all other Gulp tasks.

Here is the top folder structure.

gulpfile/
├── appinfo
├── assets
├── cli
├── env
├── fonts
├── htmls
├── scripts
├── styles
├── test
├── util
├── cli.js
├── config.js
├── default.js
└── vendor.js

Extended Gulp CLI

The Extended CLI is done by the plugin yargs, mainly about:

  • Defining app running mode (development, emulator, device, release) and building Environment (development, staging, testing, production).
  • Specifying Versioning
## gulp -h
Usage: gulp [task] [options]

Options:
  -h, --help                   Show "Extended" commands added to "Gulp" CLI. 

  --build, --build-app         Build the app from "app/" into "www/" folder without "Styles and Scripts minification" done.
                               [boolean] [default: false]

  --release, --release-app     Prepare for "App Package". "App Build" is required - with "Styles and Scripts minification" done.
                               [boolean] [default: false]

  -e, --emulate                Load "emulator" to run the app. "App Build" is required. Based on `ionic emulate [platform]`. 
                               [string] [default: ""]

  -r, --run                    Load "device" to run the app. "App Build" is required. Based on `ionic run [platform]`.                 
                               [string] [default: ""]

  --env, --environment         Specify the "build environment", such as "development", "staging", "staging", "production", etc. 
                               [string] [default: "development"]

  -v, --version                Specify the "type" when bumping app "release" version. Use with task `gulp bump-release`. Possible options: major, premajor, minor, preminor, patch, prepatch, or prerelease (based on "semver").
                               [string] [default: "patch"]

  --preid, --prerelease-id     Specify the "prerelease id" when bumping the app version for "prerelease". Usually `alpha | beta | rc`.                
                               [string] [default: "alpha"]

Examples:
  gulp -e|--emulate android                       Build app and load "Android emulator" to run the app.
  gulp -r|--run ios                               Build app aad load "iOS device" to run the app.
  gulp bump-release --v=prerelease                Bump app "release" version for prerelease.
  gulp bump-release --v=prerelease --preid=beta   Bump app "release" version for "beta" prerelease.

config.js

The Gulp Flow core file config.js defines all the configuration parameters that Gulp tasks load and can be freely customized based on the actual project needs. It includes the following parts:

Project InfoThe project info related, such as “root path” for basic folder (project root, app root, dev target root, build target root, vendor root), app info source file and sync target files, target environments and versioning.
AssetsImages, icons, and fonts, including “iconfont” processing.
StylesSaas processing (autoprefixer and minifyCss).
ScriptsMulti-bundle Browserify”, JS lint, Lint HTML report.
HTMLsindex.html injection, templatesCacheUsed.
Local ServerExpress based.
TestingKarma, Protractor.
Helper FunctionsRoot path” based path builder.

Project Init Setup

Normally Project Init Setup involves two parts:

  • Set up basic project code structure.
  • Set up project info, like project name, app name, description, author info, etc.

Project Code Structure

The current boilerplate project includes the following 4 root folder:

  • app root folder – Where the main source code locates, JS scripts, styles, images, etc.
  • vendor root folder – The folder that holds all app level external libraries which is normally controlled by bower. The library styles and fonts should be from here.
  • dev root folder – The first-level target folder that the app code builds to. It is mainly used for doing browser testing.
  • build root folder – The normal www/ folder, targeted for emulator / device testing and final release. The code inside should be done minification.

All of these can be configured in

root: {
    // Project Root
    base: path.join(__dirname,  '../'),  
    // "App Root" - all the source code should be under this folder.
    app: "./app",
    // "Dev Root" - app will be built into this folder while doing "dev" preview (in browser)
    dev: "./.tmp",
    // "Build Root" - app will be built into this folder while doing "emulation", "device tests" and "release".
    build: "./www",
    // "Vendor Root" - by "bower".
    vendor: "./bower_components"
},

Project Info

In the current project folder, it has four config files that can be used to host project infopackage.json from NPM, bower.json from Bower, ionic.project from Ionic and config.xml from Cordova. In order to sync among these files, a dedicated json is created under the app foldermanifest.json as the source file of the project info. A Gulp task gulp appinfo is used to sync the info from manifest.json to all other files.

Here is the example structure of manifest.json.

{
    "name": "ionic-boilerplate",
    "id": "com.wuneo.boilerplate.ionic",
    "version": "0.6.6",
    "build-version": "666",
    "dev-version": "0.6.6-20160204",
    "description": "A project boilerplate for starting development with Ionic framework.",
    "keywords": [
        "ionic",
        "boilerplate",
        "angular",
        "gulp",
        "browserify",
        "sass",
        "karma",
        "protractor",
        "mocha",
        "chai",
        "sinon"
    ],
    "license": "MIT",
    "repository": {
        "type": "git",
        "url": "https://github.com/cowfox/ionic-boilerplate.git"
    },
    "author": {
        "name": "Lei SHI",
        "email": "foxshee@gmail.com",
        "url": "http://5neo.be"
    },
    "contributors": []
}

Among these files, the XML file config.xml from Cordova is the most important one – it controls the final app info that iOS and Android build to. When running gulp appinfo, this file is by default to be updated. For all other config files, you can control them from Gulp Config file.

appInfo: {
    manifest: "./manifest.json", // under `app` folder.
    syncTargets: [
        // Relative to 'App Root'.
        "./package.json",
        "./bower.json",
        "./ionic.project"
    ]
},

Versioning

The project versioning is also controlled by Gulp tasksgulp bump-XXX. In the current settings, it has three levels of versioning:

  • Build # – The internal incremental version # for the project. It is also automatically increased when building to build foldergulp --build.
  • Dev Version # – The version # maintained for development – usually for doing daily build by CI.
  • Release Version # – The official public version #. It follows the semver standard.
 # Use the following Gulp tasks to bump your app version.  
`gulp bump-build` - Bump the "Build #". 
`gulp bump-dev` - Bump the "Dev Version #". 
`gulp bump-release -v=prerelease --preid=beta` - Bump the "Release Version #" based on "semver" standard. 

Development and Testing

During the development and testing cycle, Gulp tasks are to help handle some typical actions, such as:

  • Set up local development server for reviewing the app in browsergulp serve.
  • Automatically watch file changes and update to the local development server correspondingly – gulp watch.
  • Re-build vendor deliverables, such as styles and scripts – gulp vendor.
  • Examine the code qualitygulp lint.
  • Conduct Unit and E2E Testgulp unit and gulp e2e.

Use the default Gulp task gulp default (or gulp) to help take care all of the tasks running during the development process.

gulp.task(taskName, function(done) {
        runSequence(
            'clean',
            'vendor',
            'iconfont',
            'templates',
            [
                'fonts',
                'appicon',
                'images',
                'main-styles',
                'main-scripts'
            ],
            'index',
            // Set ENV variables if need to do "build".
            cli.requiredBuild ? 'noop' : 'set-env',
            cli.requiredBuild ? 'noop' : 'serve',
            cli.requiredBuild ? 'noop' : 'watch',
            // If in "release" mode, bump "Build #"
            cli.inReleaseMode ? 'bump-build' : 'noop',
            cli.inEmulateMode ? ['ionic-emulate', 'watch'] : 'noop',
            cli.inRunMode ? 'ionic-run' : 'noop',
            done);
    });

Beyond that, gulp default also covers the following convenient operations:

  • gulp --build – Build the app from app folder to www folder.
  • gulp --build --env=production – Build the app to www in release mode. From there, you can then add building platform and prepare the app release. During this process, Build # will be automatically bumped as well.
  • gulp -e – Enable Emulator Running mode. It builds the app and then invokes Emulator to run it. The watch mode is enabled during this process.
  • gulp -r – Enable Device Running mode. It builds the app and runs it in your connected device.

Build and Release

In Gulp Flow, building and releasing app is based on Ionic CLI, such as ionic platform add XXX, ionic resources, ionic build ios --device --release, etc.

Here is a typical releasing procedure for iOS app:

export BUILD_TARGET=release
export BUILD_ENV=production
gulp --$BUILD_TARGET --env=$BUILD_ENV # Do a **release** build to `www` folder
gulp bump --env=$BUILD_ENV -v=prerelease --preid=beta # Do versioning. 
gulp appinfo --env=$BUILD_ENV # Update **app info** after versioning. 
gulp add-ios --env=$BUILD_ENV # Add an iOS platform
gulp resources --env=$BUILD_ENV # Prepare the app icon and splash images. 
ionic build ios --device --release # Build the `ipa` file and `app.dsym` folder. 

As shown, the two environment variables BUILD_TARGET(build target) and BUILD_ENV (build environment) are to help decide which type of app release is to be built.

  • BUILD_TARGET=build – Perform a build type app build. Normally, it does not do minification to Styles and Scripts for a better debugging practice.
  • BUILD_TARGET=release – Perform a release type app build. It adds minification to Styles and Scripts based on build type.
  • BUILD_ENV=development|test|staging|production – Different environments lead to different changes to the app code as well as building options, such as web services IP, versioning, reporting, etc.

iOS Codesigning

When releasing an iOS app, it needs the project’s codesigning to be configured correctly , including building certificate and provisioning profile. In order to automate this, a new Cordova Hook is added in after_platform_add step to update the configuration to xcconfig files.

These files are under ./platforms/ios/cordova/, which normally takes care of both Developer Certificate and Distribution Certificate. The related configurations are under the Gulp Flow Config file.

env: {
    // It depends on the env. variable `NODE_ENV" from NodeJS.
    envType: {
        development: 'development',
        test: 'test',
        staging: 'staging',
        production: 'production'
    },

    // ----
    // Environment global variables
    // ----
    "global" : {
    },

    // ----
    // Environment specific variables
    // ----
    "development": {
    // The **name** of the **Code Signing Identity**.
    XCODE_DEVELOPER_NAME: "iPhone Developer: Lei Shi (3Y46G6EDBE)",
    },
    "production": {
    // The **name** of the **Code Signing Identity**.
    XCODE_DEVELOPER_NAME: "iPhone Distribution: Lei Shi (3Y46G6EDBE)",
    }
},

Once building certificate decided, Xcode can search for compatible provisioning profile accordingly.

Continuous Integration and Continuous Deployment

Theoretically, the entire Gulp Flow should be working on any system (local or remote) that runs NodeJS and Gulp. In order words, it should be also working with any CI and CD service – when the code is checked out to a remote system and run nam install to setup the building environment and then use Gulp Flow to run tests or build app.

In the current Ionic Boilerplate project, it implements the solution for Travis CI as well as HockeyApp (a very great mobile app distribution service). Here is the list of addons.

  • .travis.yml – The config file of Travis CI.
  • /env/scripts/* – The scripts that are used when building the app with Travis CI, such as doing decryption on Xcode building files, managing Mac OS X keychain*, making iOS app package, and uploading iOS app to HockeyApp service.

Special Notes, instead of using Travis WebLint, here is another page to help validate your YML write up.

However, although using Travis CI is working correctly, I personally would NOT recommend to use Travis CI Open Source edition (using .org not .com) to build this project – just because it takes too much time to set up the building environment (about 20 min in this case), no mentioning the following procedure for running test and app building.

Ionic framework basic needs both Cordova and Ionic to be installed as global and then in our Gulp Flow case, running nam install is too heavy since it does have tons of Gulp dependencies in order to support the whole flow.

Here is a typical case when Travis CI runs the environment setup to Ionic Boilerplate. You can check the numbers shown on the right side for each procedure and get a general sense on the total time spending.

Travis Cache

As mentioned here, Travis does provide the capability to cache the dependencies and directories. However it currently only supports travis-ci.com and container-based infrastructure.

The features described here are currently only available for private repositories on travis-ci.com and our new container-based infrastructure.

— from https://docs.travis-ci.com/user/caching/

CI Suggestions

To speed up the overall CI running, doing cache is a must. So if you are not running on travis-ci.com, I would recommend to use other CI services which can support test agent so that we can set up a dedicated test agent server to have the build environment pre-setup and cache it for future use.

I’m currently using Bamboo CI and a first-tier server ($5/mo) from DigitalOcean which is good enough for me to run Unit Test as well as app build.

The current update already adds the Mocha-Reporter into Karma, so that Bamboo CI can directly shows test results in its test section.

Travis CI Special Mentions

There are several special mentions when configuring the .travis.yml file. Hope it will help you save some time when dealing with Travis CI.

Travis Encrypted Data

When using Travis CI to help do CI(Continuous Integration)-friendly and CD(Continuous Deployment), it is inevitable that you will include some sensitive data to the Travis CI config file (variables, like API key, APP ID, etc.) or to the project folder (file, like certificate, profile, etc.).

In order to help protect them, Travis CLI provides two convenient commands travis encrypt (link) and travis encrypt-file (link) to help encrypt the variables and files.

Please check on my another post.

Napa Packages

As mentioned earlier, Ionic Boilerplate is using Browerify to utilize modularization on AngularJS. In order to support those Non-CommonJS compatible packages, Browerify-shim is used. And to better support Browerify-shim, NPM Napa is included.

In order to make sure Napa to fetch the packages during npm install, adding a --unsafe-perm option simply does the trick. Otherwise, you may have the similar error as following.

events.js:142
      throw er; // Unhandled 'error' event
      ^

Error: module not found: "ionic-js" from file _fake.js
    at onresolve (/Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/module-deps/index.js:162:30)
    at /Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/browserify/index.js:489:18
    at /Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/browser-resolve/index.js:246:21
    at /Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/resolve/lib/async.js:55:18
    at load (/Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/resolve/lib/async.js:69:43)
    at onex (/Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/resolve/lib/async.js:92:31)
    at /Users/travis/build/cowfox/ionic-boilerplate-temp/node_modules/resolve/lib/async.js:22:47
    at FSReqWrap.oncomplete (fs.js:83:15)

Phantomjs

Phantomjs is very good for running Unit Test in the background, like the Trrvis CI case. However, it seems that having phantonjs installed locally will cause the issue that karma-phantomjs-launcher will not find the PhantomJS binary to launch..

To fix it, install it as a global package and set environment variable PHANTOMJS_BIN.

# Install `phantomjs` package globally, and then set `PHANTOMJS_BIN`.
# Otherwise, `karma-phantomjs-launcher` will not find the PhantomJS binary to launch.
- npm install -g phantomjs
- export PHANTOMJS_BIN=/usr/lib/node_modules/phantomjs/lib/phantom/bin/phantomjs

Future Work & Contributing

As the moment when Inoic Boilerplate is firstly released, there are still many items on the wishlist. I will continue to work on them and schedule future update. At the same time, please have a try and let me know if you have any issue or suggestion in improveing it.

If you would like to contribute to this work, please go ahead fork it and send back with Pull Request later.

For Ionic Boilerplate

  • Add Ionic IO configuration into the project.
  • Optimize the embedded sample app, trying to provide more cases to demo the normal Ionic practices, such as:
  • Build a helper module set to make our development life much easiler. Several items on the list:
    • A better logging system.
    • A middle layer DB service.
    • A HTTP service for RESTful API.

For Gulp Flow

  • Constantly improve the current Gulp Teasks and optimoze the use of Gulp plugins
  • Continue to finish the local app release process.
  • Add support to other configration files, like .travis.yml, .io-config.json, etc.

For Other Boilerplates?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>