Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #248 from 37signals/configuration-env

Configuration environment refactoring
  • Loading branch information...
commit 06094c67159dae293ebb4013f6ab41e31343d691 2 parents 3eb67db + d922567
@sstephenson sstephenson authored
View
2  bin/node
@@ -1,2 +0,0 @@
-#!/bin/sh
-exec node $@
View
8 bin/pow
@@ -1,6 +1,2 @@
-#!/usr/bin/ruby
-require 'pathname'
-bin = Pathname.new(__FILE__).realpath
-root = bin.dirname.parent
-ENV['POW_BIN'] = bin.to_s
-exec root.join("bin/node").to_s, root.join("lib/command.js").to_s, *ARGV
+#!/usr/bin/env node
+require("../lib/command.js");
View
75 lib/configuration.js
@@ -1,5 +1,5 @@
(function() {
- var Configuration, Logger, async, compilePattern, env, fs, getFilenamesForHost, libraryPath, mkdirp, path, rstat, sourceScriptEnv;
+ var Configuration, Logger, async, compilePattern, fs, getFilenamesForHost, getUserEnv, libraryPath, mkdirp, path, rstat, sourceScriptEnv;
var __slice = Array.prototype.slice;
fs = require("fs");
@@ -14,62 +14,69 @@
sourceScriptEnv = require("./util").sourceScriptEnv;
- env = process.env;
+ getUserEnv = require("./util").getUserEnv;
module.exports = Configuration = (function() {
- Configuration.userConfigurationPath = path.join(env.HOME, ".powconfig");
+ Configuration.userConfigurationPath = path.join(process.env.HOME, ".powconfig");
Configuration.loadUserConfigurationEnvironment = function(callback) {
- var p;
- return path.exists(p = this.userConfigurationPath, function(exists) {
- if (exists) {
- return sourceScriptEnv(p, {}, callback);
+ var _this = this;
+ return getUserEnv(function(err, env) {
+ var p;
+ if (err) {
+ return callback(err);
} else {
- return callback(null, {});
+ return path.exists(p = _this.userConfigurationPath, function(exists) {
+ if (exists) {
+ return sourceScriptEnv(p, env, callback);
+ } else {
+ return callback(null, env);
+ }
+ });
}
});
};
Configuration.getUserConfiguration = function(callback) {
return this.loadUserConfigurationEnvironment(function(err, env) {
- var key, value;
if (err) {
return callback(err);
} else {
- for (key in env) {
- value = env[key];
- process.env[key] = value;
- }
- return callback(null, new Configuration);
+ return callback(null, new Configuration(env));
}
});
};
Configuration.optionNames = ["bin", "dstPort", "httpPort", "dnsPort", "timeout", "workers", "domains", "extDomains", "hostRoot", "logRoot", "rvmPath"];
- function Configuration(options) {
- var _base, _base2, _ref, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _ref19, _ref2, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
- if (options == null) options = {};
- this.bin = (_ref = (_ref2 = options.bin) != null ? _ref2 : env.POW_BIN) != null ? _ref : path.join(__dirname, "../bin/pow");
- this.dstPort = (_ref3 = (_ref4 = options.dstPort) != null ? _ref4 : env.POW_DST_PORT) != null ? _ref3 : 80;
- this.httpPort = (_ref5 = (_ref6 = options.httpPort) != null ? _ref6 : env.POW_HTTP_PORT) != null ? _ref5 : 20559;
- this.dnsPort = (_ref7 = (_ref8 = options.dnsPort) != null ? _ref8 : env.POW_DNS_PORT) != null ? _ref7 : 20560;
- this.timeout = (_ref9 = (_ref10 = options.timeout) != null ? _ref10 : env.POW_TIMEOUT) != null ? _ref9 : 15 * 60;
- this.workers = (_ref11 = (_ref12 = options.workers) != null ? _ref12 : env.POW_WORKERS) != null ? _ref11 : 2;
- this.domains = (_ref13 = (_ref14 = (_ref15 = options.domains) != null ? _ref15 : env.POW_DOMAINS) != null ? _ref14 : env.POW_DOMAIN) != null ? _ref13 : "dev";
- this.extDomains = (_ref16 = (_ref17 = options.extDomains) != null ? _ref17 : env.POW_EXT_DOMAINS) != null ? _ref16 : [];
- this.domains = (_ref18 = typeof (_base = this.domains).split === "function" ? _base.split(",") : void 0) != null ? _ref18 : this.domains;
- this.extDomains = (_ref19 = typeof (_base2 = this.extDomains).split === "function" ? _base2.split(",") : void 0) != null ? _ref19 : this.extDomains;
- this.allDomains = this.domains.concat(this.extDomains);
- this.hostRoot = (_ref20 = (_ref21 = options.hostRoot) != null ? _ref21 : env.POW_HOST_ROOT) != null ? _ref20 : libraryPath("Application Support", "Pow", "Hosts");
- this.logRoot = (_ref22 = (_ref23 = options.logRoot) != null ? _ref23 : env.POW_LOG_ROOT) != null ? _ref22 : libraryPath("Logs", "Pow");
- this.rvmPath = (_ref24 = (_ref25 = options.rvmPath) != null ? _ref25 : env.POW_RVM_PATH) != null ? _ref24 : path.join(env.HOME, ".rvm/scripts/rvm");
+ function Configuration(env) {
+ if (env == null) env = process.env;
this.loggers = {};
- this.dnsDomainPattern = compilePattern(this.domains);
- this.httpDomainPattern = compilePattern(this.allDomains);
+ this.initialize(env);
}
+ Configuration.prototype.initialize = function(env) {
+ var _base, _base2, _ref, _ref10, _ref11, _ref12, _ref13, _ref14, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
+ this.env = env;
+ this.bin = (_ref = env.POW_BIN) != null ? _ref : path.join(__dirname, "../bin/pow");
+ this.dstPort = (_ref2 = env.POW_DST_PORT) != null ? _ref2 : 80;
+ this.httpPort = (_ref3 = env.POW_HTTP_PORT) != null ? _ref3 : 20559;
+ this.dnsPort = (_ref4 = env.POW_DNS_PORT) != null ? _ref4 : 20560;
+ this.timeout = (_ref5 = env.POW_TIMEOUT) != null ? _ref5 : 15 * 60;
+ this.workers = (_ref6 = env.POW_WORKERS) != null ? _ref6 : 2;
+ this.domains = (_ref7 = (_ref8 = env.POW_DOMAINS) != null ? _ref8 : env.POW_DOMAIN) != null ? _ref7 : "dev";
+ this.extDomains = (_ref9 = env.POW_EXT_DOMAINS) != null ? _ref9 : [];
+ this.domains = (_ref10 = typeof (_base = this.domains).split === "function" ? _base.split(",") : void 0) != null ? _ref10 : this.domains;
+ this.extDomains = (_ref11 = typeof (_base2 = this.extDomains).split === "function" ? _base2.split(",") : void 0) != null ? _ref11 : this.extDomains;
+ this.allDomains = this.domains.concat(this.extDomains);
+ this.hostRoot = (_ref12 = env.POW_HOST_ROOT) != null ? _ref12 : libraryPath("Application Support", "Pow", "Hosts");
+ this.logRoot = (_ref13 = env.POW_LOG_ROOT) != null ? _ref13 : libraryPath("Logs", "Pow");
+ this.rvmPath = (_ref14 = env.POW_RVM_PATH) != null ? _ref14 : path.join(process.env.HOME, ".rvm/scripts/rvm");
+ this.dnsDomainPattern = compilePattern(this.domains);
+ return this.httpDomainPattern = compilePattern(this.allDomains);
+ };
+
Configuration.prototype.toJSON = function() {
var key, result, _i, _len, _ref;
result = {};
@@ -159,7 +166,7 @@
libraryPath = function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- return path.join.apply(path, [env.HOME, "Library"].concat(__slice.call(args)));
+ return path.join.apply(path, [process.env.HOME, "Library"].concat(__slice.call(args)));
};
getFilenamesForHost = function(host, domain) {
View
3  lib/http_server.js
@@ -115,6 +115,9 @@
case "/config.json":
res.writeHead(200);
return res.end(JSON.stringify(this.configuration));
+ case "/env.json":
+ res.writeHead(200);
+ return res.end(JSON.stringify(this.configuration.env));
case "/status.json":
res.writeHead(200);
return res.end(JSON.stringify(this));
View
2  lib/rack_application.js
@@ -112,7 +112,7 @@
RackApplication.prototype.loadEnvironment = function(callback) {
var _this = this;
return this.queryRestartFile(function() {
- return _this.loadScriptEnvironment(null, function(err, env) {
+ return _this.loadScriptEnvironment(_this.configuration.env, function(err, env) {
if (err) {
return callback(err);
} else {
View
8 lib/templates/installer/cx.pow.powd.plist.js
@@ -19,11 +19,15 @@ module.exports = function(__obj) {
return _safe(result);
};
- _print(_safe('<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n\t<key>Label</key>\n\t<string>cx.pow.powd</string>\n\t<key>ProgramArguments</key>\n\t<array>\n\t\t<string>sh</string>\n\t\t<string>-i</string>\n\t\t<string>-c</string>\n\t\t<string>$SHELL --login -c "\''));
+ _print(_safe('<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n <key>Label</key>\n <string>cx.pow.powd</string>\n <key>ProgramArguments</key>\n <array>\n <string>'));
+
+ _print(process.execPath);
+
+ _print(_safe('</string>\n <string>'));
_print(this.bin);
- _print(_safe('\'"</string>\n\t</array>\n\t<key>KeepAlive</key>\n\t<true/>\n\t<key>RunAtLoad</key>\n\t<true/>\n</dict>\n</plist>\n'));
+ _print(_safe('</string>\n </array>\n <key>KeepAlive</key>\n <true/>\n <key>RunAtLoad</key>\n <true/>\n</dict>\n</plist>\n'));
return __out.join('');
}).call((function() {
View
60 lib/util.js
@@ -1,5 +1,5 @@
(function() {
- var LineBuffer, Stream, async, exec, fs, path, spawn;
+ var LineBuffer, Stream, async, exec, fs, getUserShell, parseEnv, path, spawn;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }, __slice = Array.prototype.slice;
fs = require("fs");
@@ -159,7 +159,7 @@
} else {
if (options == null) options = {};
}
- command = "" + options.before + ";\nsource '" + script + "' > /dev/null;\n'" + process.execPath + "' -e 'console.log(JSON.stringify(process.env)); \"\"'";
+ command = "" + options.before + ";\nsource '" + script + "' > /dev/null;\nenv";
return exec(command, {
cwd: path.dirname(script),
env: env
@@ -170,12 +170,60 @@
err.stderr = stderr;
callback(err);
}
- try {
- return callback(null, JSON.parse(stdout));
- } catch (exception) {
- return callback(exception);
+ return callback(null, parseEnv(stdout));
+ });
+ };
+
+ exports.getUserEnv = function(callback) {
+ var user;
+ user = process.env.LOGNAME;
+ return getUserShell(function(shell) {
+ return exec("login -qf " + user + " " + shell + " -l -c env", function(err, stdout, stderr) {
+ if (err) {
+ return exec("login -qf " + user + " " + shell + " -c env", function(err, stdout, stderr) {
+ if (err) {
+ return callback(err);
+ } else {
+ return callback(null, parseEnv(stdout));
+ }
+ });
+ } else {
+ return callback(null, parseEnv(stdout));
+ }
+ });
+ });
+ };
+
+ getUserShell = function(callback) {
+ var command;
+ command = "dscl . -read '/Users/" + process.env.LOGNAME + "' UserShell";
+ return exec(command, function(err, stdout, stderr) {
+ var match, matches, shell;
+ if (err) {
+ return callback(process.env.SHELL);
+ } else {
+ if (matches = stdout.trim().match(/^UserShell: (.+)$/)) {
+ match = matches[0], shell = matches[1];
+ return callback(shell);
+ } else {
+ return callback(process.env.SHELL);
+ }
}
});
};
+ parseEnv = function(stdout) {
+ var env, line, match, matches, name, value, _i, _len, _ref;
+ env = {};
+ _ref = stdout.split("\n");
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ line = _ref[_i];
+ if (matches = line.match(/([^=]+)=(.+)/)) {
+ match = matches[0], name = matches[1], value = matches[2];
+ env[name] = value;
+ }
+ }
+ return env;
+ };
+
}).call(this);
View
100 src/configuration.coffee
@@ -9,8 +9,7 @@ async = require "async"
Logger = require "./logger"
{mkdirp} = require "./util"
{sourceScriptEnv} = require "./util"
-
-{env} = process
+{getUserEnv} = require "./util"
module.exports = class Configuration
# The user configuration file, `~/.powconfig`, is evaluated on
@@ -22,18 +21,22 @@ module.exports = class Configuration
#
# See the `Configuration` constructor for a complete list of
# environment options.
- @userConfigurationPath: path.join env.HOME, ".powconfig"
+ @userConfigurationPath: path.join process.env.HOME, ".powconfig"
# Evaluates the user configuration script and calls the `callback`
# with the environment variables if the config file exists. Any
# script errors are passed along in the first argument. (No error
# occurs if the file does not exist.)
@loadUserConfigurationEnvironment: (callback) ->
- path.exists p = @userConfigurationPath, (exists) ->
- if exists
- sourceScriptEnv p, {}, callback
+ getUserEnv (err, env) =>
+ if err
+ callback err
else
- callback null, {}
+ path.exists p = @userConfigurationPath, (exists) ->
+ if exists
+ sourceScriptEnv p, env, callback
+ else
+ callback null, env
# Creates a Configuration object after evaluating the user
# configuration file. Any environment variables in `~/.powconfig`
@@ -44,10 +47,7 @@ module.exports = class Configuration
if err
callback err
else
- for key, value of env
- process.env[key] = value
-
- callback null, new Configuration
+ callback null, new Configuration env
# A list of option names accessible on `Configuration` instances.
@optionNames: [
@@ -55,66 +55,70 @@ module.exports = class Configuration
"domains", "extDomains", "hostRoot", "logRoot", "rvmPath"
]
- # Pass in any options you'd like to override when creating a
- # `Configuration` instance. Valid options and their defaults:
- constructor: (options = {}) ->
- # `bin`: the path to the `pow` binary. (This should be correctly
- # configured for you.)
- @bin = options.bin ? env.POW_BIN ? path.join __dirname, "../bin/pow"
+ # Pass in any environment variables you'd like to override when
+ # creating a `Configuration` instance.
+ constructor: (env = process.env) ->
+ @loggers = {}
+ @initialize env
+
+ # Valid environment variables and their defaults:
+ initialize: (@env) ->
+ # `POW_BIN`: the path to the `pow` binary. (This should be
+ # correctly configured for you.)
+ @bin = env.POW_BIN ? path.join __dirname, "../bin/pow"
- # `dstPort`: the public port Pow expects to be forwarded or
+ # `POW_DST_PORT`: the public port Pow expects to be forwarded or
# otherwise proxied for incoming HTTP requests. Defaults to `80`.
- @dstPort = options.dstPort ? env.POW_DST_PORT ? 80
+ @dstPort = env.POW_DST_PORT ? 80
- # `httpPort`: the TCP port Pow opens for accepting incoming HTTP
- # requests. Defaults to `20559`.
- @httpPort = options.httpPort ? env.POW_HTTP_PORT ? 20559
+ # `POW_HTTP_PORT`: the TCP port Pow opens for accepting incoming
+ # HTTP requests. Defaults to `20559`.
+ @httpPort = env.POW_HTTP_PORT ? 20559
- # `dnsPort`: the UDP port Pow listens on for incoming DNS
+ # `POW_DNS_PORT`: the UDP port Pow listens on for incoming DNS
# queries. Defaults to `20560`.
- @dnsPort = options.dnsPort ? env.POW_DNS_PORT ? 20560
+ @dnsPort = env.POW_DNS_PORT ? 20560
- # `timeout`: how long (in seconds) to leave inactive Rack
+ # `POW_TIMEOUT`: how long (in seconds) to leave inactive Rack
# applications running before they're killed. Defaults to 15
# minutes (900 seconds).
- @timeout = options.timeout ? env.POW_TIMEOUT ? 15 * 60
+ @timeout = env.POW_TIMEOUT ? 15 * 60
- # `workers`: the maximum number of worker processes to spawn for
- # any given application. Defaults to `2`.
- @workers = options.workers ? env.POW_WORKERS ? 2
+ # `POW_WORKERS`: the maximum number of worker processes to spawn
+ # for any given application. Defaults to `2`.
+ @workers = env.POW_WORKERS ? 2
- # `domains`: the top-level domains for which Pow will respond to
- # DNS `A` queries with `127.0.0.1`. Defaults to `dev`. If you
- # configure this in your `~/.powconfig` you will need to re-run
+ # `POW_DOMAINS`: the top-level domains for which Pow will respond
+ # to DNS `A` queries with `127.0.0.1`. Defaults to `dev`. If you
+ # configure this in your `~/.powconfig` you will need to re-run
# `sudo pow --install-system` to make `/etc/resolver` aware of
- # the new TLD's.
- @domains = options.domains ? env.POW_DOMAINS ? env.POW_DOMAIN ? "dev"
+ # the new TLDs.
+ @domains = env.POW_DOMAINS ? env.POW_DOMAIN ? "dev"
- # `extDomains`: additional top-level domains for which Pow will
- # serve HTTP requests (but not DNS requests -- hence the "ext").
- @extDomains = options.extDomains ? env.POW_EXT_DOMAINS ? []
+ # `POW_EXT_DOMAINS`: additional top-level domains for which Pow
+ # will serve HTTP requests (but not DNS requests -- hence the
+ # "ext").
+ @extDomains = env.POW_EXT_DOMAINS ? []
# Allow for comma-separated domain lists, e.g. `POW_DOMAINS=dev,test`
@domains = @domains.split?(",") ? @domains
@extDomains = @extDomains.split?(",") ? @extDomains
@allDomains = @domains.concat @extDomains
- # `hostRoot`: path to the directory containing symlinks to
+ # `POW_HOST_ROOT`: path to the directory containing symlinks to
# applications that will be served by Pow. Defaults to
# `~/Library/Application Support/Pow/Hosts`.
- @hostRoot = options.hostRoot ? env.POW_HOST_ROOT ? libraryPath "Application Support", "Pow", "Hosts"
+ @hostRoot = env.POW_HOST_ROOT ? libraryPath "Application Support", "Pow", "Hosts"
- # `logRoot`: path to the directory that Pow will use to store its
- # log files. Defaults to `~/Library/Logs/Pow`.
- @logRoot = options.logRoot ? env.POW_LOG_ROOT ? libraryPath "Logs", "Pow"
+ # `POW_LOG_ROOT`: path to the directory that Pow will use to store
+ # its log files. Defaults to `~/Library/Logs/Pow`.
+ @logRoot = env.POW_LOG_ROOT ? libraryPath "Logs", "Pow"
- # `rvmPath`: path to the rvm initialization script. Defaults to
- # `~/.rvm/scripts/rvm`.
- @rvmPath = options.rvmPath ? env.POW_RVM_PATH ? path.join env.HOME, ".rvm/scripts/rvm"
+ # `POW_RVM_PATH`: path to the rvm initialization script. Defaults
+ # to `~/.rvm/scripts/rvm`.
+ @rvmPath = env.POW_RVM_PATH ? path.join process.env.HOME, ".rvm/scripts/rvm"
# ---
- @loggers = {}
-
# Precompile regular expressions for matching domain names to be
# served by the DNS server and hosts to be served by the HTTP
# server.
@@ -199,7 +203,7 @@ module.exports = class Configuration
# Convenience wrapper for constructing paths to subdirectories of
# `~/Library`.
libraryPath = (args...) ->
- path.join env.HOME, "Library", args...
+ path.join process.env.HOME, "Library", args...
# Strip a trailing `domain` from the given `host`, then generate a
# sorted array of possible entry names for finding which application
View
21 src/http_server.coffee
@@ -99,12 +99,18 @@ module.exports = class HttpServer extends connect.HTTPServer
req.pow = {host}
next()
- # Serve requests for status information at `http://pow/config.json`
- # and `http://pow/status.json`. The former returns a JSON
- # representation of the server `Configuration` instance; the latter
- # includes information about the current server version, number of
- # requests handled, and process ID. Third-party utilities may use
- # these endpoints to inspect a running Pow server.
+ # Serve requests for status information at `http://pow/`. The status
+ # endpoints are:
+ #
+ # * `/config.json`: Returns a JSON representation of the server's
+ # `Configuration` instance.
+ # * `/env.json`: Returns the environment variables that all spawned
+ # applications inherit.
+ # * `/status.json`: Returns information about the current server
+ # version, number of requests handled, and process ID.
+ #
+ # Third-party utilities may use these endpoints to inspect a running
+ # Pow server.
handlePowRequest: (req, res, next) =>
return next() unless req.pow.host is "pow"
@@ -112,6 +118,9 @@ module.exports = class HttpServer extends connect.HTTPServer
when "/config.json"
res.writeHead 200
res.end JSON.stringify @configuration
+ when "/env.json"
+ res.writeHead 200
+ res.end JSON.stringify @configuration.env
when "/status.json"
res.writeHead 200
res.end JSON.stringify this
View
2  src/rack_application.coffee
@@ -122,7 +122,7 @@ module.exports = class RackApplication
# `.rvmrc`.
loadEnvironment: (callback) ->
@queryRestartFile =>
- @loadScriptEnvironment null, (err, env) =>
+ @loadScriptEnvironment @configuration.env, (err, env) =>
if err then callback err
else @loadRvmEnvironment env, (err, env) =>
if err then callback err
View
24 src/templates/installer/cx.pow.powd.plist.eco
@@ -2,18 +2,16 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>Label</key>
- <string>cx.pow.powd</string>
- <key>ProgramArguments</key>
- <array>
- <string>sh</string>
- <string>-i</string>
- <string>-c</string>
- <string>$SHELL --login -c "'<%= @bin %>'"</string>
- </array>
- <key>KeepAlive</key>
- <true/>
- <key>RunAtLoad</key>
- <true/>
+ <key>Label</key>
+ <string>cx.pow.powd</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string><%= process.execPath %></string>
+ <string><%= @bin %></string>
+ </array>
+ <key>KeepAlive</key>
+ <true/>
+ <key>RunAtLoad</key>
+ <true/>
</dict>
</plist>
View
52 src/util.coffee
@@ -118,7 +118,7 @@ exports.sourceScriptEnv = (script, env, options, callback) ->
command = """
#{options.before};
source '#{script}' > /dev/null;
- '#{process.execPath}' -e 'console.log(JSON.stringify(process.env)); ""'
+ env
"""
exec command, cwd: path.dirname(script), env: env, (err, stdout, stderr) ->
@@ -128,7 +128,49 @@ exports.sourceScriptEnv = (script, env, options, callback) ->
err.stderr = stderr
callback err
- try
- callback null, JSON.parse stdout
- catch exception
- callback exception
+ callback null, parseEnv stdout
+
+# Get the user's login environment by spawning a login shell and
+# collecting its environment variables via the `env` command. First
+# spawn the user's shell with the `-l` option. If that fails, retry
+# without `-l`; some shells, like tcsh, cannot be started as
+# non-interactive login shells. If that fails, bubble the error up to
+# the callback. Otherwise, parse the output of `env` into a JavaScript
+# object and pass it to the callback.
+exports.getUserEnv = (callback) ->
+ user = process.env.LOGNAME
+ getUserShell (shell) ->
+ exec "login -qf #{user} #{shell} -l -c env", (err, stdout, stderr) ->
+ if err
+ exec "login -qf #{user} #{shell} -c env", (err, stdout, stderr) ->
+ if err
+ callback err
+ else
+ callback null, parseEnv stdout
+ else
+ callback null, parseEnv stdout
+
+# Invoke `dscl(1)` to find out what shell the user prefers. We cannot
+# rely on `process.env.SHELL` because it always seems to be
+# `/bin/bash` when spawned from `launchctl`, regardless of what the
+# user has set.
+getUserShell = (callback) ->
+ command = "dscl . -read '/Users/#{process.env.LOGNAME}' UserShell"
+ exec command, (err, stdout, stderr) ->
+ if err
+ callback process.env.SHELL
+ else
+ if matches = stdout.trim().match /^UserShell: (.+)$/
+ [match, shell] = matches
+ callback shell
+ else
+ callback process.env.SHELL
+
+# Parse the output of the `env` command into a JavaScript object.
+parseEnv = (stdout) ->
+ env = {}
+ for line in stdout.split "\n"
+ if matches = line.match /([^=]+)=(.+)/
+ [match, name, value] = matches
+ env[name] = value
+ env
View
17 test/lib/test_helper.coffee
@@ -5,14 +5,23 @@ http = require "http"
{Configuration} = require "../.."
-exports.createConfiguration = (options = {}) ->
- options.hostRoot ?= fixturePath("tmp")
- options.logRoot ?= fixturePath("tmp/logs")
- new Configuration options
+exports.merge = merge = (objects...) ->
+ result = {}
+ for object in objects
+ for key, value of object
+ result[key] = value
+ result
exports.fixturePath = fixturePath = (path) ->
join fs.realpathSync(join __dirname, ".."), "fixtures", path
+defaultEnvironment =
+ POW_HOST_ROOT: fixturePath "tmp"
+ POW_LOG_ROOT: fixturePath "tmp/logs"
+
+exports.createConfiguration = (env = {}) ->
+ new Configuration merge defaultEnvironment, env
+
exports.prepareFixtures = (callback) ->
rm_rf fixturePath("tmp"), ->
mkdirp fixturePath("tmp"), ->
View
16 test/test_configuration.coffee
@@ -10,7 +10,7 @@ module.exports = testCase
"gatherHostConfigurations returns directories and symlinks to directories": (test) ->
test.expect 1
- configuration = createConfiguration hostRoot: fixturePath("configuration")
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("configuration")
configuration.gatherHostConfigurations (err, hosts) ->
test.same hosts,
"directory": { root: fixturePath("configuration/directory") }
@@ -23,7 +23,7 @@ module.exports = testCase
"gatherHostConfigurations with non-existent host": (test) ->
test.expect 2
- configuration = createConfiguration hostRoot: fixturePath("tmp/pow")
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("tmp/pow")
configuration.gatherHostConfigurations (err, hosts) ->
test.same {}, hosts
fs.lstat fixturePath("tmp/pow"), (err, stat) ->
@@ -31,7 +31,7 @@ module.exports = testCase
test.done()
"findHostConfiguration matches hostnames to application roots": (test) ->
- configuration = createConfiguration hostRoot: fixturePath("configuration")
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("configuration")
matchHostToRoot = (host, fixtureRoot) -> (proceed) ->
configuration.findHostConfiguration host, (err, domain, conf) ->
if fixtureRoot
@@ -54,7 +54,7 @@ module.exports = testCase
], test.done
"findHostConfiguration with alternate domain": (test) ->
- configuration = createConfiguration hostRoot: fixturePath("configuration"), domains: ["dev.local"]
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("configuration"), POW_DOMAINS: "dev.local"
test.expect 3
configuration.findHostConfiguration "directory.dev.local", (err, domain, conf) ->
test.same "dev.local", domain
@@ -64,7 +64,7 @@ module.exports = testCase
test.done()
"findHostConfiguration with multiple domains": (test) ->
- configuration = createConfiguration hostRoot: fixturePath("configuration"), domains: ["test", "dev"]
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("configuration"), POW_DOMAINS: ["test", "dev"]
test.expect 4
configuration.findHostConfiguration "directory.dev", (err, domain, conf) ->
test.same "dev", domain
@@ -75,7 +75,7 @@ module.exports = testCase
test.done()
"findHostConfiguration with default host": (test) ->
- configuration = createConfiguration hostRoot: fixturePath("configuration-with-default")
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("configuration-with-default")
test.expect 2
configuration.findHostConfiguration "missing.dev", (err, domain, conf) ->
test.same "dev", domain
@@ -83,7 +83,7 @@ module.exports = testCase
test.done()
"findHostConfiguration with ext domain": (test) ->
- configuration = createConfiguration hostRoot: fixturePath("configuration"), domains: ["dev"], extDomains: ["me"]
+ configuration = createConfiguration POW_HOST_ROOT: fixturePath("configuration"), POW_DOMAINS: ["dev"], POW_EXT_DOMAINS: ["me"]
test.expect 2
configuration.findHostConfiguration "directory.me", (err, domain, conf) ->
test.same "me", domain
@@ -105,7 +105,7 @@ module.exports = testCase
logger = configuration.getLogger "test"
test.same fixturePath("tmp/logs/test.log"), logger.path
- configuration = createConfiguration logRoot: fixturePath("tmp/log2")
+ configuration = createConfiguration POW_LOG_ROOT: fixturePath("tmp/log2")
logger = configuration.getLogger "test"
test.same fixturePath("tmp/log2/test.log"), logger.path
View
4 test/test_daemon.coffee
@@ -10,7 +10,7 @@ module.exports = testCase
"start and stop": (test) ->
test.expect 2
- configuration = new Configuration hostRoot: fixturePath("tmp"), httpPort: 0, dnsPort: 0
+ configuration = new Configuration POW_HOST_ROOT: fixturePath("tmp"), POW_HTTP_PORT: 0, POW_DNS_PORT: 0
daemon = new Daemon configuration
daemon.start()
@@ -27,7 +27,7 @@ module.exports = testCase
server = net.createServer()
server.listen 0, ->
port = server.address().port
- configuration = new Configuration hostRoot: fixturePath("tmp"), httpPort: port
+ configuration = new Configuration POW_HOST_ROOT: fixturePath("tmp"), POW_HTTP_PORT: port
daemon = new Daemon configuration
daemon.start()
View
2  test/test_dns_server.coffee
@@ -12,7 +12,7 @@ module.exports = testCase
"responds to all A queries for the configured domain": (test) ->
test.expect 6
- configuration = createConfiguration domains: ["powtest", "powdev"]
+ configuration = createConfiguration POW_DOMAINS: "powtest,powdev"
dnsServer = new DnsServer configuration
dnsServer.listen 0, ->
{address, port} = dnsServer.address()
View
4 test/test_http_server.coffee
@@ -10,8 +10,8 @@ serveRoot = (root, options, callback) ->
callback = options
options = {}
configuration = createConfiguration
- hostRoot: fixturePath(root),
- dstPort: options.dstPort ? 80
+ POW_HOST_ROOT: fixturePath(root),
+ POW_DST_PORT: options.dstPort ? 80
if root is "proxies"
# there's a proxy setup in this dir to 14136
# let's create an app for it
View
6 test/test_rack_application.coffee
@@ -9,9 +9,9 @@ http = require "http"
serveApp = (path, callback) ->
configuration = createConfiguration
- hostRoot: fixturePath("apps")
- rvmPath: fixturePath("fake-rvm")
- workers: 1
+ POW_HOST_ROOT: fixturePath("apps")
+ POW_RVM_PATH: fixturePath("fake-rvm")
+ POW_WORKERS: 1
@application = new RackApplication configuration, fixturePath(path)
server = connect.createServer()
Please sign in to comment.
Something went wrong with that request. Please try again.