Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

installer upgrades #512

Merged
merged 40 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
be1d05c
installer: test installed module run correctly
axetroy Jun 19, 2019
13e167c
fix error
axetroy Jun 19, 2019
296e52b
update
axetroy Jun 19, 2019
109924f
update
axetroy Jun 19, 2019
143c03e
update
axetroy Jun 19, 2019
9a44ed6
update
axetroy Jun 19, 2019
b066a4a
add test
axetroy Jun 19, 2019
880dd11
update
axetroy Jun 19, 2019
43cd107
update
axetroy Jun 19, 2019
89bafa1
update
axetroy Jun 19, 2019
d886fdc
update
axetroy Jun 19, 2019
27f6b4f
fix install local module
axetroy Jun 19, 2019
a5f6acc
remove http/file_server.ts test case
axetroy Jun 19, 2019
c695792
merge master
axetroy Jun 19, 2019
153f9c6
add install local module test case
axetroy Jun 19, 2019
f0f5a38
fix test in windows
axetroy Jun 19, 2019
4b92265
add installed local module test case
axetroy Jun 19, 2019
a96d1f2
try fix windows test case
axetroy Jun 19, 2019
38f701d
try fix windows test case
axetroy Jun 19, 2019
f78763e
try fix windows test case
axetroy Jun 19, 2019
c58c512
split installer
bartlomieju Jun 19, 2019
891be4f
fix prompt on subsequent installation
bartlomieju Jun 19, 2019
656d49d
fix bad shebang
bartlomieju Jun 19, 2019
17e46ca
fmt
bartlomieju Jun 19, 2019
7ae9645
lint & fmt
bartlomieju Jun 19, 2019
4706dc6
fix tests
bartlomieju Jun 19, 2019
e266a89
add debug
bartlomieju Jun 19, 2019
a1a317a
update for windows
bartlomieju Jun 19, 2019
341047f
move more stuff to util
bartlomieju Jun 19, 2019
48720e0
review feedback
bartlomieju Jun 20, 2019
1d69620
lint
bartlomieju Jun 20, 2019
4b08554
remove uninstall command
bartlomieju Jun 20, 2019
1460eaa
reset CI
bartlomieju Jun 20, 2019
f8f49c5
fix tests
bartlomieju Jun 20, 2019
40247f3
support -d/--dir argument
bartlomieju Jun 20, 2019
aa71f58
remove console.log
bartlomieju Jun 20, 2019
a7a00e6
reset CI
bartlomieju Jun 20, 2019
24e8c44
fmt
bartlomieju Jun 20, 2019
571354b
factor out isRemoteUrl
bartlomieju Jun 20, 2019
e67a43a
lint
bartlomieju Jun 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 33 additions & 14 deletions installer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,65 @@ Install remote or local script as executables.

## Installation

`installer` can be install using iteself:
`installer` can be install using itself:

```sh
deno -A https://deno.land/std/installer/mod.ts deno_installer https://deno.land/std/installer/mod.ts -A
```

Installer uses `~/.deno/bin` to store installed scripts so make sure it's in `$PATH`

```
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc # change this to your shell
```

## Usage

Install script

```sh
# remote script
$ deno_installer file_server https://deno.land/std/http/file_server.ts --allow-net --allow-read
> Downloading: https://deno.land/std/http/file_server.ts
> [1/1] Compiling https://deno.land/std/http/file_server.ts
>
> ✅ Successfully installed file_server.
> ~/.deno/bin/file_server

# local script
$ deno_installer file_server ./deno_std/http/file_server.ts --allow-net --allow-read
> Looking for: /dev/deno_std/http/file_server.ts
> [1/1] Compiling file:///dev/deno_std/http/file_server.ts
>
> ✅ Successfully installed file_server.
> ~/.deno/bin/file_server
```

Use installed script:
Run installed script:

```sh
$ file_server
HTTP server listening on http://0.0.0.0:4500/
```

Update installed script
## Custom installation directory

By default installer uses `~/.deno/bin` to store installed scripts so make sure it's in your `$PATH`.

```
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc # change this to your shell
```

If you prefer to change installation directory use `-d` or `--dir` flag.

