docs: update readme with new usage

This commit is contained in:
calebboyd
2017-08-31 11:43:08 -05:00
parent c7af89f12c
commit a9de25ba1f
14 changed files with 134 additions and 92 deletions
+31 -28
View File
@@ -16,7 +16,8 @@
## Motivation and Features
- Supports production ready, ([securable](#security)) builds
- Supports production ready builds
- Self contained applications
- Ability to run multiple applications with *different* node.js runtimes.
- Distribute binaries without needing node / npm.
- Idempotent builds
@@ -25,13 +26,11 @@
- Flexible build pipeline
- Cross platform builds
## Usage (Beta)
*Note: V2 API is still subject to change. * For v1 see [V1-EOL](https://github.com/nexe/nexe/tree/V1-EOL)
## Usage
- Existing application bundle:
`nexe -i ./my-app-bundle.js -o ./my-app.exe`
`nexe my-app-bundle.js -o my-app`
- stdin interface
@@ -45,7 +44,7 @@ Additional resources can be added to the binary by passing `-r glob/pattern/**/*
## Compiling Node
By default `nexe` will attempt to download a pre-built executable. However, some users may want to customize the way node is built, either by changing the flags, providing a different icon, or different executable details. These options are supported out of the box, and subsequent builds with compatible options will result in instant build times.
By default `nexe` will attempt to download a pre-built executable. However, some users may want to customize the way node is built, either by changing the flags, providing a different icon, or different executable details. To build node, use the `build` option/flag
Nexe also exposes its patching pipeline to the user. This allows the application of simple patching of node sources prior to compilation.
@@ -57,11 +56,12 @@ Using Nexe Programatically
```javascript
const nexe = require('nexe')
const { compile } = require('nexe')
nexe.compile({
input: './my-app-bundle.js'
output: './my-app.exe'
compile({
input: './my-app.js',
output: './my-app.exe',
build: true, //builds node
patches: [
async (compiler, next) => {
await compiler.setFileContentsAsync(
@@ -89,13 +89,22 @@ nexe.compile({
- #### `target: string`
- Dash seperated platform-architecture-version. e.g. `'win32-ia32-6.10.3'`
- default: `[process.platform, process.arch, process.version.slice(1)].join('-')`
- #### `bundle: string`
- Path to a custom bundling function. When executed it should either return a fusebox producer, or a string.
- If it is a string it will be required and the export `nexeBundle` will be used
- Defaults to the internal fuse-box configuration
- #### `bundle: string | boolean`
- If a string is provided it must be a valid relative module path
and should provide an export with the following signature:
```typescript
export function createBundle (
filename: string,
options: { name: string, minify: any, cwd: string }
): Promise<string>`
```
- default: true, uses the internal fuse-box configuration
- #### `name: string`
- Module friendly name of the application
- default: basename of the input file, or `nexe_${Date.now()}`
- #### `cwd: string`
- Directory nexe will operate on as though it is the cwd
- default: process.cwd()
- #### `version: string`
- The Node version you're building for
- default: `process.version.slice(1)`
@@ -112,7 +121,7 @@ nexe.compile({
- Example: `['--with-dtrace', '--dest-cpu=x64']`
- default: `[]`
- #### `make: Array<string>`
- Array of arguments for the node build make step, on windows this step recieves optiosn for vcBuild.bat
- Array of arguments for the node build make step, on windows this step recieves options for vcBuild.bat
- default: `[]` or `['nosign', 'release']` for non windows systems
- #### `make: Array<string>`
- Alias for `make` option
@@ -143,10 +152,6 @@ nexe.compile({
- #### `loglevel: string`
- Set the loglevel, info, silent, or verbose
- default: `'info'`
- #### `padding`
- Advanced option for controlling the size available in the executable.
- It must be larger than the bundle + resources in bytes
- default: 3, 6, 9, 16, 25, or 40 MB sizes are selected automatically.
- #### `patches: Array<NexePatch>`
- Userland patches for patching or modifying node source
- default: `[]`
@@ -163,26 +168,24 @@ For examples, see the built in patches: [src/patches](src/patches)
- Quickly set a file's contents within the downloaded Node.js source.
- `replaceInFileAsync(filename: string, ...replaceArgs): Promise<void>`
- Quickly perform a replace in a file within the downloaded Node.js source. The rest arguments are passed along to `String.prototype.replace`
- `readFileAsync(filename: string): Promise<SourceFile>`
- `readFileAsync(filename: string): Promise<NexeFile>`
- Access (or create) a file within the downloaded Node.js source.
- `files: Array<SourceFile>`
- The cache of currently read, modified, or created files within the downloaded Node.js source.
- `files: Array<NexeFile>`
- The cache of the currently read, modified, or created files within the downloaded Node.js source.
#### `SourceFile`
#### `NexeFile`
- `contents: string`
- `absPath: string`
- `filename: string`
Any modifications made to `SourceFile#contents` will be maintained in the cache _without_ the need to explicitly write them back out, e.g. using `NexeCompiler#setFileContentsAsync`.
Any modifications made to `NexeFile#contents` will be maintained in the cache _without_ the need to explicitly write them back out, e.g. using `NexeCompiler#setFileContentsAsync`.
### Native Modules
Nexe has a plugin built for use with [fuse-box](http://fuse-box.org) > 2.2.1. This plugin currently supports modules that require `.node` files and those that use the `bindings` module.
Take a look at the [example](examples/native-build/build.js)
Future: Implement support `node-pre-gyp#find`.
## Security
A common use case for Nexe is production deployment. When distributing executables it is important to [sign](https://en.wikipedia.org/wiki/Code_signing) them before distributing. Nexe was designed specifically to not mangle the binary it produces, this allows the checksum and signature of the size and location offsets to be maintained through the code signing process.
- [ ] Implement support `node-pre-gyp#find`.
## Maintainers
+11
View File
@@ -0,0 +1,11 @@
# machine:
# environment:
# NODE_VERSION: 6
test:
override:
- npm run nexe-build
# deployment:
# target:
# branch: bundle-wip
# - npm run nexe-build
+3 -3
View File
@@ -2968,9 +2968,9 @@
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
},
"prettier": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.5.3.tgz",
"integrity": "sha1-WdrcaDNF7GuI+IuU7Urn4do5S/4=",
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.6.1.tgz",
"integrity": "sha512-f85qBoQiqiFM/sCmJaN4Lagj9bqMcv38vCftqp4GfVessAqq3Ns6g+3gd8UXReStLLE/DGEdwiZXoFKxphKqwg==",
"dev": true
},
"pretty-time": {
+1 -1
View File
@@ -54,7 +54,7 @@
"@types/rimraf": "0.0.28",
"got": "^7.1.0",
"mocha": "^3.2.0",
"prettier": "^1.4.4",
"prettier": "^1.6.1",
"ts-node": "3.3.0",
"typescript": "^2.4.1"
}
+6 -6
View File
@@ -4,15 +4,15 @@ import * as child from 'child_process'
import { createHash } from 'crypto'
export interface ExtractNodeModuleOptions {
[key: string]:
| {
additionalFiles: string[]
}
| true
[key: string]: { additionalFiles: string[] } | true
}
function hashName(name: string | Buffer) {
return createHash('md5').update(name).digest('hex').toString().slice(0, 8)
return createHash('md5')
.update(name)
.digest('hex')
.toString()
.slice(0, 8)
}
export function embedDotNode(
+7 -2
View File
@@ -150,11 +150,16 @@ export class NexeCompiler {
private _generateHeader() {
const version =
['configure', 'vcBuild', 'make'].reduce((a, c) => {
return (a += (this.options as any)[c].slice().sort().join())
return (a += (this.options as any)[c]
.slice()
.sort()
.join())
}, '') + this.options.enableNodeCli
const header = {
resources: this.resources.index,
version: createHash('md5').update(version).digest('hex')
version: createHash('md5')
.update(version)
.digest('hex')
}
const serializedHeader = this._serializeHeader(header)
return header
+7 -5
View File
@@ -30,11 +30,13 @@ async function compile(
: []
const nexe = compose(resource, bundle, cli, buildSteps)
return callback
? void nexe(compiler).then(() => callback && callback(null)).catch((e: Error) => {
if (callback) {
callback(e)
} else throw e
})
? void nexe(compiler).then(
() => callback && callback(null),
(e: Error) => {
if (callback) callback(e)
else throw e
}
)
: nexe(compiler)
}
+4 -2
View File
@@ -15,7 +15,6 @@ export interface NexeOptions {
build: boolean
input: string
output: string
compress: boolean
targets: (string | NexeTarget)[]
name: string
cwd: string
@@ -40,6 +39,7 @@ export interface NexeOptions {
info?: boolean
ico?: string
warmup?: string
compress?: boolean
clean?: boolean
/**
* Api Only
@@ -216,7 +216,9 @@ function normalizeOptionsAsync(input?: Partial<NexeOptions>): Promise<NexeOption
return x
})
Object.keys(alias).filter(k => k !== 'rc').forEach(x => delete opts[x])
Object.keys(alias)
.filter(k => k !== 'rc')
.forEach(x => delete opts[x])
return Promise.resolve(options)
}
+4 -1
View File
@@ -12,7 +12,10 @@ export default async function nodeGyp(
nodeGypMarker,
`
${nodeGypMarker}
${files.filter(x => x.filename.startsWith('lib')).map(x => `'${x.filename}'`).toString()},
${files
.filter(x => x.filename.startsWith('lib'))
.map(x => `'${x.filename}'`)
.toString()},
`.trim()
)
}
+9 -8
View File
@@ -28,8 +28,8 @@ interface NodeRelease {
version: string
}
async function getJson<T>(url: string) {
return JSON.parse((await got(url)).body) as T
async function getJson<T>(url: string, options?: any) {
return JSON.parse((await got(url, options)).body) as T
}
//TODO only build the latest of each major...?
@@ -38,19 +38,20 @@ function isBuildableVersion(version: string) {
return !~[0, 1, 2, 3, 4, 5, 7].indexOf(major) || version === '4.8.4'
}
export function getLatestGitRelease() {
return getJson<GitRelease>('https://api.github.com/repos/nexe/nexe/releases/latest')
export function getLatestGitRelease(options?: any) {
return getJson<GitRelease>('https://api.github.com/repos/nexe/nexe/releases/latest', options)
}
export async function storeAsset(asset: GitAsset, dest: string) {
await download(asset.browser_download_url, dest)
export async function storeAsset(asset: GitAsset, dest: string, headers?: any) {
const options = headers ? { headers } : undefined
await download(asset.browser_download_url, dest, options as any)
}
export async function getUnBuiltReleases() {
export async function getUnBuiltReleases(options?: any) {
const nodeReleases = await getJson<NodeRelease[]>(
'https://nodejs.org/download/release/index.json'
)
const existingVersions = (await getLatestGitRelease()).assets.map(x => getTarget(x.name))
const existingVersions = (await getLatestGitRelease(options)).assets.map(x => getTarget(x.name))
const versionMap: { [key: string]: true } = {}
return nodeReleases
+14 -11
View File
@@ -70,17 +70,20 @@ export function getTarget(target: string | NexeTarget = ''): NexeTarget {
target = `${target.platform}-${target.arch}-${target.version}`
}
target.toLowerCase().split('-').forEach(x => {
if (isVersion(x)) {
version = x
}
if (isPlatform(x)) {
platform = prettyPlatform[x]
}
if (isArch(x)) {
arch = prettyArch[x]
}
})
target
.toLowerCase()
.split('-')
.forEach(x => {
if (isVersion(x)) {
version = x
}
if (isPlatform(x)) {
platform = prettyPlatform[x]
}
if (isArch(x)) {
arch = prettyArch[x]
}
})
return new Target(arch, platform, version)
}
+6 -2
View File
@@ -44,11 +44,15 @@ const execFileAsync = pify(execFile)
const isWindows = process.platform === 'win32'
function pathExistsAsync(path: string) {
return statAsync(path).then(x => true).catch(falseOnEnoent)
return statAsync(path)
.then(x => true)
.catch(falseOnEnoent)
}
function isDirectoryAsync(path: string) {
return statAsync(path).then(x => x.isDirectory()).catch(falseOnEnoent)
return statAsync(path)
.then(x => x.isDirectory())
.catch(falseOnEnoent)
}
export {
+14 -10
View File
@@ -1,7 +1,7 @@
import { compile } from '../src/nexe'
import {
getUnBuiltReleases,
getLatestGitRelease
import {
getUnBuiltReleases,
getLatestGitRelease
} from '../src/releases'
import * as ci from './ci'
import { getTarget } from '../src/target'
@@ -9,16 +9,17 @@ import { pathExistsAsync, statAsync, readFileAsync, execFileAsync } from '../src
import got = require('got')
const env = process.env,
branchName = env.CIRCLE_BRANCH || env.APPVEYOR_REPO_BRANCH,
branchName = env.CIRCLE_BRANCH || env.APPVEYOR_REPO_BRANCH || 'master',
isScheduled = Boolean(env.APPVEYOR_SCHEDULED_BUILD || env.NEXE_TRIGGERED),
isLinux = Boolean(env.TRAVIS),
isWindows = Boolean(env.APPVEYOR),
isMac = Boolean(env.CIRCLECI),
isPullRequest = Boolean(env.CIRCLE_PR_NUMBER) || Boolean(env.APPVEYOR_PULL_REQUEST_NUMBER)
isPullRequest = Boolean(env.CIRCLE_PR_NUMBER) || Boolean(env.APPVEYOR_PULL_REQUEST_NUMBER),
headers = { 'Authorization': 'token ' + env.GITHUB_TOKEN }
async function build () {
if (isScheduled) {
const releases = await getUnBuiltReleases()
const releases = await getUnBuiltReleases({ headers })
if (!releases.length) {
return
}
@@ -30,7 +31,7 @@ async function build () {
await ci.triggerDockerBuild(linuxOrAlpine)
}
if (macBuild) {
await ci.triggerMacBuild(macBuild)
await ci.triggerMacBuild(macBuild, branchName)
}
if (windowsBuild) {
await ci.triggerWindowsBuild(windowsBuild)
@@ -38,12 +39,13 @@ async function build () {
}
if (env.NEXE_VERSION) {
const
const
target = getTarget(env.NEXE_VERSION),
output = isWindows ? './out.exe' : './out',
options = {
empty: true,
build: true,
configure: [],
make: [target.arch],
version: target.version,
output
@@ -53,14 +55,16 @@ async function build () {
}
if (isMac) {
await compile(options)
const timeout = setInterval(() => console.log('Building...'), 300 * 1000)
await compile({...options, make: [], configure: ['--dest-cpu=x64']})
clearInterval(timeout)
}
if (isLinux) {}
if (await pathExistsAsync(output)) {
await assertNexeBinary(output)
const gitRelease = await getLatestGitRelease()
const gitRelease = await getLatestGitRelease({ headers })
await got(gitRelease.upload_url.split('{')[0], {
query: { name: target.toString() },
body: await readFileAsync(output),
+17 -13
View File
@@ -27,20 +27,24 @@ export function triggerDockerBuild(release: NexeTarget) {
// })
}
export function triggerMacBuild (release: NexeTarget) {
return Promise.resolve()
// assert.ok(env.CIRCLE_TOKEN)
// const circle = `https://circleci.com/api/v1.1/project/github/nexe/nexe/tree/master?circle-token=${env.CIRCLE_TOKEN}`
// return got(circle, {
// json: true,
// body: {
// build_parameters: {
// NEXE_VERSION: release
// }
// }
// })
export function triggerMacBuild (release: NexeTarget, branch: string) {
assert.ok(env.CIRCLE_TOKEN)
const circle = `https://circleci.com/api/v1.1/project/github/nexe/nexe/tree/${branch}?circle-token=${env.CIRCLE_TOKEN}`
return got(circle, {
json: true,
body: {
build_parameters: {
NEXE_VERSION: release.toString(),
GITHUB_TOKEN: env.GITHUB_TOKEN
}
}
})
}
export function triggerWindowsBuild (release: NexeTarget) {
return Promise.resolve(env.NEXE_VERSION = release.toString())
const hasVersion = 'NEXE_VERSION' in env
env.NEXE_VERSION = hasVersion
? env.NEXE_VERSION!.trim()
: release.toString()
return Promise.resolve()
}