@Doop/Deploy
All purpose Doop server deployment script.
This project exposes an executable command line script doop-deploy
which is used to deploy Doop projects based on their configuration from app.config.deploy.profiles
.
Features:
- Simple pull from git + build + restart PM2 deployment process
- Peer deployments of other profiles if required
- Special branch expressions to query + deploy to complex branches
- File date deltas to skip unnecessary package reinstall, build, restart steps
- Automatic handling of PM2 processes and custom names
- Optional branch Semver + version bumping on successful deploy
NOTE: For verbose debugging set the DEBUG=deploy
environment variable as per the Debug config.
System-wide config
@Doop/Deploy generally reads from a Doop projects main config object but if the script is installed system-wide it will also check for a ~/.doop-deploy
INI file first.
Any keys within this file are merged into the main ENV setup before processing anything else.
An example of using ~/.doop-deploy
to specify a directory to change to before trying to read config:
# Change to site directory to read config
DOOP_DEPLOY_BASE=/sites/acme.com
Environment Variables
Environment variables are read from the current shell and the above ~/.doop-deploy
file before execution.
Variable | Type | Description |
---|---|---|
DOOP_DEPLOY_BASE |
String |
Set the working direcotry before attempting to read in the config |
DOOP_DEPLOY_PROFILE |
String |
Profile to deploy if no CLI profile is selected |
Configuration
Configuration is read per-profile from the app.config.deploy.profiles
object. Each key is the local ID of the profile with the object following the specification below.
Config key | Type | Default | Description |
---|---|---|---|
id |
String |
(key) | The key of the object, provided here for the template engine |
enabled |
Boolean |
true |
Whether the profile is directly deployable, if disabled the profile can only be deployed via a peerDeploy
|
path |
String |
(current path) | Change to this root directory before deploying a profile |
script |
Array / String
|
Script or other executable which will be run instead of any of the below processes | |
repo |
String |
--repo=<REPO> or "origin"
|
Which source repository to use when deploying, --repo overrides the setting if present |
branch |
String |
--branch=<BRANCH> or "master"
|
The branch to use when deploying, can be a complex expression - see Branch Expressions |
title |
String |
(key via _.startCase) | The human readable name of the deployment profile |
sort |
Number |
10 |
Sort position if deploying multiple profiles (low-to-high) |
peerDeploy |
Array / String
|
"" |
Additional deployments implied if this profile is deployed |
peerDeny |
Array / String
|
"" |
Deployments that are not allowed alongside this profile - an error is raised if both are attempted at once |
processes |
Number |
1 |
The number of PM2 processes to manage during a deployment |
env |
Object |
{} |
Environment variables to set when building (usually NODE_ENV needs to be set |
semver |
Boolean / String
|
false |
Whether to commit a new version completion, ENUM: 'patch' / true , 'minor', 'major' |
semverPackage |
Boolean |
true |
Whether to also bump the new version in package.json
|
pm2Name |
String |
"${profile.id}-${process.alpha}" |
Templatable name of the PM2 processes |
pm2Names |
Array<String> |
(Computed from pm2Name if not specified) | Computed templatable names or manual overrides if needed |
pm2Args |
Object<Array> |
{} |
Specific PM2 arguments per computed instance name |
pm2Args.default |
Array |
['-e', (key)] |
Universal defaults for PM2 process arguments if no other key matches |
Notes:
- Profiles are always deployed according to
sort
order, even if specified bypeerDeploy
- Setting
semver
requires write access as it will try to commit the new version based on the result of the deployment (cherry-picks and all) - If
script
is specified it is deferred to instead of any other deploy process - no fetching, building or restarts are performed - only the script is run - When using
script
only thepath
andenv
settings are processed - all other settings are ignored
Branch expressions
The branch
property can be set to either:
- A branch name (e.g. 'master', 'dev')
- A tag (e.g. 'v1.2.3')
- A patch hash (e.g. '36d050e', 'f9748544f569e38646a10e8aeecdd0fa47bba0ac')
- One of the special expressions below
The following branch expressions are also supported, they take the form TYPE ARG1=VAL1,ARG2=VAL2,ARG3=VAL3
(e.g. branch: tag semver=v1.x.x,sort=desc
)
Branch type | Arguments | Default | Description |
---|---|---|---|
tag |
semver |
"*" |
A semver expression to filter tags by, the first valid semver tag is used |
sort |
"desc" |
Whether to sort tags asending, decending before filtering |
String Templates
Some config options can be templated using ES6 template syntax.
The following variables are available when templating:
Variable | Type | Description |
---|---|---|
_ |
Object |
Lodash instance |
semver |
Object |
Semver instance |
profile |
Object |
Profile configuration object (see above for details) |
process |
Object |
Details about the current proceses iteration |
process.offset |
Number |
Offset (starting at zero) of the current iteration |
process.alpha |
String |
Alphabetical offset of the process (e.g. 'a', 'b' etc.) |
process.name |
String |
Computed process name |
Example config
Project config
The following is a recommended setup in package.json
:
{
"name": "acme-project",
"version": "1.0.0",
"description": "Project that does stuff",
"main": "server/index.js",
"scripts": {
"build": "gulp build",
"dev": "gulp serve",
"deploy": "doop-deploy",
"deploy:pre": "gulp preDeploy",
"deploy:post": "gulp postDeploy",
"start": "node server"
}
}
Basic config
The following is a standard deployment setup with a "Dev" and "Live" profile located in two different paths.
# Within in a Doop `config/index.js` file:
var config = {
deploy: {
profiles: {
dev: {
title: 'Dev',
path: '/sites/dev.acme.com',
sort: 1,
processes: 2,
env: {'NODE_ENV': 'dev'},
pm2Name: 'dev-${process.alpha}',
pm2Args: {
default: [
'-e', 'dev',
'-o', 'port=${10000 + process.offset + 1}',
'-o', 'papertrail-program=${process.name}',
],
'dev-a': [
'-e', 'dev',
'-o', 'port=${10000 + process.offset + 1}',
'-o', 'papertrail-program=${process.name}',
'-o', 'cache.cleanAuto=true',
'-o', 'mongo.migration=true',
],
},
},
live: {
title: 'Live',
path: '/sites/acme.com',
sort: 2,
processes: 8,
env: {'NODE_ENV': 'production'},
pm2Name: 'live-${process.alpha}',
pm2Args: {
default: [
'-e', 'production',
'-o', 'port=${10100 + process.offset + 1}',
'-o', 'papertrail-program=${process.name}',
],
'live-a': [
'-e', 'production',
'-o', 'port=${10000 + process.offset + 1}',
'-o', 'papertrail-program=${process.name}',
'-o', 'cache.cleanAuto=true',
'-o', 'mongo.migration=true',
],
},
},
},
},
}
Chain deployments
The following is an example where:
- Dev becomes the latest version (on
npm run deploy --dev
) - Live becomes the Dev version on deploy (on
npm run deploy --live
) - Fallback is always the previously deployed live version
- Any deployment to Dev is incremented as a patch on semver, if live is deployed it uses the latest semver
The config below should be spliced into the existing config profile:
var config = {
deploy: {
profiles: {
dev: {
semver: 'patch', // Increment semver when deploying Dev
},
live: {
peerDeploy: ['dev', 'fallback'], // Imply other profiles must be updated first
},
fallback: {
enabled: false, // Disable direct deployment
},
},
},
};