docs: update readme with new usage
This commit is contained in:
@@ -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
@@ -0,0 +1,11 @@
|
||||
# machine:
|
||||
# environment:
|
||||
# NODE_VERSION: 6
|
||||
test:
|
||||
override:
|
||||
- npm run nexe-build
|
||||
# deployment:
|
||||
# target:
|
||||
# branch: bundle-wip
|
||||
# - npm run nexe-build
|
||||
|
||||
Generated
+3
-3
@@ -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
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user