Skip to content

Commit

Permalink
Merge pull request #4 from dworthen/feature/windows
Browse files Browse the repository at this point in the history
Add support for windows
  • Loading branch information
brombal authored Feb 2, 2024
2 parents 29c4aca + 581aa10 commit 86c1848
Show file tree
Hide file tree
Showing 9 changed files with 728 additions and 338 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
node_modules/
bin/*
5 changes: 3 additions & 2 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {
...require('gts/.prettierrc.json')
}
...require('gts/.prettierrc.json'),
bracketSpacing: true,
};
57 changes: 30 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
# Node.js/npm Just installer

A simple Node.js installer for the excellent Just command runner (https://just.systems).
A simple Node.js installer for the excellent Just command runner (https://just.systems). This allows
you to install Just as part of the `npm install` command, so you don't have to worry about setting
it up separately. Great for development teams!

Local usage (great for team projects):

```bash
$ npm install -D just-install
# or
$ yarn add -D just-install
```

Or globally:

```bash
$ npm install -g just-install
# or
$ yarn global add just-install
```

> Now with Windows support!
## Purpose

Using Just is an excellent option for a more robust command runner in your Node.js projects.
However, it requires installing Just separately from the `npm install` command, using one
of the various package managers for your system of choice.
Using Just is an excellent option for a more robust command runner in your Node.js projects.
However, it requires installing Just on your system as a separate installation step, using one
of the various package managers for your system of choice.

If you want to remove the extra setup step of installing Just before developing a Node.js
application, this utility will install a local, standalone binary as part of the `npm install`
If you want to remove the extra setup step of installing Just when developing a Node.js
application, this utility will install a local, standalone binary as part of the `npm install`
command.

After installation, the `just` command will work in npm scripts:

```js
```json
// package.json:

{
...
// ...
"scripts": {
"start": "just start"
}
Expand All @@ -47,31 +55,26 @@ It's great for teams who want to make the set up process for their project as ea

# Implementation notes

- The installer script from Just is included in this repo and modified slightly because as of this
writing, the `--tlsv1.3` flag used for curl does not seem to work on MacOS Ventura (13.1). It
has been replaced with `--tlsv1.2`. Hopefully this will be resolved either by Just or Apple
(haha).

- Yarn cannot execute non-JS scripts as binaries. The workaround is to make the binary a JS file
that executes the Just binary (using `child_process.execFileSync()`). There is an outstanding
issue on yarn's Github (https://github.com/yarnpkg/berry/issues/882); hopefully there will be some
resolution eventually.
This utility executes Just by spawning a child process from Node.js and passing through all stdio
from the Just executable. This is necessary due to technical reasons for Windows and Yarn users,
so to avoid an overly-complex installation process, it works this way for Mac and Linus users too.
I don't expect this to cause any issues, but it might be worth noting.

# MIT License

Copyright © 2022 Alex Brombal

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4 changes: 0 additions & 4 deletions bin/just

This file was deleted.

25 changes: 25 additions & 0 deletions bin/just.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env node

/**
* This file is a pass-through for the actual just binary. It exists because for two reasons:
*
* - Yarn does not allow references to anything other than .js files in the "bin" field in package.json.
* - Windows does not allow executing binaries that don't end in .exe, and we need the package.json "bin" field to
* point to the same file on all platforms.
*/

import child_process from 'node:child_process';
import path from 'node:path';
import process from 'node:process';
import url from "url";

const ext = process.platform === 'win32' ? '.exe' : '';

const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

child_process.execFileSync(
path.resolve(__dirname, 'just' + ext),
process.argv.slice(2),
{ stdio: 'inherit' }
);
96 changes: 65 additions & 31 deletions install.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,74 @@
const https = require('https');
const fs = require('fs');
const child_process = require('child_process');
#!/usr/bin/env node
import path from 'node:path';
import fs from 'node:fs';
import child_process from 'node:child_process';
import fetch from 'node-fetch';
import extract from 'extract-zip';
import url from "url";

const url = 'https://just.systems/install.sh';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

try {
fs.unlinkSync('./bin/just');
} catch (err) {
// ignore
}
const binDir = path.resolve(__dirname, 'bin');

async function installNix() {
const justInstallShScriptUrl = 'https://just.systems/install.sh';

const IS_YARN = process.env.npm_execpath.includes('yarn');
const res = await fetch(justInstallShScriptUrl);

https.get(url, res => {
res.setEncoding('utf8');
let body = '';
res.on('data', data => {
body += data;
const buffer = await res.arrayBuffer();
fs.writeFileSync('./install.sh', new DataView(buffer));
fs.chmodSync('./install.sh', '755');

child_process.execFileSync('./install.sh', ['-f', '--to', './bin'], {
stdio: 'inherit',
});
res.on('end', () => {
fs.writeFileSync('./install.sh', body);
fs.chmodSync('./install.sh', '755');

child_process.execFileSync('./install.sh', ['--to', './bin'], {
stdio: 'inherit',
});
fs.rmSync('./install.sh');
}

if (IS_YARN) {
// move bin/just to bin/justbin
fs.renameSync('./bin/just', './bin/justbin');
async function installWindows() {
const baseDownloadUrl = 'https://github.com/casey/just/releases/latest';
const windowsZipName = 'just-{TAG}-x86_64-pc-windows-msvc.zip';

// copy just-yarn.js to /bin/just
fs.copyFileSync('./just-yarn.js', './bin/just');
// Get asset url
// Redirects to the latest release tag.
// e.g., https://github.com/casey/just/releases/tag/1.13.0
const assetUrlRes = await fetch(baseDownloadUrl, { redirect: 'manual' });
const tag = assetUrlRes.headers.get('location').split('/').pop();
const assetName = windowsZipName.replace('{TAG}', tag);
const assetUrl = `${baseDownloadUrl}/download/${assetName}`;

// chmod +x bin/just
fs.chmodSync('./bin/just', '755');
}
});
});
// Create ./extract directory
const extractPath = path.resolve(__dirname, 'extract');
fs.rmSync(extractPath, { force: true, recursive: true });
fs.mkdirSync(extractPath);

// Download archive to ./extract/[assetName].zip
const archivePath = path.resolve(extractPath, path.basename(assetUrl));
const downloadRes = await fetch(assetUrl, { maxRedirections: 5 });
const archiveBuffer = await downloadRes.arrayBuffer();
fs.writeFileSync(archivePath, new DataView(archiveBuffer));

// Unpack archive into ./extract
await extract(archivePath, { dir: extractPath });

// Move ./extract/just.exe to ./bin/just.exe
fs.copyFileSync(
path.resolve(extractPath, 'just.exe'),
path.resolve(binDir, 'just.exe')
);

// Delete ./extract
fs.rmSync(extractPath, { force: true, recursive: true });
}

async function install() {
if (process.platform === 'win32') {
await installWindows();
} else {
await installNix();
}
}

void install();
18 changes: 0 additions & 18 deletions just-yarn.js

This file was deleted.

Loading

0 comments on commit 86c1848

Please sign in to comment.