spekulate
Automatically generates an RPM Spec file for your Node.js project
Installation
npm install --global spekulate
Features
- Generates an RPM Spec file for your project
- Creates a systemd service definition file
- Supports configuration using your existing
package.json
- Currently supports CentOS 7
Usage
Let's start with a simple Node.js project:
my-cool-api
├── package.json
└── server.js
0 directories, 2 files
First run npm install to install your dependencies:
npm install
This creates the node_modules
directory:
my-cool-api
├── node_modules
├── package.json
└── server.js
1 directory, 2 files
Run the spekulate
command from inside the project directory:
spekulate
You've now got an RPM Spec file and a systemd service definition for your project. You'll also notice that your application has been packaged into a tar.gz
archive, ready to be built with an RPM building tool like rpmbuild
or mock
:
my-cool-api
├── SOURCES
│ └── my-cool-api.tar.gz
├── SPECS
│ └── my-cool-api.spec
├── node_modules
├── my-cool-api.service
├── package.json
└── server.js
3 directories, 5 files
spekulate is designed to be used at build time, just before you package your application into an RPM. Because of this, we recommend adding the generated files to your .gitignore
file:
*.service
SOURCES
SPECS
Install your dependencies first
spekulate assumes that you've already installed your npm dependencies when it is run. This means that you don't need to worry about running npm install
inside a clean RPM-building environment like mock.
The generated spec file instructs your RPM building tool to run npm rebuild
as part of the build process. This ensures that any native modules are rebuilt for your target environment, even if they were originally installed on a different platform.
If for some reason you do not want to rebuild your native modules, you can explicity tell spekulate not to rebuild by adding the following to your package.json
:
{
"spec": {
"rebuild": false
}
}
A typical spekulate build looks like this:
npm install
npm test
spekulate
# build the RPM (using rpmbuild, mock etc.)
Local installation
To avoid the need to install spekulate globally, we recommend installing it locally and creating an npm script in your package.json
file:
npm install --save-dev spekulate
{
"scripts": {
"spec": "spekulate"
}
}
You can then run npm run spec
to generate your spec file in an environment where spekulate isn't installed globally (like your CI server.)
Pruning dependencies
To minimise the final RPM size, your development dependencies (dependencies added with the --save-dev flag) are automatically pruned so that they're not shipped with your production code.
If for some reason you need to package your dev dependencies with your production code you can explicity tell spekulate not to prune by adding the following to your package.json
:
{
"spec": {
"prune": false
}
}
npm start
script
The systemd service file that spekulate generates uses the npm start
script to start your application. Make sure that you've defined this script in your package.json
file.
{
"scripts": {
"start": "node server.js"
}
}
Including only certain files
Similar to npm
, if you specify a files
directive in your package.json
then spekulate
will only include those files or directories plus package.json
and node_modules
in the source tarball:
{
"files": [
"lib",
"routes",
"index.js"
]
}
Alongside this, the main
attribute is also included in the files
listing, although the service is still started using npm start
:
{
"main": "server.js",
"files": [
"lib",
"routes",
"index.js"
]
}
If you have only a main
directive, spekulate will assume you are using it for its original purpose and not create an archive only including that one file.
Node versions
By default, the spec file that spekulate generates isn't tied to a particular Node version. It simply requires the nodejs
package. It's up to you to make the package available when you install the RPM using yum
.
We strongly recommend that you use the Nodesource binary distributions to install a modern version of Node.js for both your RPM building environment and your target server. Follow the setup instructions for Enterprise Linux and then run yum install nodejs
.
If you're using multiple node repositories or a repository with multiple versions of node, you can specify an RPM version requirement with the nodeVersion
property in your package.json
file:
{
"spec": {
"nodeVersion": "< 5.0.0"
}
}
The nodeVersion
property must conform to the RPM version syntax. Take particular note of the epoch
([epoch:]version[-release]
) as a range without an epoch may not result in the desired dependency:
"nodeVersion": "< 7.0.0"
---
Available Packages
nodejs.x86_64 6.2.2-1nodesource.el7.centos nodesource # <- matches as no epoch specified
nodejs.x86_64 1:6.3.0-1nodesource.el7.centos nodesource
nodejs.x86_64 2:6.11.0-1nodesource.el7.centos nodesource # <- Latest but epoch of '2'
Directory Structure
spekulate creates the following directories for your application:
Directory | Purpose |
---|---|
/opt/:projectName |
This is where your application is stored |
/var/log/:projectName |
This is created for any log files that your application needs to write to |
Configuration
spekulate is configured using the spec
property inside your existing package.json
file.
Dependencies
To add a dependency to the generated spec file, list the package dependencies in the requires
array:
{
"spec": {
"requires": [
"vim",
"screen"
]
}
}
If you have any build dependencies (such as python
for node-gyp
), instead of having them available outside the build environment you can instead add them to the buildRequires
array:
{
"spec": {
"buildRequires": [
"python"
]
}
}
Executables
If you have scripts that need to be executable when they're installed on your target server, add them to the executable
array. You can list both files and entire directories:
{
"spec": {
"executable": [
"./other-scripts/my-script.js",
"./scripts"
]
}
}
Config files
If you have files that need to be preserved after every yum update
, you can add them to the config
array.
On update you can achieve following two behaviors:
-
"noreplace": false
(default): Local edited file will be renamed with.rpmsave
-
"noreplace": true
: Local edited file will be preserved and new file from update will be renamed with.rpmnew
{
"spec": {
"config": [
{"file": "./my-config.js", "noreplace": true},
{"file": "./my-other-config.js"}
]
}
}
Post Install Actions
If you need to perform any actions after installing your package (such as moving files on the target server) you can specify these inline using the post
property:
{
"spec": {
"post": [
"mv /opt/my-cool-api/rc.local /etc/rc.local"
]
}
}
Environment variable
If you need to specify environment variables during startup (NODE_ENV for example) you can specify these inline using the spec.environment property:
{
"spec": {
"environment": {
"NODE_ENV": "production",
"NODE_INSTANCE": "%i"
}
}
}
Service Options
If you need to set specific systemd service options - in the [Service]
section of the .service file, you can specify these using the spec.serviceOptions property:
{
"spec": {
"serviceOptions": {
"CPUSchedulingPriority": 50,
"LimitNOFILE": 10000
}
}
}
Unit Options
You can set specific systemd unit options - in the [Unit]
section of the .service file, you can specify these using the spec.unitOptions property:
{
"spec": {
"unitOptions": {
"After": "my-cooler-api.service",
"Before": "my-coolest-api.service",
"Requires": "my-very-cool-api.service",
}
}
}
If you do not require any specific unit options spekulate will use default options:
[Unit]
Description=My Cool API
After=network.target nss-lookup.target
Release Number
By default spekulate will set the RPM release number to 1, if you want to override this you can do so by using the --release
flag:
spekulate --release=7
Custom Name
By default spekulate will set the name from package.json
, if you want to override this you can do so by using the --name
flag:
spekulate --name=my-cool-api
This is useful if you are using private NPM packages which start with an @
.