Everything Titanium using the CLI

Everything Titanium using the CLI

Titanium's new CLI has got some sugar from Appcelerator's team, and is now a very powerful and extensible tool. Indeed, it has become a sort of Swiss knife for the Titanium developer, allowing to automate various tasks : sdk install / upgrade, project build / deployment, translatable strings extraction, etc. Apart from the official CLI tool, there are numerous other useful tools : alloy, gittio, etc. Even more, it is possible to hack most of these tools to extend their capacities through the commands concept.

This talk, given during the TiConf Amsterdam 2014 (http://ticonf.org), showcases a lot of command line tools for productive Titanium developers, and explains how to create custom commands.

D219d638498612dc190342584a0b1149?s=128

Xavier Lacot

June 29, 2014
Tweet

Transcript

  1. TiConf Amsterdam - Xavier Lacot - June 29th, 2014 Everything

    Titanium using the CLI
  2. Hello, I am Xavier • Founder, Web- and Mobile- expert

    at JoliCode • Contributor to several Open Source projects • Titanium developer since 2009 • Speaker at CodeStrong and TiConf • Former President of the French Association of PHP Users • Co-organizer of the Titanium Paris meetup group @xavierlacot with TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 2
  3. None
  4. A (brief) history as a Titanium developer...

  5. Titanium developer: 2009-2011 TiConf 2014 - Monitor Your App: A

    Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 5
  6. Titanium Studio: 2011-present TiConf 2014 - Monitor Your App: A

    Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 6
  7. My experience aside Titanium • Symfony developer since a lot

    of years • nodejs developer more recently • used to use Command Line Interfaces, aka. CLI TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 7
  8. The day when Titanium got his very own CLI TiConf

    2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 8
  9. CLI > GUI • a long debate... • Pros: •

    higher productivity • automate/script tasks • use the convenient tools rather than a "all-in-one" solution • My CLI is 1000% faster than your Eclipse crap ( Appcelerator) • feel like a hipster-hacker when it works • Cons: not polished and well packaged TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 9
  10. GUI CLI TiConf 2014 - Monitor Your App: A Complete

    Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 10
  11. The Titanium CLI...

  12. Get / install the tool • Installing the CLI is

    an easy task: $  npm  install  -­‐g  titanium • Want to live dangerously? $  npm  install  -­‐g  git://github.com/appcelerator/titanium.git TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 12
  13. ticonf-2014$ ^[p $ titanium sdk • $ ti sdk helps

    manage the installed Titanium sdk: • list : list installed sdks • install : (un-)install a sdk version / update to newest • select : select a specific version by default TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 13
  14. $ titanium config • ti config : list all options

    • ti config user.email : retrieves the value of a property • ti config user.email "hello@ticonf.org" : sets property Some interesting configuration options • cli.completion : enable cli tab-completion (working soon) • genymotion.enabled : enabled to push directly to genymotion without manually calling "adb" TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 14
  15. These settings are stored in ~/.titanium/config.json {   "user":  {

        "name":  "Xavier  Lacot",     "email":  "xavier@lacot.org"   },   "app":  {     "workspace":  "/Users/xavier/Documents/workspace/titanium",     "idprefix":  "com.jolicode",   },   [...],   "sdk":  {     "selected":  "3.2.3.GA"   },   "genymotion":  {     "enabled":  true   } } TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 15
  16. ticonf-2014$ ^[p $ titanium create • A Swiss tool for

    creating projects! • interactive: asks common questions (id, name, platforms, etc.) • fast: create a project in 10 seconds! • lightweight: it uses a simple two-tabs project template TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 16
  17. TiConf 2014 - Monitor Your App: A Complete Panel of

    Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 17
  18. Troubleshouting the configuration • $ ti setup check : checks

    the configuration TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 18
  19. Troubleshouting the configuration • $ ti setup quick : complete

    setup (developer name, sdk version, etc.) • $ ti setup app : default app values (company app id prefix, publisher, website, etc.) • other commands: user , network , cli , sdk , ios , android TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 19
  20. ticonf-2014$ ^[p $ titanium build • all platforms are supported

    • simulators or real devices • even store-submission is possible! $  ti  build  -­‐p  ios $  ti  build  -­‐p  android  -­‐T  dist-­‐playstore  -­‐K  jolicode.keystore  -­‐L  jolicode TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 20
  21. A lot of other commands... • $ ti status displays

    the current login/user status • $ ti info displays a complete diagnosis of the platform: • OS, JAVA, XCode • Titanium SDKs • Android SDK, Platforms and Emulators • Genymotion Emulators • iOS certificates and provisionnings, simulators, connected devices • etc. TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 21
  22. Extending the CLI

  23. Built extensible through plugins • add new commands • hook

    into exisiting commands TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 23
  24. Creating new commands 1. a single js file, where you

    want /path/to/sthing/pony.js 2. register it for Titanium's cli: $  ti  config  -­‐a  paths.commands  /path/to/sthing (adds /path/to/sthing to the paths.commands config array) 3. write the content of the command • a declarative part • command description, name, etc. • optionnaly, a config() method to set the default value of some execution option TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 24
  25. Creating new commands • the content of the command •

    a validate() method called to validate the options passed to the command • a run() method, the "body" of the command • the command name is the filename • pony for /path/to/sthing/pony.js • not possible to rename/alias it - ticket + PR on their way :-) • the latter loaded takes precedence the last pony command overrides an earlier pony TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 25
  26. A sample custom command exports.cliVersion  =  '>=3.2'; exports.name  =  'pony:fly';

    exports.desc  =  'makes  ponies  fly'; exports.config  =  function(logger,  config,  cli)  {        return  {                noAuth:  true,                skipBanner:  true,                options:  {                        quantity:  {                                abbr:  'qty',                                default:  1,                                desc:  'number  of  ponies  flying'                        }                }        }; };  ​  ​  ​ 1 3 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 26
  27. A sample custom command exports.cliVersion  =  '>=3.2'; exports.name  =  'pony:fly';

    exports.desc  =  'makes  ponies  fly'; exports.config  =  function(logger,  config,  cli)  {        return  {                noAuth:  true,                skipBanner:  true,                options:  {                        quantity:  {                                abbr:  'qty',                                default:  1,                                desc:  'number  of  ponies  flying'                        }                }        }; };  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​  ​ 5 17 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 27
  28. A sample custom command exports.validate  =  function  (logger,  config,  cli)

     {        if  (cli.argv.quantity)  {                if  (!/^\d+$/.test(cli.argv.quantity))  {                        logger.banner();                        logger.error('quantity  must  be  an  integer\n');                        process.exit(1);                }        } }; exports.run  =  function  (logger,  config,  cli,  finished)  {        var  i  =  0;        while  (i  <  cli.argv.quantity)  {                console.log('ponies  are  flying!');                i++;        } };  ​  ​  ​  ​  ​  ​  ​  ​  ​ 1 9 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 28
  29. A sample custom command exports.validate  =  function  (logger,  config,  cli)

     {        if  (cli.argv.quantity)  {                if  (!/^\d+$/.test(cli.argv.quantity))  {                        logger.banner();                        logger.error('quantity  must  be  an  integer\n');                        process.exit(1);                }        } }; exports.run  =  function  (logger,  config,  cli,  finished)  {        var  i  =  0;        while  (i  <  cli.argv.quantity)  {                console.log('ponies  are  flying!');                i++;        } };  ​  ​  ​  ​  ​  ​  ​  ​ 11 18 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 29
  30. ticonf-2014$ ^[p A sample custom command TiConf 2014 - Monitor

    Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 30
  31. Hooking into commands: concept • idea: run operations when certain

    events occur • hooks are a common concept, not specific to Titanium • can somehow be seen as "events" at the cli scale TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 31
  32. Hooking into commands: howto 1. a single js file, where

    you want /path/to/sthing/soundAfterBuild.js 2. register it for Titanium's cli: $  ti  config  -­‐a  paths.hooks  /path/to/sthing (adds /path/to/sthing to the paths.hooks config array) 3. write the content of the hook • using cli.on() , attach listeners to the occurence of hooks • cli.addHook() is an alias for cli.on() TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 32
  33. ticonf-2014$ ^[p A sample custom hook var  path  =  require('path'),

      play  =  require('play'); exports.cliVersion  =  '>=3.2'; exports.init  =  function(logger,  config,  cli,  nodeappc)  {        cli.on('build.post.compile',  function(builder,  next)  {                play.sound(path.resolve(__dirname,  'sounds',  'trumpet.wav'));                next();        }) };  ​ 6 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 33
  34. • build.pre.construct • build.pre.compile • build.android.startEmulator • build.android.copyResource • build.android.titaniumprep

    • build.android.writeAndroidManifest • build.android.aapt • build.android.javac • build.android.proguard • build.android.dexer • build.android.jarsigner • build.android.zipalign • build.ios.copyResource • build.ios.prerouting • build.ios.titaniumprep • build.ios.xcodebuild • build.ios.writeBuildManifest • build.post.compile • build.finalize • create.pre • create.post • clean.pre • clean.post • cli:go • cli:command-loaded • cli:pre-validate • cli:post-validate • cli:pre-execute • cli:post-execute • help:header Available hooks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 34
  35. Some remarks about hooks • you can call hooks manually,

    using cli.emit : • hooks callbacks can have a priority (default 1000): cli.on('some.event',  {        priority:  999,        post:  function()  {  /*  this  is  the  callback  */  } }); The lower the priority, the earlier the hook is executed /**  *  @param  {String|Array}  hookNames  -­‐  The  hook  name  or  an  array  of  many  hook  names  *  @param  {Object}  [data]  -­‐  The  event  payload  *  @param  {Function}  callback  -­‐  A  callback  when  the  event  has  finished  firing  */ function  emit(hookNames,  data,  callback) TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 35
  36. Some remarks about commands • do not forget to manually

    call the finished() callback: Else, the cli:post-execute hooks never gets called exports.run  =  function  (logger,  config,  cli,  finished)  {        var  i  =  0;        while  (i  <  cli.argv.quantity)  {                console.log('ponies  are  flying!');                i++;        }        if  (typeof  finished  ==  'function')  {                finished()        } };  ​  ​  ​ 9 11 TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 36
  37. package cli plugins • cli plugins are made of hooks

    and commands, and can be installed globally or locally • global install: • create the plugin somewhere, eg. /path/to/plugin • $ ti config paths.plugins -a /path/to/plugin • the plugin can contain commands and/or hooks • it will work in every Titanium project TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 37
  38. package cli plugins • local install: • put the plugin

    in the plugins folder of your project • reference it in tiapp.xml : < app   ti="http://ti.appcelerator.org">        <plugins>                <plugin  version="1.0">ticonf.playSound</plugin>        </plugins> </ app> • may only contain hooks, not commands • usable hooks: the ones after cli:post-validate ti: xmlns: ti: TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 38
  39. Some interesting CLI plugins

  40. ti-i18n • a replacement for alloy's extract-i18n command • extract

    translation strings from your app $  ti  help  i18n Usage:  titanium  i18n  <subcommand> CLI  to  manage  internationalizing  your  Titanium  app. Titanium  i18n  CLI  Subcommands:      extract      extract  i18n  strings  from  the  source  code  (js  and  tss  files) • focus on your code, not on translation dictionnaries thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 40
  41. ti-installr-hook • push your dev builds to Installr • install

    it: $  npm  install  -­‐g  ti-­‐installr-­‐hook  -­‐-­‐unsafe-­‐perm • Set token in tiapp.xml : • build the app: $  ti  build  -­‐p  ios  -­‐T  dist-­‐adhoc  -­‐-­‐installr • and voila! <property  name="installr.api_token">ENTER_INSTALLR_API_TOKEN_HERE</property <property  name="installr.notify"  type="bool">true</property> TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 41
  42. ti-testflight-hook • push your dev builds to Testflight (ios only).

    Do not use it. Go Installr. • install it: $  npm  install  -­‐g  ti-­‐testflight-­‐hook  -­‐-­‐unsafe-­‐perm • Set token in tiapp.xml : <property  name="testflight.api_token">ENTER_API_TOKEN_HERE</property> <property  name="testflight.team_token">ENTER_TEAM_TOKEN_HERE</property> • build the app: $  ti  build  -­‐p  ios  -­‐T  dist-­‐adhoc  -­‐-­‐testflight • and voila! thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 42
  43. alloy hooks into ti cli the $ alloy new task

    installs this plugin: exports.installPlugin  =  function(alloyPath,  projectPath)  {   var  id  =  'ti.alloy';   //  copy  plugin   var  srcFile  =  path.join(alloyPath,'Alloy','plugin',CONST.PLUGIN_FILE);   var  destFile  =  path.join(projectPath,'plugins',id,CONST.PLUGIN_FILE);   exports.copyFileSync(srcFile,  destFile);   //  add  the  plugin  to  tiapp.xml,  if  necessary   tiapp.init(path.join(projectPath,  'tiapp.xml'));   tiapp.installPlugin({     id:  'ti.alloy',     version:  '1.0'   }); }; thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 43
  44. TiNy CLI Some magic for lazy developers annoyed with long

    command lines 1. install it: $  npm  install  -­‐g  tn  -­‐-­‐unsafe-­‐perm 2. Have fun: $  titanium  build  -­‐-­‐platform  ios  -­‐-­‐device-­‐family  ipad now types: $  tn  ipad TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 44
  45. TiNy CLI Stunning features: • save build receipes: now types:

    $  tn  ship-­‐my-­‐app • lots of built-in receipes (pre-saved configurations). List: $  tn  list $  tn  save  ship-­‐my-­‐app  172B24F5-­‐1337-­‐1337-­‐1337-­‐D08EB0A7EA5D  "Check  Norris  (6MBV5WT2BD)" thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 45
  46. Other Titanium-related CLI

  47. ticonf-2014$ ^[p $ alloy • all alloy developers know the

    alloy CLI tool • new : turns a "classic" project into "alloy" style • compile : runs the alloy compilation • generate : generates code (controllers, widgets, etc.) TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 47
  48. $ gittio TiConf 2014 - Monitor Your App: A Complete

    Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 48
  49. $ gittio • also available as a CLI tool! $

     npm  install  -­‐g  gittio • manage native modules and alloy widgets using gitt.io: $  gittio  install  com.jolicode.pageflow $  gittio  update  -­‐g $  gittio  update  -­‐t  widget • want to try a module / widget? $  gittio  demo  dk.napp.drawer  -­‐p  ios thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 49
  50. tiapp.xml • not a CLI tool • a library for

    parsing and manipulating the tiapp.xml file npm  install  tiapp.xml • Usage: var  tiapp  =  require('tiapp.xml').load('./tiapp.xml'); tiapp.id  =  'com.other.name'; //  or  whichever  of  tiapp.xml  properties tiapp.write(); thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 50
  51. $ ticons • 2 versions of TiCons: • http://ticons.fokkezb.nl/ (PHP

    implementation) • The CLI tool ✌ $  npm  install  -­‐g  ticons Note that it has a dependacy on imagemagick . • Supports: • icons and assets • splashscreen - with 9-patch and locale support • more up-to-date than Appcelerator's alternative Spork (outdated) TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 51
  52. $ ticons • icons radius for android • orientation, platform

    and project type detection • mutualization of ios and Android MDPI images (lighter repository) $  ticons  -­‐h    Usage:  ticons  command  <args>  [options]    Commands:        icons  [options]  [input]  generate  icons        splashes  [options]  [input]  generate  splash  screens  (aka  launch  images)        assets  [input]                  generate  missing  densities  for  input  asset(s) thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 52
  53. $ tipi (and tetanize) • tetanize : • a "npm

    package" to "titanium commonjs module" transformer $  npm  install  -­‐g  tetanize • Usage: $  cd  /path/to/underscorejs $  tetanize • tipi : • keeps track of tetanize -generated modules installed $  npm  install  -­‐g  tipi $  tipi  install  underscore thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 53
  54. $ tishadow • no, promises, I won't say a word

    about TiShadow. Go use it now. thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 54
  55. $ ti-stealth • avoid to ship applications with console.log statements

    $  npm  install  -­‐g  ti-­‐stealth • remove or restore debug statements from the CLI: $  ti-­‐stealth  enable  [-­‐-­‐levels  <levels>]  [-­‐-­‐not-­‐levels  <levels>] $  ti-­‐stealth  restore comments / uncomments console.log and Ti.Api.info|debug|error|etc. in Resources • a sample alloy.jmk file shows how to automate logs removal in production and... guess what... thanks TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions TiConf 2014 - Monitor Your App: A Complete Panel of Titanium Monitoring Solutions 55
  56. Xavier Lacot http://jolicode.com Thank you! Go Oranje!