logankoester / grunt-phonegap

A Grunt plugin to provide local build tasks for Phonegap applications
MIT License
173 stars 61 forks source link

Profile based configuration #54

Open szalishchuk opened 10 years ago

szalishchuk commented 10 years ago

Problem

I have two separate directories which I would like grunt-phonegap to use as base directory:

So, I would like to be able to build an app in debug mode with full source code files being used. I would also like to include weinre debug script there, which I already do with preprocessing my html file, but it requires grunt-phonegap to point to the right directory. And once I'm ready to make a release and deploy and .apk file, I would like to use my dist/webapp directory, where my assets are as thin as possible.

I don't feel that there's a viable solution with grunt-phonegap at this point to solve this case.

Solution

Profile based configuration (or whatever people like to call it) is pretty much a standard for grunt based plugins nowadays, because of, I assume, it is being straightforward and flexible at the same time. I believe grunt-phonegap would benefit from it as well.

Instead of this:

phonegap: {
    config: {
        // options ...
    }
}

We could have this:

phonegap: {
    options: {
        // Common settings
    }
    ,dev: {
        options: {
            // Settings that are specific for dev environment
        }
        ,someMethod: function() {} // e.g. releaseName
    }
    ,prod: {
        options: {
            // Settings that are specific for production environment
        }
        ,someMethod: function() {} // e.g. releaseName
    }
}

This way we can share common settings and method implementations in general scope and override those on profile level. Profile name is a custom thing that each developer creates for himself. Then, to call a specific setting you need to run the following grunt phonegap[:profileName][:nextOption][:nextOption] So you won't have to change your command chain convention, just add one command with the highest priority. If no profile was specified, then :default option should be used.

At the end of the day we would get the following task to build a release version of the app for android grunt phonegap:dev:release:android or grunt phonegap:prod:release:android

logankoester commented 10 years ago

Hey @szalishchuk, I like your suggestion. The reason it isn't already implemented this way already is that I've been using grunt-environment to accomplish the same goal.

Your solution, however, is more standard in the Grunt world, so this is probably a good direction to go.

For grunt-phonegap#1.0.0 (which I have not really been working on, just thinking about, so far), I think we should make a number of breaking changes to the configuration object, to wrap some of the more platform-specific features into their appropriate namespace. For example:

phonegap: {
  options: {
    icons: {} // Default icons for all environments, all platforms...
  }, platforms: {
    android: {
       // Android-specific options for all environments...
      versionCode: {}
      keys: {}
      icons: {} // Icons specifically themed for Android
    }
  }
  development: {
    options: {
      icons: {} // Default icons with a badge to indicate "development" builds
    }, platforms: {
      android: {
        icons: {} // Android-specific icons with a badge to indicate "development" builds
      }
    }
  }
}

This is essentially two layers of "profiles", one being named by the end-user and the other being named by the set of supported platforms by Phonegap. It might seem a little contrived in this example, but it gives users a lot of flexibility to configure their projects, and helps clean up the config object's top-level.

How does this look to you?

szalishchuk commented 10 years ago

phonegap.platforms is a great idea, because it is currently not obvious which option is platform specific and which isn't. Although in this case I would change an order of commands in the grunt task to be grunt phonegap:prod:android:release instead of grunt phonegap:prod:release:android to make chaining semantically go through the namespace hierarchy (:prod:android) and then execute an action on these (:release). It is also have a flavor of natural language.

logankoester commented 10 years ago

Agreed! On Feb 18, 2014 11:30 AM, "Sviatoslav Zalishchuk" notifications@github.com wrote:

Problem

I have two separate directories which I would like grunt-phonegap to use as base directory:

  • source code (dev files with comments that I work with)
  • dist/webapp which I build with another grunt task. It has everything stripped down to 3 files: index.html, package.js and package.css

So, I would like to be able to build an app in debug mode with full source code files being used. I would also like to include weinre debug script there, which I already do with preprocessing my html file, but it requires grunt-phonegap to point to the right directory. And once I'm ready to make a release and deploy and .apk file, I would like to use my dist/webapp directory, where my assets are as thin as possible.

I don't feel that there's a viable solution with grunt-phonegap at this point to solve this case. Solution

Profile based configuration (or whatever people like to call it) is pretty much a standard for grunt based plugins nowadays, because of, I assume, it is being straightforward and flexible at the same time. I believe grunt-phonegap would benefit from it as well.

