pine2e v0.3.0
pine2e
Pinetwo's pluggable stack for twelve-factor web apps based on Express.js and PostgreSQL. Runs on Heroku and similar environments.
Under development. Normally, parts of the stack are developed elsewhere and then extracted from production apps.
How to create a Pine2e app
Create a new folder:
mkdir example
cd example
git initCreate .gitignore:
.env
.env.*Create package.json:
{
"name": "example",
"version": "0.0.0",
"private": true
}Install npm packages:
npm install pine2e --save
npm install debug --save
npm install supervisor --save-dev
npm install grunt grunt-es6-transpiler grunt-contrib-less --save-devCreate Gruntfile.js to set up p2e:... tasks, ES6 transpiler and LESS compiler:
module.exports = function(grunt) {
grunt.initConfig({
es6transpiler: {
server: {
expand: true,
cwd: 'src/',
src: '**/*.js',
dest: 'lib/'
},
client: {
expand: true,
cwd: 'assets/js6/',
src: '**/*.js',
dest: 'assets/js/',
options: {
globals: {
"jQuery": false
}
}
}
},
less: {
client: {
files: {
"assets/css/styles.css": "assets/less/styles.less"
}
}
}
});
grunt.loadNpmTasks('pine2e');
grunt.loadNpmTasks('grunt-es6-transpiler');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.registerTask('server', ['es6transpiler:server']);
grunt.registerTask('client', ['es6transpiler:client', 'less']);
grunt.registerTask('default', ['client', 'server']);
};Create server.js:
require('./app').startServer();Create app.js:
module.exports = require('pine2e').initializeRootApp(__dirname);Create src/config.js:
exports.configureApp = function(app) {
};
exports.configureRootApp = function(app) {
};Create src/routes.js:
module.exports = function(app) {
app.get('/', (req, res) => {
res.render('home');
})
}Create views/home.jade:
extends layout
block content
.container
h1 Hello, world!Create views/layout.jade (this example uses Bootstrap, Google Fonts and LiveReload):
doctype html
html(lang="en")
head
meta(charset='utf8')
meta(http-equiv="X-UA-Compatible", content="IE=edge")
link(href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700', rel='stylesheet', type='text/css')
link(href='/bower_components/bootstrap/dist/css/bootstrap.min.css', rel='stylesheet', type='text/css')
link(href='/assets/css/styles.css', rel='stylesheet', type='text/css')
<!--[if lt IE 9]>
script(src="/bower_components/html5shiv/dist/html5shiv.min.js")
script(src="/bower_components/respond/dest/respond.min.js")
<![endif]-->
title Example
body
header
.container
.col-sm-8
p Hello
.col-sm-4
p World
block content
script(src="/bower_components/jquery/jquery.min.js")
script(src="/bower_components/bootstrap/dist/js/bootstrap.min.js")
script(src="/assets/js/app.js")
script.
if ((location.host || '').split(':')[0] === 'localhost')
document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')Create assest/less/styles.less:
h1 {
color: red;
}Create assets/js6/app.js:
jQuery(function($) {
$('h1').click((e) => {
e.preventDefault();
$('h1').after('<p>Hello from JavaScript</p>');
})
});Compile all assets (note: LiveReload is recommended for ongoing compilation):
gruntCreate bower.json:
{
"private": true,
"dependencies": {
"jquery": "~1.9.0",
"bootstrap": "~3.1.0",
"html5shiv": "~3.7.1",
"respond": "~1.4.2"
}
}then install Bower components:
bower installCreate Procfile for Heroku:
web: node server.jsCreate Procfile.dev for auto-restarting development server:
web: ./node_modules/.bin/supervisor -i assets,migrations,views,src,test server.jsIf you use CoffeeScript and/or Streamline.js, you need to add -x and -e to Procfile.dev:
web: ./node_modules/.bin/supervisor -x ./node_modules/.bin/_coffee -e '_coffee|coffee' -i assets,migrations,views,src,test server.jsStart your local PostgreSQL server (Postgres.app recommended).
Create a dev database in the local PostgreSQL installation:
echo 'CREATE DATABASE example_dev' | psql -d postgresSet up your development environment in .env pointing to your local PostgreSQL database:
PORT=5000
DATABASE_URL=postgres://andreyvit:@localhost:5432/example_devCreate staging and production Heroku apps:
heroku apps:create --remote staging example-staging
heroku apps:create --remote production exampleAdd PostgreSQL addon with automatic backups:
heroku addons:add --app example-staging heroku-postgresql
heroku pg:promote --app example-staging HEROKU_POSTGRESQL_YELLOW_URL # use your URL
heroku addons:add --app example-staging pgbackups:auto-month
heroku addons:add --app example heroku-postgresql
heroku pg:promote --app example HEROKU_POSTGRESQL_AMBER_URL # use your URL
heroku addons:add --app example pgbackups:auto-monthSet up SESSION_SECRET, a required config variable to prevent session hijacking attacks:
heroku config:set --app example-staging "SESSION_SECRET=asdfghjklasdfghjkl"
heroku config:set --app example "SESSION_SECRET=zxcvbnmzxcvbnm"
echo "SESSION_SECRET=qwertyuiop" >>.envSet up logging:
heroku config:set --app example-staging "DEBUG=sql,p2e:*,app:*"
heroku config:set --app example "DEBUG=sql,p2e:*,app:*"
echo "DEBUG=sql,p2e:*,app:*" >>.envThis enables logging of all SQL statements, all Pine2e messages and all messages coming from your app (as long as you use app:smt for your app's logging via the debug module). In the future you may want to log less in production.
Dump Heroku config into local env files:
heroku config -s --app example-staging >.env.staging
echo "HEROKU_APP=myapp-staging" >>.env.staging
heroku config -s --app example >.env.production
echo "HEROKU_APP=example" >>.env.productionAppend ?ssl=1 to each DATABASE_URL (in .env.staging and .env.production).
You can also set GIT_REMOTE to the name of the Heroku remotes, if they don't match the environment names (“staging”, “production”).
Run the app using:
foreman startRun the app in autorestarting mode using:
foreman start -f Procfile.devLock dependency versions for reproducable deployments (you need to rerun this command after any package.json dependency changes):
npm shrinkwrapCommit everything into Git, then deploy to Heroku using:
grunt p2e:deploy:staging
grunt p2e:deploy:productionConfiguration and environments
Pine2e apps follow Heroku's twelve-factor apps methodology. In particular, the app's configuration is loaded from environment variables like PORT and DATABASE_URL. (Unlike, say, Rails, which loads its configuration from YAML files like config/database.yml.)
Normally, you set up a Procfile and use the Foreman gem to run the app. Foreman loads the environment variables from a file called .env, although the file name can be provided from the command line.
Pine2e embraces the notion of .env file, extending the idea to multiple environments (configurations) of the app. You can use any environment names you want, but the following four are special and conventional:
dev, for running the app during developmenttest, to use when running tests (its database gets be wiped out by each test run)stagingproduction(Pine2e will take extra care when deploying to production, and will refuse to perform destructive actions with production database)
(Any other environment names you might use are treated exactly like staging.)
The configuration for the dev environment is stored in .env (so that it's the default one used by Foreman), and other environments can be configured in .env.test, .env.staging, .env.production, etc.
In addition to the normal Heroku variables you may set up via Heroku commands and find in heroku config -s output, Pine2e defines the following ones:
HEROKU_APPspecifies the name of the Heroku app for the given environment (frequently passed as--app $HEROKU_APPto Heroku commands); this must be specified for staging and production envs (i.e..env.stagingand.env.production), otherwise many Grunt commands won't workGIT_REMOTEspecifies the name of the Git deployment remote for the given environment, if it's different from the name of the environment
Grunt tasks
Be sure to npm install -g grunt-cli if you haven't already.
To create a migration:
grunt p2e:new:migration:create-widgetTo execute migrations:
grunt p2e:migrate:dev
grunt p2e:migrate:test
grunt p2e:migrate:staging
grunt p2e:migrate:productionTo dump the schema of ‘dev’ environment's database into schema.sql:
grunt p2e:schema:dumpTo copy the database from production to staging, overwriting the staging database:
grunt p2e:db:copy:production:stagingTo deploy:
p2e:deploy:staging
p2e:deploy:productionTo run an arbitrary Heroku command in the given environment's app:
p2e:heroku:staging:ps
p2e:heroku:staging:config
p2e:heroku:staging:pgbackups:restore
# note: currently there's no way to pass argumentsTests
Uses mocha, run npm test to execute tests.
The MIT License
Copyright (c) 2014 Andrey Tarantsov (andrey@tarantsov.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.