![gal@spitfire.co.il](/assets/img/avatar_default.png)
47 changed files with 1508 additions and 186 deletions
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash |
||||
|
||||
echo "Building binary in docker" |
||||
|
||||
docker build . -t schlez/nsw-static-binary |
||||
|
||||
echo "Copying to ./nsw-linux" |
||||
|
||||
docker run --rm -v $(pwd):$(pwd) --workdir $(pwd) schlez/nsw-static-binary cp /app/_build/default/executable/NswApp.exe ./nsw-linux |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
sed -i 's@"flags": \[\]@"flags": ["-ccopt", "-static"]@' package.json |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
_build |
||||
_esy |
||||
node_modules |
||||
Dockerfile |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
FROM frolvlad/alpine-glibc |
||||
|
||||
RUN apk add --no-cache nodejs bash npm curl g++ make m4 patch gmp-dev perl git jq |
||||
ADD .ci/shasum /usr/bin/shasum |
||||
|
||||
USER root |
||||
|
||||
RUN npm -g config set user root |
||||
RUN npm i -g esy@latest |
||||
|
||||
WORKDIR /app |
||||
ADD . /app |
||||
|
||||
RUN jq '. | .buildDirs.executable.flags |= . + ["-ccopt", "-static"]' package.json > package.json.new && mv package.json.new package.json |
||||
RUN npx esy i |
||||
RUN npx esy pesy |
||||
RUN npx esy b |
||||
RUN npx esy test |
After Width: | Height: | Size: 195 KiB |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
opam-version: "2.0" |
||||
maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>" |
||||
authors: ["Daniel Bünzli <daniel.buenzl i@erratique.ch>"] |
||||
homepage: "http://erratique.ch/software/cmdliner" |
||||
doc: "http://erratique.ch/software/cmdliner/doc/Cmdliner" |
||||
dev-repo: "git+http://erratique.ch/repos/cmdliner.git" |
||||
bug-reports: "https://github.com/dbuenzli/cmdliner/issues" |
||||
tags: [ "cli" "system" "declarative" "org:erratique" ] |
||||
license: "ISC" |
||||
depends:[ "ocaml" {>= "4.03.0"} ] |
||||
build: [[ make "all" "PREFIX=%{prefix}%" ]] |
||||
install: |
||||
[[make "install" "LIBDIR=%{_:lib}%" "DOCDIR=%{_:doc}%" ] |
||||
[make "install-doc" "LIBDIR=%{_:lib}%" "DOCDIR=%{_:doc}%" ]] |
||||
|
||||
synopsis: """Declarative definition of command line interfaces for OCaml""" |
||||
description: """\ |
||||
|
||||
Cmdliner allows the declarative definition of command line interfaces |
||||
for OCaml. |
||||
|
||||
It provides a simple and compositional mechanism to convert command |
||||
line arguments to OCaml values and pass them to your functions. The |
||||
module automatically handles syntax errors, help messages and UNIX man |
||||
page generation. It supports programs with single or multiple commands |
||||
and respects most of the [POSIX][1] and [GNU][2] conventions. |
||||
|
||||
Cmdliner has no dependencies and is distributed under the ISC license. |
||||
|
||||
[1]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html |
||||
[2]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html |
||||
""" |
||||
url { |
||||
archive: "http://erratique.ch/software/cmdliner/releases/cmdliner-1.0.3.tbz" |
||||
checksum: "3674ad01d4445424105d33818c78fba8" |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
opam-version: "2.0" |
||||
version: "0.6.3" |
||||
homepage: "https://github.com/aantron/lambda-soup" |
||||
doc: "http://aantron.github.io/lambda-soup" |
||||
bug-reports: "https://github.com/aantron/lambda-soup/issues" |
||||
license: "BSD" |
||||
|
||||
authors: "Anton Bachin <antonbachin@yahoo.com>" |
||||
maintainer: "Anton Bachin <antonbachin@yahoo.com>" |
||||
dev-repo: "git+https://github.com/aantron/lambda-soup.git" |
||||
depends: [ |
||||
"ocaml" |
||||
"jbuilder" {build & >= "1.0+beta10"} |
||||
"markup" {>= "0.7.1"} |
||||
"ounit" {with-test} |
||||
] |
||||
build: [ |
||||
["jbuilder" "build" "-p" name "-j" jobs] |
||||
] |
||||
synopsis: "Easy functional HTML scraping and manipulation with CSS selectors" |
||||
description: """ |
||||
Lambda Soup is an HTML scraping library inspired by Python's Beautiful Soup. It |
||||
provides lazy traversals from HTML nodes to their parents, children, siblings, |
||||
etc., and to nodes matching CSS selectors. The traversals can be manipulated |
||||
using standard functional combinators such as fold, filter, and map. |
||||
|
||||
The DOM tree is mutable. You can use Lambda Soup for automatic HTML rewriting in |
||||
scripts. Lambda Soup rewrites its own ocamldoc page this way. |
||||
|
||||
A major goal of Lambda Soup is to be easy to use, including in interactive |
||||
sessions, and to have a minimal learning curve. It is a very simple library.""" |
||||
url { |
||||
src: "https://github.com/aantron/lambda-soup/archive/0.6.3.tar.gz" |
||||
checksum: "md5=89f0596aa05a6e7a33bf9d74797905f1" |
||||
} |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
opam-version: "2.0" |
||||
version: "0.8.0" |
||||
|
||||
maintainer: "Anton Bachin <antonbachin@yahoo.com>" |
||||
authors: "Anton Bachin <antonbachin@yahoo.com>" |
||||
homepage: "https://github.com/aantron/markup.ml" |
||||
doc: "http://aantron.github.io/markup.ml" |
||||
bug-reports: "https://github.com/aantron/markup.ml/issues" |
||||
dev-repo: "git+https://github.com/aantron/markup.ml.git" |
||||
license: "BSD" |
||||
|
||||
depends: [ |
||||
"ocaml" |
||||
"dune" {build} |
||||
"ounit" {with-test} |
||||
"uchar" |
||||
"uutf" {>= "1.0.0"} |
||||
] |
||||
# Markup.ml implicitly requires OCaml 4.02.3, as this is a contraint of Dune. |
||||
# Without that, Markup.ml implicitly requires OCaml 4.01.0, as this is a |
||||
# constraint of Uutf. Without *that*, Markup.ml works on OCaml 3.11 or searlier. |
||||
|
||||
build: [ |
||||
["dune" "build" "-p" name "-j" jobs] |
||||
] |
||||
|
||||
synopsis: "Error-recovering functional HTML5 and XML parsers and writers" |
||||
|
||||
description: """ |
||||
Markup.ml provides an HTML parser and an XML parser. The parsers are wrapped in |
||||
a simple interface: they are functions that transform byte streams to parsing |
||||
signal streams. Streams can be manipulated in various ways, such as processing |
||||
by fold, filter, and map, assembly into DOM tree structures, or serialization |
||||
back to HTML or XML. |
||||
|
||||
Both parsers are based on their respective standards. The HTML parser, in |
||||
particular, is based on the state machines defined in HTML5. |
||||
|
||||
The parsers are error-recovering by default, and accept fragments. This makes it |
||||
very easy to get a best-effort parse of some input. The parsers can, however, be |
||||
easily configured to be strict, and to accept only full documents. |
||||
|
||||
Apart from this, the parsers are streaming (do not build up a document in |
||||
memory), non-blocking (can be used with threading libraries), lazy (do not |
||||
consume input unless the signal stream is being read), and process the input in |
||||
a single pass. They automatically detect the character encoding of the input |
||||
stream, and convert everything to UTF-8.""" |
||||
|
||||
url { |
||||
src: "https://github.com/aantron/markup.ml/archive/0.8.0.tar.gz" |
||||
checksum: "md5=be0e44a8e8a540f633996e0e26109b4d" |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
opam-version: "2.0" |
||||
maintainer: "rudi.grinberg@gmail.com" |
||||
authors: [ |
||||
"Tikhon Jelvis" |
||||
"Rudi Grinberg" |
||||
] |
||||
homepage: "https://github.com/rgrinberg/ocaml-semver" |
||||
bug-reports: "https://github.com/rgrinberg/ocaml-semver/issues" |
||||
license: "BSD3" |
||||
dev-repo: "git+https://github.com/rgrinberg/ocaml-semver.git" |
||||
build: [ |
||||
["ocaml" "setup.ml" "-configure"] |
||||
["ocaml" "setup.ml" "-build"] |
||||
["ocaml" "setup.ml" "-configure" "--enable-tests"] {with-test} |
||||
["ocaml" "setup.ml" "-build"] {with-test} |
||||
["ocaml" "setup.ml" "-test"] {with-test} |
||||
["ocaml" "setup.ml" "-doc"] {with-doc} |
||||
] |
||||
install: ["ocaml" "setup.ml" "-install"] |
||||
remove: ["ocamlfind" "remove" "semver"] |
||||
depends: [ |
||||
"ocaml" {>= "4.02.0"} |
||||
"ocamlfind" {build} |
||||
"ounit" {with-test} |
||||
"ocamlbuild" {build} |
||||
] |
||||
synopsis: "Semantic versioning module" |
||||
description: """ |
||||
Provides a single module `Semver` that can parse, compare, and manipulate |
||||
software versions of the form x.x.x. See http://semver.org/""" |
||||
flags: light-uninstall |
||||
url { |
||||
src: "https://github.com/rgrinberg/ocaml-semver/archive/v0.1.0.tar.gz" |
||||
checksum: "md5=ce6614ba2f91754028b29a12989f9da6" |
||||
} |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
opam-version: "2.0" |
||||
maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>" |
||||
authors: ["Daniel Bünzli <daniel.buenzl i@erratique.ch>"] |
||||
homepage: "http://erratique.ch/software/topkg" |
||||
doc: "http://erratique.ch/software/topkg/doc" |
||||
license: "ISC" |
||||
dev-repo: "git+http://erratique.ch/repos/topkg.git" |
||||
bug-reports: "https://github.com/dbuenzli/topkg/issues" |
||||
tags: ["packaging" "ocamlbuild" "org:erratique"] |
||||
depends: [ |
||||
"ocaml" {>= "4.01.0"} |
||||
"ocamlfind" {build & >= "1.6.1"} |
||||
"ocamlbuild" |
||||
"result" ] |
||||
build: [[ |
||||
"ocaml" "pkg/pkg.ml" "build" |
||||
"--pkg-name" name |
||||
"--dev-pkg" "%{pinned}%" ]] |
||||
synopsis: """The transitory OCaml software packager""" |
||||
description: """\ |
||||
|
||||
Topkg is a packager for distributing OCaml software. It provides an |
||||
API to describe the files a package installs in a given build |
||||
configuration and to specify information about the package's |
||||
distribution, creation and publication procedures. |
||||
|
||||
The optional topkg-care package provides the `topkg` command line tool |
||||
which helps with various aspects of a package's life cycle: creating |
||||
and linting a distribution, releasing it on the WWW, publish its |
||||
documentation, add it to the OCaml opam repository, etc. |
||||
|
||||
Topkg is distributed under the ISC license and has **no** |
||||
dependencies. This is what your packages will need as a *build* |
||||
dependency. |
||||
|
||||
Topkg-care is distributed under the ISC license it depends on |
||||
[fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner], |
||||
[webbrowser][webbrowser] and `opam-format`. |
||||
|
||||
[fmt]: http://erratique.ch/software/fmt |
||||
[logs]: http://erratique.ch/software/logs |
||||
[bos]: http://erratique.ch/software/bos |
||||
[cmdliner]: http://erratique.ch/software/cmdliner |
||||
[webbrowser]: http://erratique.ch/software/webbrowser |
||||
""" |
||||
url { |
||||
src: "http://erratique.ch/software/topkg/releases/topkg-1.0.0.tbz" |
||||
checksum: "md5=e3d76bda06bf68cb5853caf6627da603" |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
opam-version: "2.0" |
||||
maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>" |
||||
authors: ["Daniel Bünzli <daniel.buenzl i@erratique.ch>"] |
||||
homepage: "http://ocaml.org" |
||||
doc: "https://ocaml.github.io/uchar/" |
||||
dev-repo: "git+https://github.com/ocaml/uchar.git" |
||||
bug-reports: "https://github.com/ocaml/uchar/issues" |
||||
tags: [ "text" "character" "unicode" "compatibility" "org:ocaml.org" ] |
||||
license: "typeof OCaml system" |
||||
depends: [ |
||||
"ocaml" {>= "3.12.0"} |
||||
"ocamlbuild" {build} |
||||
] |
||||
build: [ |
||||
["ocaml" "pkg/git.ml"] |
||||
[ |
||||
"ocaml" |
||||
"pkg/build.ml" |
||||
"native=%{ocaml:native}%" |
||||
"native-dynlink=%{ocaml:native-dynlink}%" |
||||
] |
||||
] |
||||
synopsis: "Compatibility library for OCaml's Uchar module" |
||||
description: """ |
||||
The `uchar` package provides a compatibility library for the |
||||
[`Uchar`][1] module introduced in OCaml 4.03. |
||||
|
||||
The `uchar` package is distributed under the license of the OCaml |
||||
compiler. See [LICENSE](LICENSE) for details. |
||||
|
||||
[1]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Uchar.html""" |
||||
url { |
||||
src: |
||||
"https://github.com/ocaml/uchar/releases/download/v0.0.2/uchar-0.0.2.tbz" |
||||
checksum: "md5=c9ba2c738d264c420c642f7bb1cf4a36" |
||||
} |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
opam-version: "2.0" |
||||
maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>" |
||||
authors: ["Daniel Bünzli <daniel.buenzl i@erratique.ch>"] |
||||
homepage: "http://erratique.ch/software/uutf" |
||||
doc: "http://erratique.ch/software/uutf/doc/Uutf" |
||||
dev-repo: "git+http://erratique.ch/repos/uutf.git" |
||||
bug-reports: "https://github.com/dbuenzli/uutf/issues" |
||||
tags: [ "unicode" "text" "utf-8" "utf-16" "codec" "org:erratique" ] |
||||
license: "ISC" |
||||
depends: [ |
||||
"ocaml" {>= "4.01.0"} |
||||
"ocamlfind" {build} |
||||
"ocamlbuild" {build} |
||||
"topkg" {build} |
||||
"uchar" |
||||
] |
||||
depopts: ["cmdliner"] |
||||
conflicts: ["cmdliner" { < "0.9.6"} ] |
||||
build: [[ |
||||
"ocaml" "pkg/pkg.ml" "build" |
||||
"--pinned" "%{pinned}%" |
||||
"--with-cmdliner" "%{cmdliner:installed}%" ]] |
||||
synopsis: "Non-blocking streaming Unicode codec for OCaml" |
||||
description: """ |
||||
Uutf is a non-blocking streaming codec to decode and encode the UTF-8, |
||||
UTF-16, UTF-16LE and UTF-16BE encoding schemes. It can efficiently |
||||
work character by character without blocking on IO. Decoders perform |
||||
character position tracking and support newline normalization. |
||||
|
||||
Functions are also provided to fold over the characters of UTF encoded |
||||
OCaml string values and to directly encode characters in OCaml |
||||
Buffer.t values. |
||||
|
||||
Uutf has no dependency and is distributed under the ISC license.""" |
||||
url { |
||||
src: "http://erratique.ch/software/uutf/releases/uutf-1.0.1.tbz" |
||||
checksum: "md5=b8535f974027357094c5cdb4bf03a21b" |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
open Nsw; |
||||
|
||||
let run = () => { |
||||
Console.log( |
||||
Printf.sprintf("export PATH=%s/bin:$PATH", Directories.currentVersion), |
||||
); |
||||
|
||||
Lwt.return(); |
||||
}; |
@ -0,0 +1,101 @@
@@ -0,0 +1,101 @@
|
||||
open Nsw; |
||||
|
||||
let mkDownloadsDir = () => { |
||||
let exists = Lwt_unix.file_exists(Directories.downloads); |
||||
if%lwt (exists |> Lwt.map(x => !x)) { |
||||
Console.log( |
||||
<Pastel> |
||||
"Creating " |
||||
<Pastel color=Pastel.Cyan> Directories.downloads </Pastel> |
||||
" for the first time" |
||||
</Pastel>, |
||||
); |
||||
let%lwt _ = System.mkdirp(Directories.downloads); |
||||
Lwt.return(); |
||||
} else { |
||||
Lwt.return(); |
||||
}; |
||||
}; |
||||
|
||||
let main = (~version as versionName) => { |
||||
let%lwt os = System.NodeOS.get() |
||||
and arch = System.NodeArch.get() |
||||
and versionName = |
||||
switch (versionName) { |
||||
| Some(versionName) => Lwt.return(versionName) |
||||
| None => Nvmrc.getVersion() |
||||
}; |
||||
|
||||
let versionName = Versions.format(versionName); |
||||
|
||||
Console.log( |
||||
<Pastel> |
||||
"Looking for node " |
||||
<Pastel color=Pastel.Cyan> versionName </Pastel> |
||||
" for " |
||||
<Pastel color=Pastel.Cyan> |
||||
{System.NodeOS.toString(os)} |
||||
" " |
||||
{System.NodeArch.toString(arch)} |
||||
</Pastel> |
||||
</Pastel>, |
||||
); |
||||
|
||||
let%lwt filepath = |
||||
Versions.getFileToDownload(~version=versionName, ~os, ~arch); |
||||
let tarDestination = |
||||
Filename.concat(Directories.downloads, versionName ++ ".tar.gz"); |
||||
|
||||
Console.log( |
||||
<Pastel> |
||||
"Downloading " |
||||
<Pastel color=Pastel.Cyan> filepath </Pastel> |
||||
" to " |
||||
<Pastel color=Pastel.Cyan> tarDestination </Pastel> |
||||
</Pastel>, |
||||
); |
||||
|
||||
let%lwt _ = System.mkdirp(Filename.dirname(tarDestination)); |
||||
let%lwt _ = Http.download(filepath, ~into=tarDestination); |
||||
let extractionDestination = |
||||
Filename.concat(Directories.nodeVersions, versionName); |
||||
|
||||
Console.log( |
||||
<Pastel> |
||||
"Extracting " |
||||
<Pastel color=Pastel.Cyan> tarDestination </Pastel> |
||||
" to " |
||||
<Pastel color=Pastel.Cyan> extractionDestination </Pastel> |
||||
</Pastel>, |
||||
); |
||||
|
||||
let%lwt _ = |
||||
Compression.extractFile(tarDestination, ~into=extractionDestination); |
||||
|
||||
Lwt.return(); |
||||
}; |
||||
|
||||
let run = (~version) => |
||||
try%lwt (main(~version)) { |
||||
| Versions.No_Download_For_System(os, arch) => |
||||
Console.log( |
||||
<Pastel> |
||||
"Version exists, but can't find a file for your system:\n" |
||||
" OS: " |
||||
<Pastel color=Pastel.Cyan> {System.NodeOS.toString(os)} </Pastel> |
||||
"\n" |
||||
" Architecture: " |
||||
<Pastel color=Pastel.Cyan> {System.NodeArch.toString(arch)} </Pastel> |
||||
</Pastel>, |
||||
) |
||||
|> Lwt.return |
||||
| Versions.Version_not_found(version) => |
||||
Console.log( |
||||
<Pastel> |
||||
"Version " |
||||
<Pastel color=Pastel.Cyan> version </Pastel> |
||||
" not found!" |
||||
</Pastel>, |
||||
) |
||||
|> Lwt.return |
||||
}; |
@ -1,122 +1,37 @@
@@ -1,122 +1,37 @@
|
||||
module Path = { |
||||
let rec join = xs => |
||||
switch (xs) { |
||||
| [x] => x |
||||
| [x, ...xs] => Filename.concat(x, join(xs)) |
||||
| [] => "" |
||||
}; |
||||
}; |
||||
|
||||
module Fs = { |
||||
open Core; |
||||
let readdir = dir => |
||||
switch (Sys.readdir(dir)) { |
||||
| x => Ok(x) |
||||
| exception (Sys_error(error)) => Error(error) |
||||
}; |
||||
|
||||
let realpath = Filename.realpath; |
||||
}; |
||||
|
||||
module Result = { |
||||
let return = x => Ok(x); |
||||
|
||||
let both = (a, b) => |
||||
switch (a, b) { |
||||
| (Error(_) as e, _) |
||||
| (_, Error(_) as e) => e |
||||
| (Ok(ax), Ok(bx)) => Ok((ax, bx)) |
||||
}; |
||||
|
||||
let map = (fn, res) => |
||||
switch (res) { |
||||
| Ok(x) => Ok(fn(x)) |
||||
| Error(_) as e => e |
||||
}; |
||||
|
||||
let bind = (fn, res) => |
||||
switch (res) { |
||||
| Ok(x) => fn(x) |
||||
| Error(_) as e => e |
||||
}; |
||||
|
||||
let fold = (error, ok, res) => |
||||
switch (res) { |
||||
| Ok(x) => ok(x) |
||||
| Error(x) => error(x) |
||||
}; |
||||
|
||||
module Let_syntax = { |
||||
let map = (x, ~f) => map(f, x); |
||||
let bind = (x, ~f) => bind(f, x); |
||||
}; |
||||
}; |
||||
|
||||
module Opt = { |
||||
let orThrow = (message, opt) => |
||||
switch (opt) { |
||||
| None => failwith(message) |
||||
| Some(x) => x |
||||
}; |
||||
|
||||
let fold = (none, some, opt) => |
||||
switch (opt) { |
||||
| None => none() |
||||
| Some(x) => some(x) |
||||
}; |
||||
|
||||
let toResult = (error, opt) => |
||||
switch (opt) { |
||||
| None => Error(error) |
||||
| Some(x) => Ok(x) |
||||
}; |
||||
}; |
||||
|
||||
module Directories = { |
||||
open Core; |
||||
let home = |
||||
Sys.getenv("HOME") |
||||
|> Opt.orThrow("There isn't $HOME environment variable set."); |
||||
let sfwRoot = Path.join([home, ".nsw"]); |
||||
let nodeVersions = Path.join([sfwRoot, "node-versions"]); |
||||
let currentVersion = Path.join([sfwRoot, "current"]); |
||||
}; |
||||
|
||||
let currentVersion = () => |
||||
switch (Fs.realpath(Directories.currentVersion)) { |
||||
| x => Some(x) |
||||
| exception (Unix.Unix_error(_, _, _)) => None |
||||
}; |
||||
|
||||
let printableVersions = (~current, ~versions) => { |
||||
open Pastel; |
||||
open Nsw; |
||||
|
||||
let colorizeVersions = (~current, ~versions) => { |
||||
let strings = |
||||
versions |
||||
|> List.map(version => { |
||||
let fullPath = Path.join([Directories.nodeVersions, version]); |
||||
let str = "- " ++ version; |
||||
fullPath == current ? <Pastel color=Green> str </Pastel> : str; |
||||
open Versions.Local; |
||||
let str = "- " ++ version.name; |
||||
|
||||
let color = |
||||
current |
||||
|> Opt.bind(current => |
||||
current.name == version.name ? Some(Pastel.Green) : None |
||||
); |
||||
|
||||
<Pastel ?color> str </Pastel>; |
||||
}); |
||||
|
||||
<Pastel> ...strings </Pastel>; |
||||
<Pastel> |
||||
<Pastel color=Pastel.Cyan> "## List of installed versions:\n" </Pastel> |
||||
<Pastel> ...strings </Pastel> |
||||
</Pastel>; |
||||
}; |
||||
|
||||
let run = () => { |
||||
open Result; |
||||
|
||||
let%bind current = |
||||
currentVersion() |
||||
|> Opt.toResult("No version selected") |
||||
|> Result.fold(x => x, x => x) |
||||
|> Result.return; |
||||
let%bind x = Fs.readdir(Directories.nodeVersions); |
||||
let getVersionsString = () => |
||||
Result.( |
||||
{ |
||||
let%bind versions = |
||||
Fs.readdir(Directories.nodeVersions) |> Result.map(Array.to_list); |
||||
Versions.getInstalledVersions() |> Result.map(Array.to_list); |
||||
|
||||
Console.log( |
||||
<Pastel color=Pastel.Cyan> "## List of installed versions:" </Pastel>, |
||||
let current = Versions.getCurrentVersion(); |
||||
|
||||
colorizeVersions(~current, ~versions) |> Result.return; |
||||
} |
||||
); |
||||
printableVersions(~current, ~versions) |> Console.log; |
||||
Result.return(); |
||||
}; |
||||
|
||||
let run = () => getVersionsString() |> Result.map(Console.log) |> Lwt.return; |
||||
|
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
open Nsw; |
||||
|
||||
let run = () => |
||||
Versions.Local.( |
||||
{ |
||||
let%lwt versions = Versions.getInstalledVersions() |> Result.toLwt; |
||||
let currentVersion = Versions.getCurrentVersion(); |
||||
|
||||
Console.log("The following versions are installed:"); |
||||
|
||||
versions |
||||
|> Array.iter(version => { |
||||
let color = |
||||
switch (currentVersion) { |
||||
| None => None |
||||
| Some(x) when x.name == version.name => Some(Pastel.Cyan) |
||||
| Some(_) => None |
||||
}; |
||||
Console.log(<Pastel ?color> "* " {version.name} </Pastel>); |
||||
}); |
||||
|
||||
Lwt.return(); |
||||
} |
||||
); |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
open Nsw; |
||||
|
||||
let run = () => { |
||||
Console.log("Looking for some node versions upstream..."); |
||||
|
||||
let%lwt versions = Versions.getRemoteVersions(); |
||||
let currentVersion = Versions.getCurrentVersion(); |
||||
|
||||
versions |
||||
|> List.iter(version => { |
||||
open Versions.Remote; |
||||
let str = "* " ++ version.name; |
||||
let color = |
||||
switch (currentVersion, version.installed) { |
||||
| (Some({name: currentVersionName, _}), _) |
||||
when currentVersionName == version.name => |
||||
Some(Pastel.Cyan) |
||||
| (_, true) => Some(Pastel.Green) |
||||
| (_, false) => None |
||||
}; |
||||
|
||||
Console.log(<Pastel ?color> str </Pastel>); |
||||
}); |
||||
|
||||
Lwt.return(); |
||||
}; |
@ -1 +1,99 @@
@@ -1 +1,99 @@
|
||||
ListInstallations.run(); |
||||
let version = "1.0.0"; |
||||
|
||||
module Commands = { |
||||
let use = version => Lwt_main.run(Use.run(version)); |
||||
let listRemote = () => Lwt_main.run(ListRemote.run()); |
||||
let listLocal = () => Lwt_main.run(ListLocal.run()); |
||||
let install = version => Lwt_main.run(Install.run(~version)); |
||||
let env = () => Lwt_main.run(Env.run()); |
||||
}; |
||||
|
||||
open Cmdliner; |
||||
|
||||
let help_secs = [ |
||||
`S(Manpage.s_common_options), |
||||
`P("These options are common to all commands."), |
||||
`S("MORE HELP"), |
||||
`P("Use `$(mname) $(i,COMMAND) --help' for help on a single command."), |
||||
`Noblank, |
||||
`S(Manpage.s_bugs), |
||||
`P("File bug reports at https://github.com/Schniz/nsw"), |
||||
]; |
||||
|
||||
let install = { |
||||
let doc = "Install another node version"; |
||||
let man = []; |
||||
|
||||
let selectedVersion = { |
||||
let doc = "Install another version specified in $(docv)."; |
||||
Arg.( |
||||
value & pos(0, some(string), None) & info([], ~docv="VERSION", ~doc) |
||||
); |
||||
}; |
||||
|
||||
( |
||||
Term.(const(Commands.install) $ selectedVersion), |
||||
Term.info("install", ~version, ~doc, ~exits=Term.default_exits, ~man), |
||||
); |
||||
}; |
||||
|
||||
let listLocal = { |
||||
let doc = "List all the installed versions"; |
||||
let man = []; |
||||
|
||||
( |
||||
Term.(app(const(Commands.listLocal), const())), |
||||
Term.info("ls", ~version, ~doc, ~exits=Term.default_exits, ~man), |
||||
); |
||||
}; |
||||
|
||||
let listRemote = { |
||||
let doc = "List all the versions upstream"; |
||||
let man = []; |
||||
|
||||
( |
||||
Term.(app(const(Commands.listRemote), const())), |
||||
Term.info("ls-remote", ~version, ~doc, ~exits=Term.default_exits, ~man), |
||||
); |
||||
}; |
||||
|
||||
let use = { |
||||
let doc = "Switch to another installed node version"; |
||||
let man = []; |
||||
|
||||
let selectedVersion = { |
||||
let doc = "Switch to version $(docv).\nLeave empty to look for value from `.nvmrc`"; |
||||
Arg.( |
||||
value & pos(0, some(string), None) & info([], ~docv="VERSION", ~doc) |
||||
); |
||||
}; |
||||
|
||||
( |
||||
Term.(const(Commands.use) $ selectedVersion), |
||||
Term.info("use", ~version, ~doc, ~exits=Term.default_exits, ~man), |
||||
); |
||||
}; |
||||
|
||||
let env = { |
||||
let doc = "Show env configurations"; |
||||
let sdocs = Manpage.s_common_options; |
||||
let man = help_secs; |
||||
( |
||||
Term.(const(Commands.env) $ const()), |
||||
Term.info("env", ~version, ~doc, ~exits=Term.default_exits, ~man, ~sdocs), |
||||
); |
||||
}; |
||||
|
||||
let defaultCmd = { |
||||
let doc = "Manage Node.js installations"; |
||||
let sdocs = Manpage.s_common_options; |
||||
let man = help_secs; |
||||
( |
||||
Term.(ret(const(_ => `Help((`Pager, None))) $ const())), |
||||
Term.info("nsw", ~version, ~doc, ~exits=Term.default_exits, ~man, ~sdocs), |
||||
); |
||||
}; |
||||
|
||||
let _ = |
||||
Term.eval_choice(defaultCmd, [install, use, listLocal, listRemote, env]) |
||||
|> Term.exit; |
||||
|
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
open Nsw; |
||||
|
||||
let lwtIgnore = lwt => Lwt.catch(() => lwt, _ => Lwt.return()); |
||||
|
||||
exception Version_Not_Installed(string); |
||||
|
||||
let switchVersion = version => { |
||||
let versionDir = Filename.concat(Directories.nodeVersions, version); |
||||
|
||||
let%lwt _ = |
||||
if%lwt (Lwt_unix.file_exists(versionDir) |> Lwt.map(x => !x)) { |
||||
Lwt.fail(Version_Not_Installed(version)); |
||||
}; |
||||
|
||||
let destination = Filename.concat(versionDir, "installation"); |
||||
let source = Directories.currentVersion; |
||||
|
||||
Console.log( |
||||
<Pastel> |
||||
"Linking " |
||||
<Pastel color=Pastel.Cyan> source </Pastel> |
||||
" to " |
||||
<Pastel color=Pastel.Cyan> destination </Pastel> |
||||
</Pastel>, |
||||
); |
||||
|
||||
let%lwt _ = Lwt_unix.unlink(Directories.currentVersion) |> lwtIgnore; |
||||
let%lwt _ = Lwt_unix.symlink(destination, Directories.currentVersion); |
||||
|
||||
Console.log( |
||||
<Pastel> "Using " <Pastel color=Pastel.Cyan> version </Pastel> </Pastel>, |
||||
); |
||||
|
||||
Lwt.return(); |
||||
}; |
||||
|
||||
let main = (~version as providedVersion) => { |
||||
let%lwt version = |
||||
switch (providedVersion) { |
||||
| Some(version) => Lwt.return(version) |
||||
| None => Nvmrc.getVersion() |
||||
}; |
||||
switchVersion(Versions.format(version)); |
||||
}; |
||||
|
||||
let run = version => |
||||
try%lwt (main(~version)) { |
||||
| Version_Not_Installed(version) => |
||||
Console.log( |
||||
<Pastel color=Pastel.Red> |
||||
"The following version is not installed: " |
||||
version |
||||
</Pastel>, |
||||
) |
||||
|> Lwt.return |
||||
| Nvmrc.Version_Not_Provided => |
||||
Console.log( |
||||
<Pastel color=Pastel.Red> |
||||
"No .nvmrc was found in the current directory. Please provide a version number." |
||||
</Pastel>, |
||||
) |
||||
|> Lwt.return |
||||
}; |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
eval $(nsw env) |
||||
nsw install v8.11.3 |
||||
nsw use v8.11.3 |
||||
|
||||
if [ "$(node --version)" != "v8.11.3" ]; then |
||||
echo "Node version is not v8.11.3!" |
||||
exit 1 |
||||
fi |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash |
||||
|
||||
eval $(nsw env) |
||||
nsw install |
||||
nsw use |
||||
|
||||
if [ "$(node --version)" != "v10.9.0" ]; then |
||||
echo "Node version is not v10.9.0!" |
||||
exit 1 |
||||
fi |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash |
||||
|
||||
set -e |
||||
|
||||
DIRECTORY=`dirname $0` |
||||
BINARY=$1 |
||||
TEMP_DIR_BASE=$(pwd)/$DIRECTORY/.tmp |
||||
TEMP_BINARY_PATH=$TEMP_DIR_BASE/bin |
||||
TEMP_NSW_DIR=$TEMP_DIR_BASE/.nsw |
||||
|
||||
if [ "$BINARY" == "" ]; then |
||||
echo "No binary supplied!" |
||||
exit 1 |
||||
fi |
||||
|
||||
echo "using nvm=$BINARY" |
||||
|
||||
rm -rf $TEMP_DIR_BASE |
||||
mkdir $TEMP_DIR_BASE $TEMP_BINARY_PATH |
||||
cp $BINARY $TEMP_BINARY_PATH/nsw |
||||
|
||||
for test_file in $DIRECTORY/*/run.sh; do |
||||
rm -rf $TEMP_NSW_DIR |
||||
|
||||
echo "Running test in $test_file" |
||||
echo "Running test in $test_file" | sed "s/./-/g" |
||||
(cd $(dirname $test_file) && NSW_DIR=$TEMP_NSW_DIR PATH=$TEMP_BINARY_PATH:$PATH bash $(basename $test_file)) |
||||
echo "" |
||||
echo " -> Finished!" |
||||
|
||||
rm -rf $TEMP_NSW_DIR |
||||
done |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
let extractFile = (~into as destination, filepath) => { |
||||
let%lwt _ = System.mkdirp(destination); |
||||
let%lwt _ = |
||||
System.unix_exec( |
||||
"tar", |
||||
~args=[|"-xvf", filepath, "--directory", destination|], |
||||
~stderr=`Dev_null, |
||||
); |
||||
let%lwt files = Fs.readdir(destination) |> Result.toLwt; |
||||
let filename = files[0]; |
||||
Lwt_unix.rename( |
||||
Filename.concat(destination, filename), |
||||
Filename.concat(destination, "installation"), |
||||
); |
||||
}; |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
let sfwRoot = |
||||
Opt.( |
||||
Sys.getenv_opt("NSW_DIR") |
||||
or { |
||||
let home = |
||||
Sys.getenv_opt("HOME") |
||||
|> Opt.orThrow("There isn't $HOME environment variable set."); |
||||
Filename.concat(home, ".nsw"); |
||||
} |
||||
); |
||||
let nodeVersions = Filename.concat(sfwRoot, "node-versions"); |
||||
let currentVersion = Filename.concat(sfwRoot, "current"); |
||||
let downloads = Filename.concat(sfwRoot, "downloads"); |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
open Core; |
||||
|
||||
let readdir = dir => |
||||
switch (Sys.readdir(dir)) { |
||||
| x => Ok(x) |
||||
| exception (Sys_error(error)) => Error(error) |
||||
}; |
||||
|
||||
let writeFile = (path, contents) => { |
||||
let%lwt x = Lwt_unix.openfile(path, [Unix.O_RDWR, Unix.O_CREAT], 777); |
||||
let%lwt _ = |
||||
Lwt.finalize( |
||||
() => Lwt_unix.write_string(x, contents, 0, String.length(contents)), |
||||
() => Lwt_unix.close(x), |
||||
); |
||||
Lwt.return(); |
||||
}; |
||||
|
||||
let realpath = Filename.realpath; |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
type response = { |
||||
body: string, |
||||
status: int, |
||||
}; |
||||
|
||||
let body = response => response.body; |
||||
let status = response => response.status; |
||||
|
||||
let rec getBody = listOfStrings => { |
||||
switch (listOfStrings) { |
||||
| [] => "" |
||||
| ["", ...rest] => String.concat("\n", rest) |
||||
| [_, ...xs] => getBody(xs) |
||||
}; |
||||
}; |
||||
|
||||
let rec getStatus = string => { |
||||
List.nth(String.split_on_char(' ', string), 1); |
||||
}; |
||||
|
||||
exception Unknown_status_code(response); |
||||
exception Not_found(response); |
||||
exception Internal_server_error(response); |
||||
|
||||
let verifyStatus = response => { |
||||
switch (response.status) { |
||||
| 200 => Lwt.return(response) |
||||
| x when x / 100 == 4 => Lwt.fail(Not_found(response)) |
||||
| x when x / 100 == 5 => Lwt.fail(Internal_server_error(response)) |
||||
| x => Lwt.fail(Unknown_status_code(response)) |
||||
}; |
||||
}; |
||||
|
||||
let parseResponse = lines => { |
||||
let body = getBody(lines); |
||||
let status = getStatus(lines |> List.hd) |> int_of_string; |
||||
{body, status}; |
||||
}; |
||||
|
||||
let makeRequest = url => { |
||||
let%lwt response = |
||||
System.unix_exec("curl", ~args=[|url, "-D", "-", "--silent"|]); |
||||
response |> parseResponse |> verifyStatus; |
||||
}; |
||||
|
||||
let download = (url, ~into) => { |
||||
let%lwt response = |
||||
System.unix_exec( |
||||
"curl", |
||||
~args=[|url, "-D", "-", "--silent", "-o", into|], |
||||
); |
||||
response |> parseResponse |> verifyStatus; |
||||
}; |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
exception Version_Not_Provided; |
||||
|
||||
let getVersion = () => { |
||||
let%lwt cwd = Lwt_unix.getcwd(); |
||||
let nvmrcFile = Filename.concat(cwd, ".nvmrc"); |
||||
try%lwt ( |
||||
Lwt_io.lines_of_file(nvmrcFile) |
||||
|> Lwt_stream.to_list |
||||
|> Lwt.map(List.hd) |
||||
|> Lwt.map(String.trim) |
||||
) { |
||||
| Unix.Unix_error(Unix.ENOENT, _, _) => Lwt.fail(Version_Not_Provided) |
||||
}; |
||||
}; |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
let orThrow = (message, opt) => |
||||
switch (opt) { |
||||
| None => failwith(message) |
||||
| Some(x) => x |
||||
}; |
||||
|
||||
let map = (fn, opt) => |
||||
switch (opt) { |
||||
| None => None |
||||
| Some(x) => Some(fn(x)) |
||||
}; |
||||
|
||||
let bind = (fn, opt) => |
||||
switch (opt) { |
||||
| None => None |
||||
| Some(x) => fn(x) |
||||
}; |
||||
|
||||
let fold = (none, some, opt) => |
||||
switch (opt) { |
||||
| None => none() |
||||
| Some(x) => some(x) |
||||
}; |
||||
|
||||
let toResult = (error, opt) => |
||||
switch (opt) { |
||||
| None => Error(error) |
||||
| Some(x) => Ok(x) |
||||
}; |
||||
|
||||
let some = x => Some(x); |
||||
|
||||
let (or) = (opt, b) => fold(() => b, x => x, opt); |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
let return = x => Ok(x); |
||||
|
||||
let both = (a, b) => |
||||
switch (a, b) { |
||||
| (Error(_) as e, _) |
||||
| (_, Error(_) as e) => e |
||||
| (Ok(ax), Ok(bx)) => Ok((ax, bx)) |
||||
}; |
||||
|
||||
let mapError = (fn, res) => |
||||
switch (res) { |
||||
| Error(x) => Error(fn(x)) |
||||
| Ok(_) as x => x |
||||
}; |
||||
|
||||
let map = (fn, res) => |
||||
switch (res) { |
||||
| Ok(x) => Ok(fn(x)) |
||||
| Error(_) as e => e |
||||
}; |
||||
|
||||
let bind = (fn, res) => |
||||
switch (res) { |
||||
| Ok(x) => fn(x) |
||||
| Error(_) as e => e |
||||
}; |
||||
|
||||
let fold = (error, ok, res) => |
||||
switch (res) { |
||||
| Ok(x) => ok(x) |
||||
| Error(x) => error(x) |
||||
}; |
||||
|
||||
module Let_syntax = { |
||||
let map = (x, ~f) => map(f, x); |
||||
let bind = (x, ~f) => bind(f, x); |
||||
}; |
||||
|
||||
let toLwt = res => |
||||
switch (res) { |
||||
| Error(x) => Lwt.fail_with(x) |
||||
| Ok(x) => Lwt.return(x) |
||||
}; |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
let unix_exec = |
||||
(~args=[||], ~env=?, ~stderr: Lwt_process.redirection=`Keep, command) => { |
||||
let realArgs = Array.append([|command|], args); |
||||
Lwt_process.pread_lines(~stderr, ~env?, ("", realArgs)) |
||||
|> Lwt_stream.to_list; |
||||
}; |
||||
|
||||
let mkdirp = destination => |
||||
unix_exec("mkdir", ~stderr=`Dev_null, ~args=[|"-p", destination|]); |
||||
|
||||
module NodeArch = { |
||||
type t = |
||||
| X32 |
||||
| X64 |
||||
| Other; |
||||
|
||||
let rec last = xs => |
||||
switch (xs) { |
||||
| [x] => Some(x) |
||||
| [_, ...xs] => last(xs) |
||||
| [] => None |
||||
}; |
||||
|
||||
let findArches = unameResult => { |
||||
let words = unameResult |> List.hd |> String.split_on_char(' '); |
||||
List.exists(word => word == "x86_64", words) ? X64 : X32; |
||||
}; |
||||
|
||||
/* Get node-compliant architecture (x64, x86) */ |
||||
let get = () => |
||||
switch (Sys.os_type) { |
||||
| "Unix" => |
||||
let%lwt result = unix_exec("uname", ~args=[|"-a"|]); |
||||
try (result |> findArches |> Lwt.return) { |
||||
| _ => Lwt.fail_with("Error getting unix information") |
||||
}; |
||||
| _ => Lwt.return(Other) |
||||
}; |
||||
|
||||
let toString = |
||||
fun |
||||
| X64 => "x64" |
||||
| X32 => "x32" |
||||
| Other => "other"; |
||||
}; |
||||
|
||||
module NodeOS = { |
||||
type t = |
||||
| Darwin |
||||
| Linux |
||||
| Other(string); |
||||
|
||||
let get = () => |
||||
switch (Sys.os_type) { |
||||
| "Unix" => |
||||
let%lwt result = unix_exec("uname", ~args=[|"-s"|]); |
||||
switch (result |> List.hd) { |
||||
| "Darwin" => Lwt.return(Darwin) |
||||
| _ => Lwt.return(Linux) |
||||
| exception _ => Lwt.fail_with("Error getting unix information") |
||||
}; |
||||
| other => Other(other) |> Lwt.return |
||||
}; |
||||
|
||||
let toString = |
||||
fun |
||||
| Darwin => "darwin" |
||||
| Linux => "linux" |
||||
| Other(_) => "other"; |
||||
}; |
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
let foo = () => print_endline("Hello"); |
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
module VersionSet = Set.Make(String); |
||||
|
||||
module Local = { |
||||
type t = { |
||||
name: string, |
||||
fullPath: string, |
||||
}; |
||||
}; |
||||
|
||||
exception Version_not_found(string); |
||||
|
||||
module Remote = { |
||||
type t = { |
||||
name: string, |
||||
baseURL: string, |
||||
installed: bool, |
||||
}; |
||||
|
||||
let skip = (~amount, str) => |
||||
Str.last_chars(str, String.length(str) - amount); |
||||
|
||||
let parseSemver = version => version |> skip(~amount=1) |> Semver.of_string; |
||||
|
||||
let compare = (v1, v2) => |
||||
switch (parseSemver(v1), parseSemver(v2)) { |
||||
| (Some(v1), Some(v2)) => Semver.compare(v1, v2) |
||||
| (None, _) |
||||
| (_, None) => - Core.String.compare(v1, v2) |
||||
}; |
||||
|
||||
let getInstalledVersionSet = () => |
||||
Fs.readdir(Directories.nodeVersions) |
||||
|> Result.fold(_ => [||], x => x) |
||||
|> Array.fold_left( |
||||
(acc, curr) => VersionSet.add(curr, acc), |
||||
VersionSet.empty, |
||||
); |
||||
|
||||
let getRelativeLinksFromHTML = html => |
||||
Soup.parse(html) |
||||
|> Soup.select("pre a") |
||||
|> Soup.to_list |
||||
|> List.map(Soup.attribute("href")) |
||||
|> Core.List.filter_map(~f=x => x); |
||||
|
||||
let downloadFileSuffix = ".tar.gz"; |
||||
|
||||
let getVersionFromFilename = filename => { |
||||
let strings = filename |> String.split_on_char('-'); |
||||
List.nth(strings, 1); |
||||
}; |
||||
}; |
||||
|
||||
let format = version => { |
||||
let version = |
||||
switch (Str.first_chars(version, 1) |> Int32.of_string) { |
||||
| _ => "v" ++ version |
||||
| exception _ => version |
||||
}; |
||||
|
||||
version; |
||||
}; |
||||
|
||||
let endsWith = (~suffix, str) => { |
||||
let suffixLength = String.length(suffix); |
||||
|
||||
String.length(str) > suffixLength |
||||
&& Str.last_chars(str, suffixLength) == suffix; |
||||
}; |
||||
|
||||
exception No_Download_For_System(System.NodeOS.t, System.NodeArch.t); |
||||
|
||||
let getFileToDownload = (~version as versionName, ~os, ~arch) => { |
||||
let versionName = |
||||
switch (Str.first_chars(versionName, 1) |> Int32.of_string) { |
||||
| _ => "v" ++ versionName |
||||
| exception _ => versionName |
||||
}; |
||||
let url = "https://nodejs.org/dist/" ++ versionName ++ "/"; |
||||
let%lwt html = |
||||
try%lwt (Http.makeRequest(url) |> Lwt.map(Http.body)) { |
||||
| Http.Not_found(_) => Lwt.fail(Version_not_found(versionName)) |
||||
}; |
||||
let filenames = |
||||
html |
||||
|> Remote.getRelativeLinksFromHTML |
||||
|> List.filter( |
||||
endsWith( |
||||
~suffix= |
||||
System.NodeOS.toString(os) |
||||
++ "-" |
||||
++ System.NodeArch.toString(arch) |
||||
++ Remote.downloadFileSuffix, |
||||
), |
||||
); |
||||
|
||||
switch (filenames |> List.hd) { |
||||
| x => Lwt.return(url ++ x) |
||||
| exception _ => Lwt.fail(No_Download_For_System(os, arch)) |
||||
}; |
||||
}; |
||||
|
||||
let getCurrentVersion = () => |
||||
switch (Fs.realpath(Directories.currentVersion)) { |
||||
| installationPath => |
||||
let fullPath = Filename.dirname(installationPath); |
||||
Some(Local.{fullPath, name: Core.Filename.basename(fullPath)}); |
||||
| exception (Unix.Unix_error(_, _, _)) => None |
||||
}; |
||||
|
||||
let getInstalledVersions = () => |
||||
Fs.readdir(Directories.nodeVersions) |
||||
|> Result.map(x => { |
||||
Array.sort(Remote.compare, x); |
||||
x; |
||||
}) |
||||
|> Result.map( |
||||
Array.map(name => |
||||
Local.{ |
||||
name, |
||||
fullPath: Filename.concat(Directories.nodeVersions, name), |
||||
} |
||||
), |
||||
); |
||||
|
||||
let getRemoteVersions = () => { |
||||
let%lwt bodyString = |
||||
Http.makeRequest("https://nodejs.org/dist/") |> Lwt.map(Http.body); |
||||
|
||||
let versions = bodyString |> Remote.getRelativeLinksFromHTML; |
||||
let installedVersions = Remote.getInstalledVersionSet(); |
||||
|
||||
versions |
||||
|> Core.List.filter(~f=x => |
||||
Str.last_chars(x, 1) == "/" && Str.first_chars(x, 1) != "." |
||||
) |
||||
|> Core.List.map(~f=x => Str.first_chars(x, String.length(x) - 1)) |
||||
|> List.sort(Remote.compare) |
||||
|> List.map(name => |
||||
Remote.{ |
||||
name, |
||||
installed: VersionSet.find_opt(name, installedVersions) != None, |
||||
baseURL: "https://nodejs.org/dist/" ++ name ++ "/", |
||||
} |
||||
) |
||||
|> Lwt.return; |
||||
}; |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
open TestFramework; |
||||
|
||||
describe("Smoke test", ({test}) => { |
||||
test("Tests run!", ({expect}) => |
||||
expect.int(1).toBe(1) |
||||
); |
||||
|
||||
test("Get version", ({expect}) => { |
||||
let version = run([|"--version"|]); |
||||
expect.string(version).toMatch("^[0-9]+.[0-9]+.[0-9]+$"); |
||||
}); |
||||
|
||||
test("env", ({expect}) => { |
||||
let env = run([|"env"|]) |> redactSfwRoot; |
||||
expect.string(env).toMatchSnapshot(); |
||||
}); |
||||
}); |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
let projectDir = Sys.getcwd(); |
||||
let tmpDir = Filename.concat(projectDir, ".nswTmp"); |
||||
|
||||
include Rely.Make({ |
||||
let config = |
||||
Rely.TestFrameworkConfig.initialize({ |
||||
snapshotDir: |
||||
Filename.concat( |
||||
projectDir, |
||||
Filename.concat("test", "__snapshots__"), |
||||
), |
||||
projectDir, |
||||
}); |
||||
}); |
||||
|
||||
let run = args => { |
||||
let arguments = |
||||
args |> Array.append([|"./_build/default/executable/NswApp.exe"|]); |
||||
let env = Unix.environment() |> Array.append([|"NSW_DIR=" ++ tmpDir|]); |
||||
let result = |
||||
Lwt_process.pread_chars(~env, ("", arguments)) |> Lwt_stream.to_string; |
||||
Lwt_main.run(result); |
||||
}; |
||||
|
||||
let clearTmpDir = () => { |
||||
let _ = Lwt_process.pread(("", [|"rm", "-rf", tmpDir|])) |> Lwt_main.run; |
||||
(); |
||||
}; |
||||
|
||||
let redactSfwRoot = |
||||
Str.global_replace(Str.regexp_string(tmpDir), "<sfwRoot>"); |
@ -1,2 +1,3 @@
@@ -1,2 +1,3 @@
|
||||
Nsw.Util.foo(); |
||||
print_endline("Add Your Test Cases Here"); |
||||
include SmokeTest; |
||||
|
||||
TestFramework.cli(); |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
Smoke test › env |
||||
export PATH=<sfwRoot>/current/bin:$PATH |
||||
|
Loading…
Reference in new issue