Skip to content

Commit e7c5f84

Browse files
authored
Merge pull request #11 from thetnaingtn/support-npm
Release as npm package
2 parents c6acb05 + 854e074 commit e7c5f84

5 files changed

+277
-1
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Mac
22
.DS_Store
33

4-
#goreleaser
4+
# node
5+
node_modules
6+
7+
# goreleaser
58
dist/

.goreleaser.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
builds:
22
- main: ./cmd/forky
3+
binary: forky
34
goos:
45
- darwin
56
- linux

package-lock.json

+35
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "forkyy",
3+
"version": "0.5.2",
4+
"description": "Synchronize your forks with ease",
5+
"main": "index.js",
6+
"scripts": {
7+
"postinstall": "node postinstall.js install",
8+
"preuninstall": "node postinstall.js uninstall"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/thetnaingtn/forky.git"
13+
},
14+
"keywords": [],
15+
"author": "thetnaingtn",
16+
"license": "ISC",
17+
"goBinary":{
18+
"name":"forky",
19+
"path":"./bin"
20+
},
21+
"files": [
22+
"dist",
23+
"postinstall.js"
24+
],
25+
"bugs": {
26+
"url": "https://github.com/thetnaingtn/forky/issues"
27+
},
28+
"homepage": "https://github.com/thetnaingtn/forky#readme",
29+
"dependencies": {
30+
"mkdirp": "^1.0.4"
31+
}
32+
}