Instead of this:

phonegap: { config: { // options ... } }

We could have this:

phonegap: { options: { // Common settings } ,dev: { options: { // Settings that are specific for dev environment } ,someMethod: function() {} // e.g. releaseName } ,prod: { options: { // Settings that are specific for production environment } ,someMethod: function() {} // e.g. releaseName } }

This way we can share common settings and method implementations in general scope and override those on profile level. Profile name is a custom thing that each developer creates for himself. Then, to call a specific setting you need to run the following grunt phonegap[:profileName][:nextOption][:nextOption] So you won't have to change your command chain convention, just add one command with the highest priority. If no profile was specified, then :default option should be used.

At the end of the day we would get the following task to build a release version of the app for android grunt phonegap:dev:release:android or grunt phonegap:prod:release:android

Reply to this email directly or view it on GitHubhttps://github.com/logankoester/grunt-phonegap/issues/54 .

mkcode commented 10 years ago

I ended up hacking this in to my project. Here is how I did it:

In gruntfile.coffee:

module.exports = (grunt) ->
  target = grunt.option('target') || 'development'
  appConfig = require('./native/config').forEnv(target)

  grunt.initConfig
    phonegap:
      config:
        root: 'native/www'
        config: 'native/www/config.xml'
        cordova: 'native/.cordova'
        path: appConfig.buildPath()
        plugins: ['https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git'
                  'https://github.com/mkcode/org.apache.cordova.statusbar'
                  'https://git-wip-us.apache.org/repos/asf/cordova-plugin-network-information.git']
        platforms: ['ios']
        maxBuffer: 200 # You may need to raise this for iOS.
        verbose: true
        releases: 'releases'
        releaseName: ->
          pkg = grunt.file.readJSON('package.json')
          return(pkg.name + '-' + pkg.version)

        # Must be set for ios to work.
        # Should return the app name.
        name: -> appConfig.appName
          # pkg = grunt.file.readJSON('package.json')
          # return pkg.name

native/config.coffee:

# Set properties for the native app in this file
# GlobalConfig keys are overwritten by specific env keys

_localIP = require('address').ip()

globalConfig =
  buildPath: -> 'native-build/myapp-native-' + @env

envConfig =
  development:
    appName: 'My App - Dev'
    appId: 'com.myapp.mobile.development'
    mobileRoot: "http://#{_localIP}:3333"
    urlRoot: "http://#{_localIP}:5000"
    debug: true
    serverIP: _localIP
  staging:
    appName: 'My App - Staging'
    appId: 'com.myapp.mobile.staging'
    mobileRoot: "http://m.staging.myapp.com"
    urlRoot: "https://myapp-staging.herokuapp.com"
  production:
    appName: 'My App'
    appId: 'com.myapp.mobile'
    mobileRoot: "http://m.myapp.com"
    urlRoot: "https://myapp.com"

extend = (object, properties) ->
  for key, val of properties
    object[key] = val
  object

exports.forEnv = (env) ->
  config = extend(globalConfig, envConfig[env])
  config['env'] = env
  config[env] = true
  config
mkcode commented 10 years ago

And those mobileRoot and urlRoot properties are fed into an index.html handlebars template that is copied into phonegap before phonegap build is run

Doing it like this allows me to run: grunt phonegap:build --target staging and it makes a specific application for that env

mkcode commented 10 years ago

Also forgot to mention, for this to work on iOS, the config.xml needs to be handled as well:

In config.xml.hbs:

<?xml version='1.0' encoding='utf-8'?>
<widget id="{{ appId }}" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0">
    <name>{{ appName }}</name>

So the build process is:

logankoester commented 10 years ago

Interesting hack @mkcode! I've seen at least three ways to do this now, but @szalishchuk is right - Grunt has a standard API for this and it would be ideal if a future version of grunt-phonegap could adhere to it.

runspired commented 10 years ago

Right now I'm accomplishing this by having a global phonegap variable.

var phonegapConfig = {
   dev : {...},
   staging : {...},
   prod : {...},
}

Which I use with an environment flag:

grunt.initConfig({

        phonegap: phonegapConfig[grunt.option('target') || 'dev']

});

I agree that using the standard grunt interface for multiple environments would be nice, but it also makes sense to me how that current phonegap tasks mirror the phonegap cli commands.

julkue commented 8 years ago

:+1: