Skip to content
This repository
Browse code

Merge pull request #292 from 37signals/rvm-deprecation

Deprecate automatic RVM support
  • Loading branch information...
commit 3e62babd559430baa21aac8a36072c2c0a7d5aa0 2 parents b7ea07d + e789bcd
Sam Stephenson authored June 06, 2012
1  Cakefile
@@ -29,6 +29,7 @@ buildTemplates = (callback) ->
29 29
     compile("http_server/layout.html")
30 30
     compile("http_server/proxy_error.html")
31 31
     compile("http_server/rackup_file_missing.html")
  32
+    compile("http_server/rvm_deprecation_notice.html")
32 33
     compile("http_server/welcome.html")
33 34
     compile("installer/cx.pow.firewall.plist")
34 35
     compile("installer/cx.pow.powd.plist")
66  MANUAL.md
Source Rendered
@@ -186,20 +186,64 @@ loaded first.
186 186
 
187 187
 ### Working With Different Versions of Ruby ###
188 188
 
189  
-Pow offers full support for running multiple applications under
190  
-different versions of Ruby with
191  
-[rvm](http://rvm.beginrescueend.com/). Just add a [project
192  
-`.rvmrc`](http://rvm.beginrescueend.com/workflow/rvmrc/#project)
193  
-file to your application's root directory and you're good to go.
  189
+Pow invokes each application's Ruby processes in an isolated
  190
+environment. This design makes it possible to use different Ruby
  191
+runtimes on a per-application basis.
194 192
 
195  
-For example, to instruct Pow to run an application with Ruby 1.9.2,
196  
-use this `.rvmrc` file (assuming you've already installed 1.9.2 with
197  
-`rvm install 1.9.2`):
  193
+There are three ways to specify which version of Ruby to use for a
  194
+particular application.
198 195
 
199  
-    rvm 1.9.2
  196
+#### Specifying Ruby Versions with rbenv ####
200 197
 
201  
-If an application has an `.rvmrc` file but rvm isn't installed, Pow
202  
-will ignore the `.rvmrc` file and fall back to your system Ruby.
  198
+You can use [rbenv](https://github.com/sstephenson/rbenv) to specify
  199
+per-application Ruby versions for use with Pow.
  200
+
  201
+The `rbenv local` command lets you set a per-project Ruby version by
  202
+saving an `.rbenv-version` file in the current directory. For example,
  203
+to configure a particular application to run with Ruby 1.9.3-p194,
  204
+change to the application's directory and run:
  205
+
  206
+    $ rbenv local 1.9.3-p194
  207
+
  208
+Assuming you have set up rbenv in your login environment, there is no
  209
+additional configuration necessary to use it with Pow.
  210
+
  211
+For more information, see the [rbenv
  212
+documentation](https://github.com/sstephenson/rbenv#readme).
  213
+
  214
+#### Specifying Ruby Versions with RVM ####
  215
+
  216
+[RVM](http://rvm.io/) is another option for specifying per-application
  217
+Ruby versions for use with Pow.
  218
+
  219
+You can create a [project `.rvmrc`
  220
+file](https://rvm.io/workflow/rvmrc#project) to specify an
  221
+application's Ruby version. For example, to configure your application
  222
+to run with Ruby 1.8.7, add the following to `.rvmrc` in the
  223
+application's root directory:
  224
+
  225
+    rvm 1.8.7
  226
+
  227
+Because RVM works by injecting itself into your shell, you must first
  228
+load it in your application's `.powrc` or `.powenv` file using the
  229
+following code:
  230
+
  231
+    [ ! -f "$rvm_path/scripts/rvm" ] || source "$rvm_path/scripts/rvm"
  232
+    [ ! -f ".rvmrc" ] || source ".rvmrc"
  233
+
  234
+For more information, see the [RVM web site](http://rvm.io/).
  235
+
  236
+#### Specifying Ruby Versions by Setting $PATH ####
  237
+
  238
+You can adjust the `PATH` environment variable in `.powrc` or
  239
+`.powenv` to select Ruby versions on a per-application basis. For
  240
+example, if you have Ruby installed into `/opt/ruby-1.8.7`, you can
  241
+add the following to `.powenv`:
  242
+
  243
+    export PATH="/opt/ruby-1.8.7/bin:$PATH"
  244
+
  245
+When Pow loads your application, it will find and use the first `ruby`
  246
+binary in your `PATH` — in this case `/opt/ruby-1.8.7/bin/ruby`.
203 247
 
204 248
 ### Serving Static Files ###
205 249
 
2  build.sh
@@ -9,7 +9,7 @@ DIST="$(pwd)/dist"
9 9
 cake build
10 10
 
11 11
 mkdir -p "$ROOT/$VERSION/node_modules"
12  
-cp -R package.json bin lib "$ROOT/$VERSION"
  12
+cp -R package.json bin lib libexec "$ROOT/$VERSION"
13 13
 cp Cakefile "$ROOT/$VERSION"
14 14
 cd "$ROOT/$VERSION"
15 15
 BUNDLE_ONLY=1 npm install --production &>/dev/null
11  lib/configuration.js
@@ -74,7 +74,8 @@
74 74
       this.extDomains = (_ref10 = typeof (_base1 = this.extDomains).split === "function" ? _base1.split(",") : void 0) != null ? _ref10 : this.extDomains;
75 75
       this.allDomains = this.domains.concat(this.extDomains);
76 76
       this.allDomains.push(/\d+\.\d+\.\d+\.\d+\.xip\.io$/, /[0-9a-z]{1,7}\.xip\.io$/);
77  
-      this.hostRoot = (_ref11 = env.POW_HOST_ROOT) != null ? _ref11 : libraryPath("Application Support", "Pow", "Hosts");
  77
+      this.supportRoot = libraryPath("Application Support", "Pow");
  78
+      this.hostRoot = (_ref11 = env.POW_HOST_ROOT) != null ? _ref11 : path.join(this.supportRoot, "Hosts");
78 79
       this.logRoot = (_ref12 = env.POW_LOG_ROOT) != null ? _ref12 : libraryPath("Logs", "Pow");
79 80
       this.rvmPath = (_ref13 = env.POW_RVM_PATH) != null ? _ref13 : path.join(process.env.HOME, ".rvm/scripts/rvm");
80 81
       this.dnsDomainPattern = compilePattern(this.domains);
@@ -97,6 +98,14 @@
97 98
       return (_base = this.loggers)[name] || (_base[name] = new Logger(path.join(this.logRoot, name + ".log")));
98 99
     };
99 100
 
  101
+    Configuration.prototype.disableRvmDeprecationNotices = function() {
  102
+      return fs.writeFile(path.join(this.supportRoot, ".disableRvmDeprecationNotices"), "");
  103
+    };
  104
+
  105
+    Configuration.prototype.enableRvmDeprecationNotices = function() {
  106
+      return fs.unlink(path.join(this.supportRoot, ".disableRvmDeprecationNotices"));
  107
+    };
  108
+
100 109
     Configuration.prototype.findHostConfiguration = function(host, callback) {
101 110
       var _this = this;
102 111
       if (host == null) {
38  lib/http_server.js
@@ -71,6 +71,8 @@
71 71
 
72 72
       this.handleApplicationNotFound = __bind(this.handleApplicationNotFound, this);
73 73
 
  74
+      this.handleRvmDeprecationRequest = __bind(this.handleRvmDeprecationRequest, this);
  75
+
74 76
       this.handleProxyRequest = __bind(this.handleProxyRequest, this);
75 77
 
76 78
       this.findRackApplication = __bind(this.findRackApplication, this);
@@ -83,7 +85,7 @@
83 85
 
84 86
       this.logRequest = __bind(this.logRequest, this);
85 87
 
86  
-      HttpServer.__super__.constructor.call(this, [o(this.logRequest), o(this.annotateRequest), o(this.handlePowRequest), o(this.findHostConfiguration), o(this.handleStaticRequest), o(this.findRackApplication), o(this.handleProxyRequest), o(this.handleApplicationRequest), x(this.handleErrorStartingApplication), o(this.handleFaviconRequest), o(this.handleApplicationNotFound), o(this.handleWelcomeRequest), o(this.handleRailsAppWithoutRackupFile), o(this.handleLocationNotFound)]);
  88
+      HttpServer.__super__.constructor.call(this, [o(this.logRequest), o(this.annotateRequest), o(this.handlePowRequest), o(this.findHostConfiguration), o(this.handleStaticRequest), o(this.findRackApplication), o(this.handleProxyRequest), o(this.handleRvmDeprecationRequest), o(this.handleApplicationRequest), x(this.handleErrorStartingApplication), o(this.handleFaviconRequest), o(this.handleApplicationNotFound), o(this.handleWelcomeRequest), o(this.handleRailsAppWithoutRackupFile), o(this.handleLocationNotFound)]);
87 89
       this.staticHandlers = {};
88 90
       this.rackApplications = {};
89 91
       this.requestCount = 0;
@@ -187,7 +189,7 @@
187 189
       return exists(join(root, "config.ru"), function(rackConfigExists) {
188 190
         var application, _base, _ref1;
189 191
         if (rackConfigExists) {
190  
-          req.pow.application = (_ref1 = (_base = _this.rackApplications)[root]) != null ? _ref1 : _base[root] = new RackApplication(_this.configuration, root);
  192
+          req.pow.application = (_ref1 = (_base = _this.rackApplications)[root]) != null ? _ref1 : _base[root] = new RackApplication(_this.configuration, root, req.pow.host);
191 193
         } else if (application = _this.rackApplications[root]) {
192 194
           delete _this.rackApplications[root];
193 195
           application.quit();
@@ -230,6 +232,38 @@
230 232
       return req.pow.resume();
231 233
     };
232 234
 
  235
+    HttpServer.prototype.handleRvmDeprecationRequest = function(req, res, next) {
  236
+      var action, application, match;
  237
+      if (!(application = req.pow.application)) {
  238
+        return next();
  239
+      }
  240
+      if (match = req.url.match(/^\/__pow__\/rvm_deprecation(.*)/)) {
  241
+        action = match[1];
  242
+        if (!(action === "" || req.method === "POST")) {
  243
+          return next();
  244
+        }
  245
+        switch (action) {
  246
+          case "":
  247
+            true;
  248
+            break;
  249
+          case "/add_to_powrc":
  250
+            application.writeRvmBoilerplate();
  251
+            break;
  252
+          case "/enable":
  253
+            this.configuration.enableRvmDeprecationNotices();
  254
+            break;
  255
+          case "/disable":
  256
+            this.configuration.disableRvmDeprecationNotices();
  257
+            break;
  258
+          default:
  259
+            return next();
  260
+        }
  261
+        return renderResponse(res, 200, "rvm_deprecation_notice");
  262
+      } else {
  263
+        return next();
  264
+      }
  265
+    };
  266
+
233 267
     HttpServer.prototype.handleApplicationRequest = function(req, res, next) {
234 268
       var application;
235 269
       if (application = req.pow.application) {
28  lib/rack_application.js
... ...
@@ -1,6 +1,7 @@
1 1
 // Generated by CoffeeScript 1.3.3
2 2
 (function() {
3  
-  var RackApplication, async, basename, bufferLines, exists, fs, join, nack, pause, sourceScriptEnv, _ref, _ref1;
  3
+  var RackApplication, async, basename, bufferLines, exists, fs, join, nack, pause, resolve, sourceScriptEnv, _ref, _ref1,
  4
+    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
4 5
 
5 6
   async = require("async");
6 7
 
@@ -10,13 +11,14 @@
10 11
 
11 12
   _ref = require("./util"), bufferLines = _ref.bufferLines, pause = _ref.pause, sourceScriptEnv = _ref.sourceScriptEnv;
12 13
 
13  
-  _ref1 = require("path"), join = _ref1.join, exists = _ref1.exists, basename = _ref1.basename;
  14
+  _ref1 = require("path"), join = _ref1.join, exists = _ref1.exists, basename = _ref1.basename, resolve = _ref1.resolve;
14 15
 
15 16
   module.exports = RackApplication = (function() {
16 17
 
17  
-    function RackApplication(configuration, root) {
  18
+    function RackApplication(configuration, root, firstHost) {
18 19
       this.configuration = configuration;
19 20
       this.root = root;
  21
+      this.firstHost = firstHost;
20 22
       this.logger = this.configuration.getLogger(join("apps", basename(this.root)));
21 23
       this.readyCallbacks = [];
22 24
       this.quitCallbacks = [];
@@ -96,9 +98,10 @@
96 98
         var rvm;
97 99
         if (rvmrcExists) {
98 100
           return exists(rvm = _this.configuration.rvmPath, function(rvmExists) {
99  
-            var before;
  101
+            var before, libexecPath;
100 102
             if (rvmExists) {
101  
-              before = "source '" + rvm + "' > /dev/null";
  103
+              libexecPath = resolve("" + __dirname + "/../libexec");
  104
+              before = ("'" + libexecPath + "/pow_rvm_deprecation_notice' '" + [_this.firstHost] + "'\nsource '" + rvm + "' > /dev/null").trim();
102 105
               return sourceScriptEnv(script, env, {
103 106
                 before: before
104 107
               }, callback);
@@ -250,6 +253,21 @@
250 253
       });
251 254
     };
252 255
 
  256
+    RackApplication.prototype.writeRvmBoilerplate = function() {
  257
+      var line1, line2, powrc;
  258
+      powrc = join(this.root, ".powrc");
  259
+      line1 = '[ ! -f "$rvm_path/scripts/rvm" ] || source "$rvm_path/scripts/rvm"';
  260
+      line2 = '[ ! -f ".rvmrc" ] || source ".rvmrc"';
  261
+      return fs.readFile(powrc, "utf8", function(err, contents) {
  262
+        var lines, _ref2;
  263
+        lines = (_ref2 = contents != null ? contents.split("\n") : void 0) != null ? _ref2 : [];
  264
+        if (!(__indexOf.call(lines, line1) >= 0 && __indexOf.call(lines, line2) >= 0)) {
  265
+          lines.push(line1, line2, "");
  266
+          return fs.writeFile(powrc, lines.join("\n"));
  267
+        }
  268
+      });
  269
+    };
  270
+
253 271
     return RackApplication;
254 272
 
255 273
   })();
2  lib/templates/http_server/error_starting_application.html.js
@@ -55,7 +55,7 @@ module.exports = function(__obj) {
55 55
             __out.push(__sanitize(_this.rest.join("\n")));
56 56
             __out.push('</div>');
57 57
           }
58  
-          return __out.push('</pre>\n    <p>(If your app uses Bundler, check to make sure you have the <a href="http://gembundler.com/">latest version</a>, then run <code>bundle install</code>. If you&rsquo;re using rvm, make sure you have the <a href="https://rvm.beginrescueend.com/rvm/upgrading/">latest version</a> installed and your app is using the right gemset.)</p>\n  </section>\n');
  58
+          return __out.push('</pre>\n  </section>\n');
59 59
         });
60 60
       }));
61 61
     
48  lib/templates/http_server/rvm_deprecation_notice.html.js
... ...
@@ -0,0 +1,48 @@
  1
+module.exports = function(__obj) {
  2
+  if (!__obj) __obj = {};
  3
+  var __out = [], __capture = function(callback) {
  4
+    var out = __out, result;
  5
+    __out = [];
  6
+    callback.call(this);
  7
+    result = __out.join('');
  8
+    __out = out;
  9
+    return __safe(result);
  10
+  }, __sanitize = function(value) {
  11
+    if (value && value.ecoSafe) {
  12
+      return value;
  13
+    } else if (typeof value !== 'undefined' && value != null) {
  14
+      return __escape(value);
  15
+    } else {
  16
+      return '';
  17
+    }
  18
+  }, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
  19
+  __safe = __obj.safe = function(value) {
  20
+    if (value && value.ecoSafe) {
  21
+      return value;
  22
+    } else {
  23
+      if (!(typeof value !== 'undefined' && value != null)) value = '';
  24
+      var result = new String(value);
  25
+      result.ecoSafe = true;
  26
+      return result;
  27
+    }
  28
+  };
  29
+  if (!__escape) {
  30
+    __escape = __obj.escape = function(value) {
  31
+      return ('' + value)
  32
+        .replace(/&/g, '&amp;')
  33
+        .replace(/</g, '&lt;')
  34
+        .replace(/>/g, '&gt;')
  35
+        .replace(/"/g, '&quot;');
  36
+    };
  37
+  }
  38
+  (function() {
  39
+    (function() {
  40
+    
  41
+      __out.push('<!doctype html>\n<html>\n  <head>\n    <title>Pow: Automatic RVM support is deprecated</title>\n    <style type="text/css">\n      html {\n        background: #fff;\n        margin: 0;\n        padding: 0;\n      }\n\n      body {\n        margin: 30px auto;\n        padding: 15px 35px;\n        width: 520px;\n        font-family: Lucida Grande;\n      }\n\n      h1 {\n        font-family: Helvetica;\n        font-size: 24px;\n      }\n\n      a {\n        color: #03f;\n      }\n\n      p {\n        width: 460px;\n        font-size: 13px;\n      }\n\n      .important {\n        background: #ff3;\n      }\n\n      code {\n        font-family: Menlo;\n        font-size: 12px;\n      }\n    </style>\n    <script type="text/javascript">\n      function addToPowrc(link) {\n        perform(link, "add_to_powrc",\n          "This code has been added to your application&rsquo;s <code>.powrc</code> file."\n        )\n      }\n\n      function disable(link) {\n        perform(link, "disable",\n          "You will not see this deprecation notice for other applications. " +\n          \'<a href="#" onclick="enable(this); return false">Undo</a>\'\n        )\n      }\n\n      function enable(link) {\n        perform(link, "enable")\n      }\n\n      function perform(link, action, successHTML) {\n        if (link.className == "busy") return\n        link.className = "busy"\n\n        xhr = new XMLHttpRequest()\n        xhr.open("POST", "/__pow__/rvm_deprecation/" + action, true)\n        xhr.onreadystatechange = function() {\n          if (xhr.readyState != 4) return\n          link.className = ""\n\n          if (xhr.status == 200) {\n            var p = link.parentNode\n            var previousInnerHTML = p.previousInnerHTML\n            p.previousInnerHTML = p.innerHTML\n            p.innerHTML = successHTML || previousInnerHTML\n          }\n        }\n\n        xhr.send()\n      }\n    </script>\n  </head>\n  <body>\n    <h1>Automatic RVM support is deprecated</h1>\n\n    <p>We&rsquo;re showing you this notice because you just accessed\n    an application with a per-project <code>.rvmrc</code> file.</p>\n\n    <p><span class="important">Support for automatically loading\n    per-project <code>.rvmrc</code> files in Pow is deprecated and\n    will be removed in the next major release.</span></p>\n\n    <p>Ensure your application continues to work with future releases\n    of Pow by adding the following code to the\n    application&rsquo;s <code>.powrc</code> file:</p>\n\n    <pre><code>[ ! -f "$rvm_path/scripts/rvm" ] || source "$rvm_path/scripts/rvm"\n[ ! -f ".rvmrc" ] || source ".rvmrc"</code></pre>\n\n    <p><a href="#" onclick="addToPowrc(this); return false">Add this\n    code to <code>.powrc</code> for me</a></p>\n\n    <p>We won&rsquo;t notify you again for this project.</p>\n\n    <p><a href="#" onclick="disable(this); return false">Don&rsquo;t\n    notify me about deprecations for any other applications,\n    either</a></p>\n\n    <p>Thanks for using Pow.</p>\n  </body>\n</html>\n');
  42
+    
  43
+    }).call(this);
  44
+    
  45
+  }).call(__obj);
  46
+  __obj.safe = __objSafe, __obj.escape = __escape;
  47
+  return __out.join('');
  48
+}
24  libexec/pow_rvm_deprecation_notice
... ...
@@ -0,0 +1,24 @@
  1
+#!/usr/bin/env bash
  2
+
  3
+HOST="$1"
  4
+RVMRC_PATH="$PWD/.rvmrc"
  5
+SUPPORT_ROOT="$HOME/Library/Application Support/Pow"
  6
+DISABLED_FILE="$SUPPORT_ROOT/.disableRvmDeprecationNotices"
  7
+NOTIFIED_FILE="$SUPPORT_ROOT/.rvmDeprecationNotices"
  8
+
  9
+if [ -z "$HOST" ] || [ -f "$DISABLED_FILE" ]; then
  10
+  exit
  11
+fi
  12
+
  13
+if [ -f "$NOTIFIED_FILE" ] && grep -xF "$RVMRC_PATH" "$NOTIFIED_FILE"; then
  14
+  exit
  15
+fi
  16
+
  17
+for file in .powrc .powenv; do
  18
+  if [ -f "$file" ] && egrep 'source.+rvmrc' "$file"; then
  19
+    exit
  20
+  fi
  21
+done
  22
+
  23
+echo "$RVMRC_PATH" >> "$NOTIFIED_FILE"
  24
+open "http://$HOST/__pow__/rvm_deprecation"
18  src/configuration.coffee
@@ -108,17 +108,20 @@ module.exports = class Configuration
108 108
     # Support *.xip.io top-level domains.
109 109
     @allDomains.push /\d+\.\d+\.\d+\.\d+\.xip\.io$/, /[0-9a-z]{1,7}\.xip\.io$/
110 110
 
  111
+    # Runtime support files live in `~/Library/Application Support/Pow`.
  112
+    @supportRoot = libraryPath "Application Support", "Pow"
  113
+
111 114
     # `POW_HOST_ROOT`: path to the directory containing symlinks to
112 115
     # applications that will be served by Pow. Defaults to
113 116
     # `~/Library/Application Support/Pow/Hosts`.
114  
-    @hostRoot   = env.POW_HOST_ROOT   ? libraryPath "Application Support", "Pow", "Hosts"
  117
+    @hostRoot   = env.POW_HOST_ROOT   ? path.join @supportRoot, "Hosts"
115 118
 
116 119
     # `POW_LOG_ROOT`: path to the directory that Pow will use to store
117 120
     # its log files. Defaults to `~/Library/Logs/Pow`.
118 121
     @logRoot    = env.POW_LOG_ROOT    ? libraryPath "Logs", "Pow"
119 122
 
120  
-    # `POW_RVM_PATH`: path to the rvm initialization script. Defaults
121  
-    # to `~/.rvm/scripts/rvm`.
  123
+    # `POW_RVM_PATH` (**deprecated**): path to the rvm initialization
  124
+    # script. Defaults to `~/.rvm/scripts/rvm`.
122 125
     @rvmPath    = env.POW_RVM_PATH    ? path.join process.env.HOME, ".rvm/scripts/rvm"
123 126
 
124 127
     # ---
@@ -139,6 +142,15 @@ module.exports = class Configuration
139 142
   getLogger: (name) ->
140 143
     @loggers[name] ||= new Logger path.join @logRoot, name + ".log"
141 144
 
  145
+  # Globally disable or enable RVM deprecation notices by touching or
  146
+  # removing the `~/Library/Application
  147
+  # Support/Pow/.disableRvmDeprecationNotices` file.
  148
+  disableRvmDeprecationNotices: ->
  149
+    fs.writeFile path.join(@supportRoot, ".disableRvmDeprecationNotices"), ""
  150
+
  151
+  enableRvmDeprecationNotices: ->
  152
+    fs.unlink path.join @supportRoot, ".disableRvmDeprecationNotices"
  153
+
142 154
   # Search `hostRoot` for files, symlinks or directories matching the
143 155
   # domain specified by `host`. If a match is found, the matching domain
144 156
   # name and its configuration are passed as second and third arguments
30  src/http_server.coffee
@@ -56,6 +56,7 @@ module.exports = class HttpServer extends connect.HTTPServer
56 56
       o @handleStaticRequest
57 57
       o @findRackApplication
58 58
       o @handleProxyRequest
  59
+      o @handleRvmDeprecationRequest
59 60
       o @handleApplicationRequest
60 61
       x @handleErrorStartingApplication
61 62
       o @handleFaviconRequest
@@ -170,7 +171,7 @@ module.exports = class HttpServer extends connect.HTTPServer
170 171
     exists join(root, "config.ru"), (rackConfigExists) =>
171 172
       if rackConfigExists
172 173
         req.pow.application = @rackApplications[root] ?=
173  
-          new RackApplication @configuration, root
  174
+          new RackApplication @configuration, root, req.pow.host
174 175
 
175 176
       # If `config.ru` isn't present but there's an existing
176 177
       # `RackApplication` for the root, terminate the application and
@@ -212,6 +213,33 @@ module.exports = class HttpServer extends connect.HTTPServer
212 213
 
213 214
     req.pow.resume()
214 215
 
  216
+  # Handle requests for the mini-app that serves RVM deprecation
  217
+  # notices. Manually requesting `/__pow__/rvm_deprecation` on any
  218
+  # Rack app will show the notice. The notice is automatically
  219
+  # displayed in a separate browser window by `RackApplication` the
  220
+  # first time you load an app with an `.rvmrc` file.
  221
+  handleRvmDeprecationRequest: (req, res, next) =>
  222
+    return next() unless application = req.pow.application
  223
+
  224
+    if match = req.url.match /^\/__pow__\/rvm_deprecation(.*)/
  225
+      action = match[1]
  226
+      return next() unless action is "" or req.method is "POST"
  227
+
  228
+      switch action
  229
+        when ""
  230
+          true
  231
+        when "/add_to_powrc"
  232
+          application.writeRvmBoilerplate()
  233
+        when "/enable"
  234
+          @configuration.enableRvmDeprecationNotices()
  235
+        when "/disable"
  236
+          @configuration.disableRvmDeprecationNotices()
  237
+        else
  238
+          return next()
  239
+      renderResponse res, 200, "rvm_deprecation_notice"
  240
+    else
  241
+      next()
  242
+
215 243
   # If the request object is annotated with an application, pass the
216 244
   # request off to the application's `handle` method.
217 245
   handleApplicationRequest: (req, res, next) ->
27  src/rack_application.coffee
@@ -29,13 +29,13 @@ fs    = require "fs"
29 29
 nack  = require "nack"
30 30
 
31 31
 {bufferLines, pause, sourceScriptEnv} = require "./util"
32  
-{join, exists, basename} = require "path"
  32
+{join, exists, basename, resolve} = require "path"
33 33
 
34 34
 module.exports = class RackApplication
35 35
   # Create a `RackApplication` for the given configuration and
36 36
   # root path. The application begins life in the uninitialized
37 37
   # state.
38  
-  constructor: (@configuration, @root) ->
  38
+  constructor: (@configuration, @root, @firstHost) ->
39 39
     @logger = @configuration.getLogger join "apps", basename @root
40 40
     @readyCallbacks = []
41 41
     @quitCallbacks  = []
@@ -105,12 +105,18 @@ module.exports = class RackApplication
105 105
   # source `.rvmrc`, and invoke `callback` with the resulting
106 106
   # environment variables. If `.rvmrc` is present but rvm is not
107 107
   # installed, invoke `callback` without sourcing `.rvmrc`.
  108
+  # Before loading rvm, Pow invokes a helper script that shows a
  109
+  # deprecation notice if it has not yet been displayed.
108 110
   loadRvmEnvironment: (env, callback) ->
109 111
     exists script = join(@root, ".rvmrc"), (rvmrcExists) =>
110 112
       if rvmrcExists
111  
-        exists rvm = @configuration.rvmPath, (rvmExists) ->
  113
+        exists rvm = @configuration.rvmPath, (rvmExists) =>
112 114
           if rvmExists
113  
-            before = "source '#{rvm}' > /dev/null"
  115
+            libexecPath = resolve "#{__dirname}/../libexec"
  116
+            before = """
  117
+              '#{libexecPath}/pow_rvm_deprecation_notice' '#{[@firstHost]}'
  118
+              source '#{rvm}' > /dev/null
  119
+            """.trim()
114 120
             sourceScriptEnv script, env, {before}, callback
115 121
           else
116 122
             callback null, env
@@ -230,3 +236,16 @@ module.exports = class RackApplication
230 236
         @restart callback
231 237
       else
232 238
         callback()
  239
+
  240
+  # Append RVM autoload boilerplate to the application's `.powrc`
  241
+  # file. This is called by the RVM deprecation notice mini-app.
  242
+  writeRvmBoilerplate: ->
  243
+    powrc = join @root, ".powrc"
  244
+    line1 = '[ ! -f "$rvm_path/scripts/rvm" ] || source "$rvm_path/scripts/rvm"'
  245
+    line2 = '[ ! -f ".rvmrc" ] || source ".rvmrc"'
  246
+
  247
+    fs.readFile powrc, "utf8", (err, contents) ->
  248
+      lines = contents?.split("\n") ? []
  249
+      unless line1 in lines and line2 in lines
  250
+        lines.push line1, line2, ""
  251
+        fs.writeFile powrc, lines.join("\n")
1  src/templates/http_server/error_starting_application.html.eco
@@ -5,6 +5,5 @@
5 5
     <pre class="breakout small_text"><strong><%= @err %></strong>
6 6
 <%= @stack.join "\n" %><% if @rest: %>
7 7
 <a href="#" onclick="this.style.display='none',this.nextSibling.style.display='';return false">Show <%= @rest.length %> more lines</a><div style="display: none"><%= @rest.join "\n" %></div><% end %></pre>
8  
-    <p>(If your app uses Bundler, check to make sure you have the <a href="http://gembundler.com/">latest version</a>, then run <code>bundle install</code>. If you&rsquo;re using rvm, make sure you have the <a href="https://rvm.beginrescueend.com/rvm/upgrading/">latest version</a> installed and your app is using the right gemset.)</p>
9 8
   </section>
10 9
 <% end %>
110  src/templates/http_server/rvm_deprecation_notice.html.eco
... ...
@@ -0,0 +1,110 @@
  1
+<!doctype html>
  2
+<html>
  3
+  <head>
  4
+    <title>Pow: Automatic RVM support is deprecated</title>
  5
+    <style type="text/css">
  6
+      html {
  7
+        background: #fff;
  8
+        margin: 0;
  9
+        padding: 0;
  10
+      }
  11
+
  12
+      body {
  13
+        margin: 30px auto;
  14
+        padding: 15px 35px;
  15
+        width: 520px;
  16
+        font-family: Lucida Grande;
  17
+      }
  18
+
  19
+      h1 {
  20
+        font-family: Helvetica;
  21
+        font-size: 24px;
  22
+      }
  23
+
  24
+      a {
  25
+        color: #03f;
  26
+      }
  27
+
  28
+      p {
  29
+        width: 460px;
  30
+        font-size: 13px;
  31
+      }
  32
+
  33
+      .important {
  34
+        background: #ff3;
  35
+      }
  36
+
  37
+      code {
  38
+        font-family: Menlo;
  39
+        font-size: 12px;
  40
+      }
  41
+    </style>
  42
+    <script type="text/javascript">
  43
+      function addToPowrc(link) {
  44
+        perform(link, "add_to_powrc",
  45
+          "This code has been added to your application&rsquo;s <code>.powrc</code> file."
  46
+        )
  47
+      }
  48
+
  49
+      function disable(link) {
  50
+        perform(link, "disable",
  51
+          "You will not see this deprecation notice for other applications. " +
  52
+          '<a href="#" onclick="enable(this); return false">Undo</a>'
  53
+        )
  54
+      }
  55
+
  56
+      function enable(link) {
  57
+        perform(link, "enable")
  58
+      }
  59
+
  60
+      function perform(link, action, successHTML) {
  61
+        if (link.className == "busy") return
  62
+        link.className = "busy"
  63
+
  64
+        xhr = new XMLHttpRequest()
  65
+        xhr.open("POST", "/__pow__/rvm_deprecation/" + action, true)
  66
+        xhr.onreadystatechange = function() {
  67
+          if (xhr.readyState != 4) return
  68
+          link.className = ""
  69
+
  70
+          if (xhr.status == 200) {
  71
+            var p = link.parentNode
  72
+            var previousInnerHTML = p.previousInnerHTML
  73
+            p.previousInnerHTML = p.innerHTML
  74
+            p.innerHTML = successHTML || previousInnerHTML
  75
+          }
  76
+        }
  77
+
  78
+        xhr.send()
  79
+      }
  80
+    </script>
  81
+  </head>
  82
+  <body>
  83
+    <h1>Automatic RVM support is deprecated</h1>
  84
+
  85
+    <p>We&rsquo;re showing you this notice because you just accessed
  86
+    an application with a per-project <code>.rvmrc</code> file.</p>
  87
+
  88
+    <p><span class="important">Support for automatically loading
  89
+    per-project <code>.rvmrc</code> files in Pow is deprecated and
  90
+    will be removed in the next major release.</span></p>
  91
+
  92
+    <p>Ensure your application continues to work with future releases
  93
+    of Pow by adding the following code to the
  94
+    application&rsquo;s <code>.powrc</code> file:</p>
  95
+
  96
+    <pre><code>[ ! -f "$rvm_path/scripts/rvm" ] || source "$rvm_path/scripts/rvm"
  97
+[ ! -f ".rvmrc" ] || source ".rvmrc"</code></pre>
  98
+
  99
+    <p><a href="#" onclick="addToPowrc(this); return false">Add this
  100
+    code to <code>.powrc</code> for me</a></p>
  101
+
  102
+    <p>We won&rsquo;t notify you again for this project.</p>
  103
+
  104
+    <p><a href="#" onclick="disable(this); return false">Don&rsquo;t
  105
+    notify me about deprecations for any other applications,
  106
+    either</a></p>
  107
+
  108
+    <p>Thanks for using Pow.</p>
  109
+  </body>
  110
+</html>

0 notes on commit 3e62bab

Please sign in to comment.
Something went wrong with that request. Please try again.