Skip to content

Commit

Permalink
Merge pull request #8 from joncloud/support-linux
Browse files Browse the repository at this point in the history
Adds support for running makensis on Linux/macOS
  • Loading branch information
joncloud committed Sep 10, 2020
2 parents 89f133c + e83f8b2 commit dd1f866
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 36 deletions.
20 changes: 16 additions & 4 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
Expand All @@ -12,16 +9,31 @@ on:
jobs:
build:

runs-on: windows-latest
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ 'windows-latest', 'ubuntu-latest', 'macos-latest' ]
node-version: [12.x]

steps:
- uses: actions/checkout@v2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

- name: 'Install makensis (apt)'
run: sudo apt update && sudo apt install -y nsis nsis-pluginapi
if: ${{ matrix.os == 'ubuntu-latest' }}

- name: 'Install makensis (homebrew)'
run: brew update && brew install makensis
if: ${{ matrix.os == 'macos-latest' }}

- name: 'Clean up'
if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' }}
run: rm -rf node_modules && npm install

- run: npm test
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# Nullsoft scriptable install system GitHub action

This action calls `C:\Program Files (x86)\NSIS\makensis.exe` to create an installer. This action is currently only compatible with Windows.
This action calls `makensis` to create a Windows installer.

This codebase was ported from the Azure DevOps Extension [dev-maxima/nsis-extension][].

[dev-maxima/nsis-extension]: https://github.com/dev-maxima/nsis-extension

## Platforms

This action looks for `makensis` or `makensis.exe` in the environment path, and if not found it will attempt to look in a couple of different places:

* Windows - `C:\Program Files (x86)\NSIS\`
* Linux and macOS:
* `/usr/local/bin/`
* `/usr/bin/`
* `/opt/local/bin/`

## Inputs

### `script-file`
Expand Down
47 changes: 21 additions & 26 deletions installer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');
const makensis = require('./makensis');

const isDirectory = (item) => {
const stats = fs.statSync(item);
Expand Down Expand Up @@ -39,12 +39,6 @@ const copyDirectory = (src, dest) => {
});
};

const nsisInstallPath = path.join('C:', 'Program Files (x86)', 'NSIS');

const installerPathExists = () => {
return fs.existsSync(nsisInstallPath);
};

class Installer {
constructor(debugMode) {
this.debugMode = debugMode;
Expand Down Expand Up @@ -77,10 +71,10 @@ class Installer {
const args = [];
if (this.customArguments.indexOf('/V') === -1 && this.customArguments.indexOf('-V') === -1) {
if (this.debugMode) {
args.push('/V4');
args.push('-V4');
}
else {
args.push('/V1');
args.push('-V1');
}
}

Expand All @@ -91,27 +85,28 @@ class Installer {
}

createInstaller(scriptPath) {
if (!installerPathExists()) {
const installerPathMessage = `Installer path does not exist at ${nsisInstallPath}`;
this.debugLog(installerPathMessage);
throw new Error(installerPathMessage);
}

console.log(`Creating installer for: ${scriptPath}`);

// Include any of the plugins that may have been requested.
const nsisPluginPath = path.join(nsisInstallPath, 'plugins');
this.pluginPaths.forEach(pluginPath => {
console.log('Including plugin path', pluginPath);
copyDirectory(pluginPath, nsisPluginPath);
});
if (this.pluginPaths.length) {
const nsisdir = makensis.getSymbols().NSISDIR;
if (!nsisdir) {
throw new Error('Unable to determine NSISDIR. Check makensis -HDRINFO output');
}
const nsisPluginPath = path.join(nsisdir, 'Plugins');
this.debugLog(`Using system Plugins path ${nsisPluginPath}`);

this.pluginPaths.forEach(pluginPath => {
console.log('Including plugin path', pluginPath);
copyDirectory(pluginPath, nsisPluginPath);
});
}

const args = this.getProcessArguments(scriptPath);

const nsis3Exe = path.join(nsisInstallPath, 'makensis.exe');
const makeCommand = `"${nsis3Exe}" ${args.join(' ')}`;
this.debugLog(`Running ${makeCommand}`);
const process = execSync(makeCommand);
const args = this.getProcessArguments(scriptPath)
.join(' ');

this.debugLog(`Running ${args}`);
const _ = makensis.execSync(args);
}
};

Expand Down
81 changes: 81 additions & 0 deletions makensis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');
const { platform, env } = require('process');

const firstValidPath = (paths) => {
const possiblePaths = paths.filter(fs.existsSync);

return possiblePaths.length ? possiblePaths[0] : '';
};

const getWin32Path = () => {
const evaluationPaths = env.PATH.split(';').concat([
path.join('C:', 'Program Files (x86)', 'NSIS')
]).map(p => path.join(p, 'makensis.exe'));

return firstValidPath(
evaluationPaths
);
};

const getLinuxPath = () => {
const evaluationPaths = env.PATH.split(':').concat([
'/usr/local/bin',
'/usr/bin',
'/opt/local/bin'
]).map(p => path.join(p, 'makensis'));

return firstValidPath(evaluationPaths);
};

class Makensis {
constructor(path) {
if (!fs.existsSync(path)) {
throw new Error('Unable to find makensis executable');
}
this.path = path;
}

execSync(args) {
const buffer = execSync(`"${this.path}" ${args}`);

return buffer;
}

getSymbols() {
const buffer = this.execSync('-HDRINFO');

// Look for the defined symbols in the output.
const exp = /Defined symbols: (.*)/g;
const matches = exp.exec(buffer.toString('utf-8'));
if (!matches || !matches.length || matches.length < 2) {
throw new Error('Unable to get symbols');
}

// Create a map of all of the symbols.
const symbols = { };

// Get all of the symbols, which are comma delimited
// keys or key value pairs separated by =.
matches[1].split(',').forEach(text => {
const index = text.indexOf('=');
if (index === -1) {
symbols[text] = '';
}
else {
const name = text.substr(0, index);
const value = text.substr(index + 1);
symbols[name] = value;
}
});

return symbols;
}
}

const makensisPath = platform === 'win32'
? getWin32Path()
: getLinuxPath();

module.exports = new Makensis(makensisPath);
Empty file modified node_modules/.bin/mocha
100644 → 100755
Empty file.
Empty file modified node_modules/mocha/bin/_mocha
100644 → 100755
Empty file.
Empty file modified node_modules/mocha/bin/mocha
100644 → 100755
Empty file.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^8.1.3"
"mocha": "^8.1.3",
"yaml": "^1.10.0"
}
}
8 changes: 4 additions & 4 deletions test/installer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Installer', () => {

const args = target.getProcessArguments(existingScriptPath);

expect(args).to.contain('/V1');
expect(args).to.contain('-V1');
});

it('should default to all verbosity, given **debug** mode', () => {
Expand All @@ -45,7 +45,7 @@ describe('Installer', () => {

const args = target.getProcessArguments(existingScriptPath);

expect(args).to.contain('/V4');
expect(args).to.contain('-V4');
});

it('should not add verbosity, given /V is in arguments', () => {
Expand All @@ -56,7 +56,7 @@ describe('Installer', () => {

const args = target.getProcessArguments(existingScriptPath);

expect(args).to.not.contain('/V1');
expect(args).to.not.contain('-V1');
expect(args).to.contain('/V2');
});

Expand All @@ -68,7 +68,7 @@ describe('Installer', () => {

const args = target.getProcessArguments(existingScriptPath);

expect(args).to.not.contain('/V1');
expect(args).to.not.contain('-V1');
expect(args).to.contain('-V2');
});

Expand Down

0 comments on commit dd1f866

Please sign in to comment.