postinstall.js

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!/usr/bin/env node
2+
3+
"use strict";
4+
// Thanks to author of https://github.com/sanathkr/go-npm, we were able to modify his code to work with private packages
5+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
6+
7+
var path = require('path'),
8+
mkdirp = require('mkdirp'),
9+
fs = require('fs');
10+
11+
// Mapping from Node's `process.arch` to Golang's `$GOARCH`
12+
var ARCH_MAPPING = {
13+
"ia32": "386",
14+
"x64": "amd64",
15+
"arm": "arm",
16+
"arm64":"arm64"
17+
};
18+
19+
// Mapping between Node's `process.platform` to Golang's
20+
var PLATFORM_MAPPING = {
21+
"darwin": "darwin",
22+
"linux": "linux",
23+
"win32": "windows",
24+
"freebsd": "freebsd"
25+
};
26+
27+
async function getInstallationPath() {
28+
29+
// `npm bin` will output the path where binary files should be installed
30+
31+
const value = await execShellCommand("npm bin -g");
32+
33+
34+
var dir = null;
35+
if (!value || value.length === 0) {
36+
37+
// We couldn't infer path from `npm bin`. Let's try to get it from
38+
// Environment variables set by NPM when it runs.
39+
// npm_config_prefix points to NPM's installation directory where `bin` folder is available
40+
// Ex: /Users/foo/.nvm/versions/node/v4.3.0
41+
var env = process.env;
42+
if (env && env.npm_config_prefix) {
43+
dir = path.join(env.npm_config_prefix, "bin");
44+
}
45+
} else {
46+
dir = value.trim();
47+
}
48+
49+
await mkdirp(dir);
50+
return dir;
51+
}
52+
53+
async function verifyAndPlaceBinary(binName, binPath, callback) {
54+
if (!fs.existsSync(path.join(binPath, binName))) return callback('Downloaded binary does not contain the binary specified in configuration - ' + binName);
55+
56+
// Get installation path for executables under node
57+
const installationPath= await getInstallationPath();
58+
// Copy the executable to the path
59+
fs.rename(path.join(binPath, binName), path.join(installationPath, binName),(err)=>{
60+
if(!err){
61+
console.info("Installed cli successfully");
62+
callback(null);
63+
}else{
64+
callback(err);
65+
}
66+
});
67+
}
68+
69+
function validateConfiguration(packageJson) {
70+
71+
if (!packageJson.version) {
72+
return "'version' property must be specified";
73+
}
74+
75+
if (!packageJson.goBinary || _typeof(packageJson.goBinary) !== "object") {
76+
return "'goBinary' property must be defined and be an object";
77+
}
78+
79+
if (!packageJson.goBinary.name) {
80+
return "'name' property is necessary";
81+
}
82+
83+
if (!packageJson.goBinary.path) {
84+
return "'path' property is necessary";
85+
}
86+
}
87+
88+
function parsePackageJson() {
89+
if (!(process.arch in ARCH_MAPPING)) {
90+
console.error("Installation is not supported for this architecture: " + process.arch);
91+
return;
92+
}
93+
94+
if (!(process.platform in PLATFORM_MAPPING)) {
95+
console.error("Installation is not supported for this platform: " + process.platform);
96+
return;
97+
}
98+
99+
var packageJsonPath = path.join(".", "package.json");
100+
if (!fs.existsSync(packageJsonPath)) {
101+
console.error("Unable to find package.json. " + "Please run this script at root of the package you want to be installed");
102+
return;
103+
}
104+
105+
var packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
106+
var error = validateConfiguration(packageJson);
107+
if (error && error.length > 0) {
108+
console.error("Invalid package.json: " + error);
109+
return;
110+
}
111+
112+
// We have validated the config. It exists in all its glory
113+
var binName = packageJson.goBinary.name;
114+
var binPath = packageJson.goBinary.path;
115+
var version = packageJson.version;
116+
if (version[0] === 'v') version = version.substr(1); // strip the 'v' if necessary v0.0.1 => 0.0.1
117+
118+
// Binary name on Windows has .exe suffix
119+
if (process.platform === "win32") {
120+
binName += ".exe";
121+
}
122+
123+
124+
return {
125+
binName: binName,
126+
binPath: binPath,
127+
version: version
128+
};
129+
}
130+
131+
/**
132+
* Reads the configuration from application's package.json,
133+
* validates properties, copied the binary from the package and stores at
134+
* ./bin in the package's root. NPM already has support to install binary files
135+
* specific locations when invoked with "npm install -g"
136+
*
137+
* See: https://docs.npmjs.com/files/package.json#bin
138+
*/
139+
var INVALID_INPUT = "Invalid inputs";
140+
async function install(callback) {
141+
142+
var opts = parsePackageJson();
143+
if (!opts) return callback(INVALID_INPUT);
144+
mkdirp.sync(opts.binPath);
145+
console.info(`Copying the relevant binary for your platform ${process.platform}`);
146+
const src= `./dist/forky_${process.platform}_${ARCH_MAPPING[process.arch]}${ARCH_MAPPING[process.arch] === 'amd64' ? '_v1' : ''}/${opts.binName}`;
147+
await execShellCommand(`cp ${src} ${opts.binPath}/${opts.binName}`);
148+
await verifyAndPlaceBinary(opts.binName, opts.binPath, callback);
149+
}
150+
151+
async function uninstall(callback) {
152+
var opts = parsePackageJson();
153+
try {
154+
const installationPath = await getInstallationPath();
155+
fs.unlink(path.join(installationPath, opts.binName),(err)=>{
156+
if(err){
157+
return callback(err);
158+
}
159+
});
160+
} catch (ex) {
161+
// Ignore errors when deleting the file.
162+
}
163+
console.info("Uninstalled cli successfully");
164+
return callback(null);
165+
}
166+
167+
// Parse command line arguments and call the right method
168+
var actions = {
169+
"install": install,
170+
"uninstall": uninstall
171+
};
172+
/**
173+
* Executes a shell command and return it as a Promise.
174+
* @param cmd {string}
175+
* @return {Promise<string>}
176+
*/
177+
function execShellCommand(cmd) {
178+
const exec = require('child_process').exec;
179+
return new Promise((resolve, reject) => {
180+
exec(cmd, (error, stdout, stderr) => {
181+
if (error) {
182+
console.warn(error);
183+
}
184+
resolve(stdout? stdout : stderr);
185+
});
186+
});
187+
}
188+
189+
var argv = process.argv;
190+
if (argv && argv.length > 2) {
191+
var cmd = process.argv[2];
192+
if (!actions[cmd]) {
193+
console.log("Invalid command. `install` and `uninstall` are the only supported commands");
194+
process.exit(1);
195+
}
196+
197+
actions[cmd](function (err) {
198+
if (err) {
199+
console.error(err);
200+
process.exit(1);
201+
} else {
202+
process.exit(0);
203+
}
204+
});
205+
}

0 commit comments

Comments
 (0)