This commit kicks off the addition of installers for wasm-pack, with the main goals of being: * Users should have a one-click (ideally) solution to download and install wasm-pack. * We should ideally not have to worry about picking up "heavy" dependencies in wasm-pack like C++ or C code (if necessary). The general idea here is very similar to rustup (and in fact a good deal of code is copied from them!). The installation worklow for wasm-pack in theory after this commit looks like: 1. First, a users visits the installer landing page. A preview is available at https://alexcrichton.github.io/wasm-pack/installer/. Clearly this page needs a better stylesheet! 2. The user performs the instructions presented on the page. The website automatically detects what platform you're running on, basically giving you a curl/sh script or a Windows installer. There's various options for seeing other installers as well, and the fallback of `cargo install` is presented if we don't recognize the platform (or if we don't think we have precompiled binaries). 3a. On Unix, users execute a curl/sh script like: ``` curl https://alexcrichton.github.io/wasm-pack/installer/init.sh -sSf | sh ``` This command will download the shell script included in this PR, which will in turn do some further platform detection (like OSX vs Linux) and then download the most recent version of `wasm-pack` from GitHub releases. This is then extracted, the `wasm-pack` binary is renamed to `wasm-pack-init`, and then that binary is run. The binary will refuse by default to overwrite a previous installation, but that can be overridden with the `-f` flag. 3b. On Windows users download a binary. This binary, when run, will pop up a console window. This window will have the same output as the shell script installer, and will wait at the end of its execution to ensure the user has time to read everything. And... that's it! The deployment process for all this looks like so: * All CI builds will update the website and shell script (published to gh-pages). A small script at `docs/installer/build-installer.rs` fills in the current version number inferred from `Cargo.toml`. * Tagged CI builds will publish releases, as usual. * "Pushing a release" is done by bumping the version number in `Cargo.toml`. When bumped all online installers will now point to the new release. There will be a window of time, though, when the version number is bumped and the release hasn't finished building on CI. In this case users will get errors (unfortunately). This is all still a work-in-progress and feedback is definitely appreicated and welcome on all this!master
parent
0bd0b5a4e0
commit
69e5af8fe2
@ -0,0 +1,30 @@ |
||||
use std::fs; |
||||
|
||||
fn main() { |
||||
fs::create_dir_all("docs/book/installer").unwrap(); |
||||
fs::copy( |
||||
"docs/installer/wasm-pack.js", |
||||
"docs/book/installer/wasm-pack.js", |
||||
).unwrap(); |
||||
let index = fs::read_to_string("docs/installer/index.html").unwrap(); |
||||
fs::write( |
||||
"docs/book/installer/index.html", |
||||
fixup(&index), |
||||
).unwrap(); |
||||
|
||||
let init = fs::read_to_string("docs/installer/init.sh").unwrap(); |
||||
fs::write( |
||||
"docs/book/installer/init.sh", |
||||
fixup(&init), |
||||
).unwrap(); |
||||
} |
||||
|
||||
fn fixup(input: &str) -> String { |
||||
let manifest = fs::read_to_string("Cargo.toml").unwrap(); |
||||
let version = manifest.lines() |
||||
.find(|line| line.starts_with("version =")) |
||||
.unwrap(); |
||||
let version = &version[version.find('"').unwrap() + 1..version.rfind('"').unwrap()]; |
||||
|
||||
input.replace("$VERSION", &format!("v{}", version)) |
||||
} |
@ -0,0 +1,106 @@ |
||||
<!DOCTYPE html> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>wasm-pack</title> |
||||
<style> |
||||
body { |
||||
text-align: center; |
||||
margin: 100px; |
||||
font-size: 150%; |
||||
} |
||||
#main { |
||||
padding: 100px; |
||||
} |
||||
.instructions { |
||||
padding: 100px; |
||||
border: 1px solid black; |
||||
} |
||||
.winlink { |
||||
display: block; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
|
||||
<div id='main'> |
||||
Install wasm-pack! A tool with a blurb here. |
||||
</div> |
||||
|
||||
<div id="platform-instructions-unix" style="display: none;"> |
||||
<pre>curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh</pre> |
||||
<p> |
||||
You appear to be running Unix. If not, |
||||
<a class="default-platform-button" href="#">display all supported installers</a>. |
||||
</p> |
||||
</div> |
||||
|
||||
<div id="platform-instructions-win64" class="instructions" style="display: none;"> |
||||
<p> |
||||
You appear to be running windows 64-bit, download and run |
||||
<a class='winlink' href="https://github.com/rustwasm/wasm-pack/releases/download/$VERSION/wasm-pack-init.exe">wasm-pack-init.exe</a> |
||||
then follow the onscreen |
||||
instructions. |
||||
</p> |
||||
<hr/> |
||||
<p> |
||||
If you're a Windows Subsystem for Linux user run the following in your |
||||
terminal, then follow the onscreen instructions to install wasm-pack. |
||||
</p> |
||||
<pre>curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh</pre> |
||||
<hr/> |
||||
<p> |
||||
You appear to be running Windows 64-bit. If not, |
||||
<a class="default-platform-button" href="#"> |
||||
display all supported installers |
||||
</a>. |
||||
</p> |
||||
</div> |
||||
|
||||
<div id="platform-instructions-unknown" class="instructions" style="display: none;"> |
||||
<p>I don't recognize your platform.</p> |
||||
<p> |
||||
We would appreciate it if you |
||||
<a href="https://github.com/rustwasm/wasm-pack/issues/new">reported an issue</a>, |
||||
along with the following values: |
||||
</p> |
||||
|
||||
<div> |
||||
<div>navigator.platform:</div> |
||||
<div id="nav-plat"></div> |
||||
<div>navigator.appVersion:</div> |
||||
<div id="nav-app"></div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div id="platform-instructions-default" class="instructions"> |
||||
<div> |
||||
<p> |
||||
To install wasm-pack, if you are running Unix,<br/> |
||||
run the following in your terminal, then follow the onscreen |
||||
instructions. |
||||
</p> |
||||
<pre>curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh</pre> |
||||
</div> |
||||
|
||||
<hr/> |
||||
|
||||
<div> |
||||
<p> |
||||
If you are running Windows 64-bit,<br/>download and run |
||||
<a class='winlink' href="https://github.com/rustwasm/wasm-pack/releases/download/$VERSION/wasm-pack-init.exe">wasm-pack-init.exe</a> |
||||
then follow the onscreen instructions. |
||||
</p> |
||||
</div> |
||||
|
||||
<hr/> |
||||
|
||||
<div> |
||||
<p> |
||||
For all other platforms, run the following in your terminal: |
||||
</p> |
||||
<pre>cargo install wasm-pack</pre> |
||||
</div> |
||||
</div> |
||||
|
||||
<script type="text/javascript" src="wasm-pack.js"></script> |
@ -0,0 +1,190 @@ |
||||
#!/bin/bash |
||||
# Copyright 2016 The Rust Project Developers. See the COPYRIGHT |
||||
# file at the top-level directory of this distribution and at |
||||
# http://rust-lang.org/COPYRIGHT. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
||||
# option. This file may not be copied, modified, or distributed |
||||
# except according to those terms. |
||||
|
||||
# This is just a little script that can be downloaded from the internet to |
||||
# install wasm-pack. It just does platform detection, downloads the installer |
||||
# and runs it. |
||||
|
||||
set -u |
||||
|
||||
UPDATE_ROOT="https://github.com/rustwasm/wasm-pack/releases/download/$VERSION" |
||||
|
||||
main() { |
||||
downloader --check |
||||
need_cmd uname |
||||
need_cmd mktemp |
||||
need_cmd chmod |
||||
need_cmd mkdir |
||||
need_cmd rm |
||||
need_cmd rmdir |
||||
need_cmd tar |
||||
need_cmd which |
||||
need_cmd dirname |
||||
|
||||
get_architecture || return 1 |
||||
local _arch="$RETVAL" |
||||
assert_nz "$_arch" "arch" |
||||
|
||||
local _ext="" |
||||
case "$_arch" in |
||||
*windows*) |
||||
_ext=".exe" |
||||
;; |
||||
esac |
||||
|
||||
which rustup > /dev/null 2>&1 |
||||
need_ok "failed to find Rust installation, is rustup installed?" |
||||
local _rustup=`which rustup` |
||||
local _tardir="wasm-pack-$VERSION-${_arch}" |
||||
local _url="$UPDATE_ROOT/${_tardir}.tar.gz" |
||||
local _dir="$(mktemp -d 2>/dev/null || ensure mktemp -d -t wasm-pack)" |
||||
local _file="$_dir/input.tar.gz" |
||||
local _wasmpack="$_dir/wasm-pack$_ext" |
||||
local _wasmpackinit="$_dir/wasm-pack-init$_ext" |
||||
|
||||
printf '%s\n' 'info: downloading wasm-pack' 1>&2 |
||||
|
||||
ensure mkdir -p "$_dir" |
||||
downloader "$_url" "$_file" |
||||
if [ $? != 0 ]; then |
||||
say "failed to download $_url" |
||||
say "this may be a standard network error, but it may also indicate" |
||||
say "that wasm-pack's release process is not working. When in doubt" |
||||
say "please feel free to open an issue!" |
||||
exit 1 |
||||
fi |
||||
ensure tar xf "$_file" --strip-components 1 -C "$_dir" |
||||
mv "$_wasmpack" "$_wasmpackinit" |
||||
|
||||
# The installer may want to ask for confirmation on stdin for various |
||||
# operations. We were piped through `sh` though so we probably don't have |
||||
# access to a tty naturally. If it looks like we're attached to a terminal |
||||
# (`-t 1`) then pass the tty down to the installer explicitly. |
||||
if [ -t 1 ]; then |
||||
"$_wasmpackinit" "$@" < /dev/tty |
||||
else |
||||
"$_wasmpackinit" "$@" |
||||
fi |
||||
|
||||
local _retval=$? |
||||
|
||||
ignore rm -rf "$_dir" |
||||
|
||||
return "$_retval" |
||||
} |
||||
|
||||
get_architecture() { |
||||
local _ostype="$(uname -s)" |
||||
local _cputype="$(uname -m)" |
||||
|
||||
if [ "$_ostype" = Darwin -a "$_cputype" = i386 ]; then |
||||
# Darwin `uname -s` lies |
||||
if sysctl hw.optional.x86_64 | grep -q ': 1'; then |
||||
local _cputype=x86_64 |
||||
fi |
||||
fi |
||||
|
||||
case "$_ostype" in |
||||
Linux) |
||||
local _ostype=unknown-linux-musl |
||||
;; |
||||
|
||||
Darwin) |
||||
local _ostype=apple-darwin |
||||
;; |
||||
|
||||
MINGW* | MSYS* | CYGWIN*) |
||||
local _ostype=pc-windows-msvc |
||||
;; |
||||
|
||||
*) |
||||
err "no precompiled binaries available for OS: $_ostype" |
||||
;; |
||||
esac |
||||
|
||||
case "$_cputype" in |
||||
x86_64 | x86-64 | x64 | amd64) |
||||
local _cputype=x86_64 |
||||
;; |
||||
*) |
||||
err "no precompiled binaries available for CPU architecture: $_cputype" |
||||
|
||||
esac |
||||
|
||||
local _arch="$_cputype-$_ostype" |
||||
|
||||
RETVAL="$_arch" |
||||
} |
||||
|
||||
say() { |
||||
echo "wasm-pack-init: $1" |
||||
} |
||||
|
||||
err() { |
||||
say "$1" >&2 |
||||
exit 1 |
||||
} |
||||
|
||||
need_cmd() { |
||||
if ! check_cmd "$1" |
||||
then err "need '$1' (command not found)" |
||||
fi |
||||
} |
||||
|
||||
check_cmd() { |
||||
command -v "$1" > /dev/null 2>&1 |
||||
return $? |
||||
} |
||||
|
||||
need_ok() { |
||||
if [ $? != 0 ]; then err "$1"; fi |
||||
} |
||||
|
||||
assert_nz() { |
||||
if [ -z "$1" ]; then err "assert_nz $2"; fi |
||||
} |
||||
|
||||
# Run a command that should never fail. If the command fails execution |
||||
# will immediately terminate with an error showing the failing |
||||
# command. |
||||
ensure() { |
||||
"$@" |
||||
need_ok "command failed: $*" |
||||
} |
||||
|
||||
# This is just for indicating that commands' results are being |
||||
# intentionally ignored. Usually, because it's being executed |
||||
# as part of error handling. |
||||
ignore() { |
||||
"$@" |
||||
} |
||||
|
||||
# This wraps curl or wget. Try curl first, if not installed, |
||||
# use wget instead. |
||||
downloader() { |
||||
if check_cmd curl |
||||
then _dld=curl |
||||
elif check_cmd wget |
||||
then _dld=wget |
||||
else _dld='curl or wget' # to be used in error message of need_cmd |
||||
fi |
||||
|
||||
if [ "$1" = --check ] |
||||
then need_cmd "$_dld" |
||||
elif [ "$_dld" = curl ] |
||||
then curl -sSfL "$1" -o "$2" |
||||
elif [ "$_dld" = wget ] |
||||
then wget "$1" -O "$2" |
||||
else err "Unknown downloader" # should not reach here |
||||
fi |
||||
} |
||||
|
||||
main "$@" || exit 1 |
@ -0,0 +1,92 @@ |
||||
var platforms = ["default", "unknown", "win64", "unix"]; |
||||
var platform_override = null; |
||||
|
||||
function detect_platform() { |
||||
"use strict"; |
||||
|
||||
if (platform_override !== null) { |
||||
return platforms[platform_override]; |
||||
} |
||||
|
||||
var os = "unknown"; |
||||
|
||||
if (navigator.platform == "Linux x86_64") {os = "unix";} |
||||
if (navigator.platform == "Linux i686") {os = "unix";} |
||||
if (navigator.platform == "Linux i686 on x86_64") {os = "unix";} |
||||
if (navigator.platform == "Linux aarch64") {os = "unix";} |
||||
if (navigator.platform == "Linux armv6l") {os = "unix";} |
||||
if (navigator.platform == "Linux armv7l") {os = "unix";} |
||||
if (navigator.platform == "Linux armv8l") {os = "unix";} |
||||
if (navigator.platform == "Linux ppc64") {os = "unix";} |
||||
if (navigator.platform == "Linux mips") {os = "unix";} |
||||
if (navigator.platform == "Linux mips64") {os = "unix";} |
||||
if (navigator.platform == "Mac") {os = "unix";} |
||||
// if (navigator.platform == "Win32") {os = "win32";}
|
||||
if (navigator.platform == "Win64" || |
||||
navigator.userAgent.indexOf("WOW64") != -1 || |
||||
navigator.userAgent.indexOf("Win64") != -1) { os = "win64"; } |
||||
if (navigator.platform == "FreeBSD x86_64") {os = "unix";} |
||||
if (navigator.platform == "FreeBSD amd64") {os = "unix";} |
||||
if (navigator.platform == "NetBSD x86_64") {os = "unix";} |
||||
if (navigator.platform == "NetBSD amd64") {os = "unix";} |
||||
|
||||
// I wish I knew by now, but I don't. Try harder.
|
||||
if (os == "unknown") { |
||||
// if (navigator.appVersion.indexOf("Win")!=-1) {os = "win32";}
|
||||
if (navigator.appVersion.indexOf("Mac")!=-1) {os = "unix";} |
||||
// rust-www/#692 - FreeBSD epiphany!
|
||||
if (navigator.appVersion.indexOf("FreeBSD")!=-1) {os = "unix";} |
||||
} |
||||
|
||||
// Firefox Quantum likes to hide platform and appVersion but oscpu works
|
||||
if (navigator.oscpu) { |
||||
// if (navigator.oscpu.indexOf("Win32")!=-1) {os = "win32";}
|
||||
if (navigator.oscpu.indexOf("Win64")!=-1) {os = "win64";} |
||||
if (navigator.oscpu.indexOf("Mac")!=-1) {os = "unix";} |
||||
if (navigator.oscpu.indexOf("Linux")!=-1) {os = "unix";} |
||||
if (navigator.oscpu.indexOf("FreeBSD")!=-1) {os = "unix";} |
||||
if (navigator.oscpu.indexOf("NetBSD")!=-1) {os = "unix";} |
||||
} |
||||
|
||||
return os; |
||||
} |
||||
|
||||
function adjust_for_platform() { |
||||
"use strict"; |
||||
|
||||
var platform = detect_platform(); |
||||
|
||||
platforms.forEach(function (platform_elem) { |
||||
var platform_div = document.getElementById("platform-instructions-" + platform_elem); |
||||
platform_div.style.display = "none"; |
||||
if (platform == platform_elem || |
||||
(platform == 'unknown' && platform_elem == 'default')) { |
||||
platform_div.style.display = "block"; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
function go_to_default_platform() { |
||||
platform_override = 0; |
||||
adjust_for_platform(); |
||||
} |
||||
|
||||
function set_up_default_platform_buttons() { |
||||
var defaults_buttons = document.getElementsByClassName('default-platform-button'); |
||||
for (var i = 0; i < defaults_buttons.length; i++) { |
||||
defaults_buttons[i].onclick = go_to_default_platform; |
||||
} |
||||
} |
||||
|
||||
function fill_in_bug_report_values() { |
||||
var nav_plat = document.getElementById("nav-plat"); |
||||
var nav_app = document.getElementById("nav-app"); |
||||
nav_plat.textContent = navigator.platform; |
||||
nav_app.textContent = navigator.appVersion; |
||||
} |
||||
|
||||
(function () { |
||||
adjust_for_platform(); |
||||
set_up_default_platform_buttons(); |
||||
fill_in_bug_report_values(); |
||||
}()); |
@ -0,0 +1,122 @@ |
||||
//! Self-installation of `wasm-pack`
|
||||
//!
|
||||
//! This module contains one public function which will self-install the
|
||||
//! currently running executable as `wasm-pack`. Our goal is to install this in
|
||||
//! a place that's already in `PATH`, ideally in an idiomatic location. To that
|
||||
//! end we place `wasm-pack` next to the `rustup` executable in `PATH`.
|
||||
//!
|
||||
//! This installer is run directly (probably by clicking on it) on Windows,
|
||||
//! meaning it will pop up a console (as we're a console app). Output goes to
|
||||
//! the console and users interact with it through the console. On Unix this is
|
||||
//! intended to be run from a shell script (docs/installer/init.sh) which is
|
||||
//! downloaded via curl/sh, and then the shell script downloads this executable
|
||||
//! and runs it.
|
||||
//!
|
||||
//! This may get more complicated over time (self upates anyone?) but for now
|
||||
//! it's pretty simple! We're largely just moving over our currently running
|
||||
//! executable to a different path.
|
||||
|
||||
use std::env; |
||||
use std::fs; |
||||
use std::io; |
||||
use std::path::Path; |
||||
use std::process; |
||||
|
||||
use atty; |
||||
use failure::{Error, ResultExt}; |
||||
use which; |
||||
|
||||
pub fn install() -> ! { |
||||
if let Err(e) = do_install() { |
||||
eprintln!("{}", e); |
||||
for cause in e.iter_causes() { |
||||
eprintln!("Caused by: {}", cause); |
||||
} |
||||
} |
||||
|
||||
// On Windows we likely popped up a console for the installation. If we were
|
||||
// to exit here immediately then the user wouldn't see any error that
|
||||
// happened above or any successful message. Let's wait for them to say
|
||||
// they've read everything and then continue.
|
||||
if cfg!(windows) { |
||||
println!("Press enter to close this window..."); |
||||
let mut line = String::new(); |
||||
drop(io::stdin().read_line(&mut line)); |
||||
} |
||||
|
||||
process::exit(0); |
||||
} |
||||
|
||||
fn do_install() -> Result<(), Error> { |
||||
// Find `rustup.exe` in PATH, we'll be using its installation directory as
|
||||
// our installation directory.
|
||||
let rustup = match which::which("rustup") { |
||||
Ok(path) => path, |
||||
Err(_) => { |
||||
bail!( |
||||
"failed to find an installation of `rustup` in `PATH`, \ |
||||
is rustup already installed?" |
||||
); |
||||
} |
||||
}; |
||||
let installation_dir = match rustup.parent() { |
||||
Some(parent) => parent, |
||||
None => bail!("can't install when `rustup` is at the root of the filesystem"), |
||||
}; |
||||
let destination = installation_dir |
||||
.join("wasm-pack") |
||||
.with_extension(env::consts::EXE_EXTENSION); |
||||
|
||||
if destination.exists() { |
||||
confirm_can_overwrite(&destination)?; |
||||
} |
||||
|
||||
// Our relatively simple install step!
|
||||
let me = env::current_exe()?; |
||||
fs::copy(&me, &destination) |
||||
.with_context(|_| format!("failed to copy executable to `{}`", destination.display()))?; |
||||
println!( |
||||
"info: successfully installed wasm-pack to `{}`", |
||||
destination.display() |
||||
); |
||||
|
||||
// ... and that's it!
|
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
fn confirm_can_overwrite(dst: &Path) -> Result<(), Error> { |
||||
// If the `-f` argument was passed, we can always overwrite everything.
|
||||
if env::args().any(|arg| arg == "-f") { |
||||
return Ok(()); |
||||
} |
||||
|
||||
// If we're not attached to a TTY then we can't get user input, so there's
|
||||
// nothing to do except inform the user about the `-f` flag.
|
||||
if !atty::is(atty::Stream::Stdin) { |
||||
bail!( |
||||
"existing wasm-pack installation found at `{}`, pass `-f` to \ |
||||
force installation over this file, otherwise aborting \ |
||||
installation now", |
||||
dst.display() |
||||
); |
||||
} |
||||
|
||||
// It looks like we're at an interactive prompt, so ask the user if they'd
|
||||
// like to overwrite the previous installation.
|
||||
eprintln!( |
||||
"info: existing wasm-pack installation found at `{}`", |
||||
dst.display() |
||||
); |
||||
eprint!("info: would you like to overwrite this file? [y/N]: "); |
||||
let mut line = String::new(); |
||||
io::stdin() |
||||
.read_line(&mut line) |
||||
.with_context(|_| "failed to read stdin")?; |
||||
|
||||
if line.starts_with("y") || line.starts_with("Y") { |
||||
return Ok(()); |
||||
} |
||||
|
||||
bail!("aborting installation"); |
||||
} |
Loading…
Reference in new issue