```
$ deno_installer --dir /usr/local/bin file_server ./deno_std/http/file_server.ts --allow-net --allow-read
> [1/1] Compiling file:///dev/deno_std/http/file_server.ts
>
> ✅ Successfully installed file_server.
> /usr/local/bin/file_server
```

## Update installed script

```sh
$ deno_installer file_server https://deno.land/std/http/file_server.ts --allow-net --allow-read
> ⚠️ file_server is already installed, do you want to overwrite it? [yN]
> y
>
> Downloading: https://deno.land/std/http/file_server.ts
> [1/1] Compiling file:///dev/deno_std/http/file_server.ts
>
> ✅ Successfully installed file_server.
```
Expand All @@ -60,10 +75,14 @@ $ deno_installer --help
Install remote or local script as executables.

USAGE:
deno https://deno.land/std/installer/mod.ts EXE_NAME SCRIPT_URL [FLAGS...]
deno -A https://deno.land/std/installer/mod.ts [OPTIONS] EXE_NAME SCRIPT_URL [FLAGS...]

ARGS:
EXE_NAME Name for executable
SCRIPT_URL Local or remote URL of script to install
[FLAGS...] List of flags for script, both Deno permission and script specific flag can be used.
[FLAGS...] List of flags for script, both Deno permission and script specific
flag can be used.

OPTIONS:
-d, --dir <PATH> Installation directory path (defaults to ~/.deno/bin)
```
156 changes: 78 additions & 78 deletions installer/mod.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
#!/usr/bin/env deno --allow-all
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
const {
args,
env,
readDirSync,
mkdirSync,
writeFile,
exit,
stdin,
chmod,
remove,
run
} = Deno;
const { env, stdin, args, exit, writeFile, chmod, run } = Deno;
import { parse } from "../flags/mod.ts";
import * as path from "../fs/path.ts";
import { exists } from "../fs/exists.ts";
import { ensureDir } from "../fs/ensure_dir.ts";

const encoder = new TextEncoder();
const decoder = new TextDecoder("utf-8");
const isWindows = Deno.platform.os === "win";
// Regular expression to test disk driver letter. eg "C:\\User\username\path\to"
const driverLetterReg = /^[c-z]:/i;
const isWindows = Deno.platform.os === "win";

function showHelp(): void {
console.log(`deno installer
Install remote or local script as executables.

USAGE:
deno -A https://deno.land/std/installer/mod.ts [OPTIONS] EXE_NAME SCRIPT_URL [FLAGS...]

ARGS:
EXE_NAME Name for executable
SCRIPT_URL Local or remote URL of script to install
[FLAGS...] List of flags for script, both Deno permission and script specific
flag can be used.

OPTIONS:
-d, --dir <PATH> Installation directory path (defaults to ~/.deno/bin)
`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish the flags module was more like Go's... This would be generated automatically.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I was looking at porting one of more advanced CLI modules from node:

But I might as well look into https://golang.org/pkg/flag/

}

enum Permission {
Read,
Expand Down Expand Up @@ -67,6 +76,20 @@ function getFlagFromPermission(perm: Permission): string {
return "";
}

function getInstallerDir(): string {
// In Windows's Powershell $HOME environmental variable maybe null
// if so use $HOMEPATH instead.
let { HOME, HOMEPATH } = env();

const HOME_PATH = HOME || HOMEPATH;

if (!HOME_PATH) {
throw new Error("$HOME is not defined.");
}

return path.join(HOME_PATH, ".deno", "bin");
}

async function readCharacter(): Promise<string> {
const byteArray = new Uint8Array(1024);
await stdin.read(byteArray);
Expand All @@ -81,14 +104,6 @@ async function yesNoPrompt(message: string): Promise<boolean> {
return input === "y" || input === "Y";
}

function createDirIfNotExists(path: string): void {
try {
readDirSync(path);
} catch (e) {
mkdirSync(path, true);
}
}

function checkIfExistsInPath(filePath: string): boolean {
// In Windows's Powershell $PATH not exist, so use $Path instead.
// $HOMEDRIVE is only used on Windows.
Expand Down Expand Up @@ -120,33 +135,16 @@ function checkIfExistsInPath(filePath: string): boolean {
return false;
}

function getInstallerDir(): string {
// In Windows's Powershell $HOME environmental variable maybe null
// if so use $HOMEPATH instead.
let { HOME, HOMEPATH } = env();

const HOME_PATH = HOME || HOMEPATH;

if (!HOME_PATH) {
throw new Error("$HOME is not defined.");
}

return path.join(HOME_PATH, ".deno", "bin");
export function isRemoteUrl(url: string): boolean {
return /^https?:\/\//.test(url);
}

function showHelp(): void {
console.log(`deno installer
Install remote or local script as executables.

USAGE:
deno https://deno.land/std/installer/mod.ts EXE_NAME SCRIPT_URL [FLAGS...]

ARGS:
EXE_NAME Name for executable
SCRIPT_URL Local or remote URL of script to install
[FLAGS...] List of flags for script, both Deno permission and script specific
flag can be used.
`);
function validateModuleName(moduleName: string): boolean {
if (/^[a-z][\w-]*$/i.test(moduleName)) {
return true;
} else {
throw new Error("Invalid module name: " + moduleName);
}
}

async function generateExecutable(
Expand Down Expand Up @@ -176,7 +174,7 @@ async function generateExecutable(
}

// generate Shell script
const template = `#/bin/sh
const template = `#!/bin/sh
# ${templateHeader}
basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")

Expand All @@ -200,25 +198,36 @@ exit $ret
export async function install(
moduleName: string,
moduleUrl: string,
flags: string[]
flags: string[],
installationDir?: string
): Promise<void> {
const installerDir = getInstallerDir();
createDirIfNotExists(installerDir);
if (!installationDir) {
installationDir = getInstallerDir();
}
await ensureDir(installationDir);

// if install local module
if (!isRemoteUrl(moduleUrl)) {
moduleUrl = path.resolve(moduleUrl);
}

const filePath = path.join(installerDir, moduleName);
validateModuleName(moduleName);
const filePath = path.join(installationDir, moduleName);

if (await exists(filePath)) {
const msg =
"⚠️ ${moduleName} is already installed, " +
"do you want to overwrite it?";
"⚠️ " +
moduleName +
" is already installed" +
", do you want to overwrite it?";
if (!(await yesNoPrompt(msg))) {
return;
}
}

// ensure script that is being installed exists
const ps = run({
args: ["deno", "fetch", moduleUrl],
args: ["deno", "fetch", "--reload", moduleUrl],
stdout: "inherit",
stderr: "inherit"
});
Expand Down Expand Up @@ -254,44 +263,35 @@ export async function install(
console.log(`✅ Successfully installed ${moduleName}`);
console.log(filePath);

if (!checkIfExistsInPath(installerDir)) {
console.log("\nℹ️ Add ~/.deno/bin to PATH");
if (!checkIfExistsInPath(installationDir)) {
console.log(`\nℹ️ Add ${installationDir} to PATH`);
console.log(
" echo 'export PATH=\"$HOME/.deno/bin:$PATH\"' >> ~/.bashrc # change" +
" echo 'export PATH=\"" +
installationDir +
":$PATH\"' >> ~/.bashrc # change" +
" this to your shell"
);
}
}

export async function uninstall(moduleName: string): Promise<void> {
const installerDir = getInstallerDir();
const filePath = path.join(installerDir, moduleName);

if (!(await exists(filePath))) {
throw new Error(`ℹ️ ${moduleName} not found`);
}

await remove(filePath);
if (isWindows) {
await remove(filePath + ".cmd");
}
console.log(`ℹ️ Uninstalled ${moduleName}`);
}

async function main(): Promise<void> {
if (args.length < 3) {
const parsedArgs = parse(args.slice(1), { stopEarly: true });

if (parsedArgs.h || parsedArgs.help) {
return showHelp();
}

if (["-h", "--help"].includes(args[1])) {
if (parsedArgs._.length < 2) {
return showHelp();
}

const moduleName = args[1];
const moduleUrl = args[2];
const flags = args.slice(3);
const moduleName = parsedArgs._[0];
const moduleUrl = parsedArgs._[1];
const flags = parsedArgs._.slice(2);
const installationDir = parsedArgs.d || parsedArgs.dir;

try {
await install(moduleName, moduleUrl, flags);
await install(moduleName, moduleUrl, flags, installationDir);
} catch (e) {
console.log(e);
exit(1);
Expand Down
Loading