Add workflow and improve error handling

feature/lookup-installed
trivernis 1 year ago
parent ca8a4a3245
commit 14ba587c2c
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -0,0 +1,52 @@
name: "Build and Release"
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
build-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Set up MinGW
uses: egor-tensin/setup-mingw@v1
with:
platform: x64
- name: Cache cargo builds
uses: actions/cache@v2
with:
path: |
target
~/.cargo/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build Release
uses: actions-rs/cargo@v1
with:
use-cross: false
command: build
args: --release --all-features -Zmultitarget --target x86_64-unknown-linux-gnu --target x86_64-pc-windows-gnu
- name: Move binaries
run: mv target/x86_64-unknown-linux-gnu/release/nenv target/nenv-linux-x86_64 && mv target/x86_64-pc-windows-gnu/release/nenv.exe target/nenv-windows-x86_64.exe
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: nenv
path: target/nenv*
- name: publish release
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
LICENSE
target/nenv*

@ -1,6 +1,7 @@
use std::io;
use miette::Diagnostic;
use semver::VersionReq;
use thiserror::Error;
use crate::{
@ -15,14 +16,15 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error, Diagnostic)]
pub enum Error {
#[error("Failed to call nodejs.com api: {0}")]
#[error("Failed to call nodejs.com api.")]
Web(
#[from]
#[source]
#[diagnostic_source]
ApiError,
),
#[error("Failed to extract archive: {0}")]
#[error("The node archive could not be extracted")]
Extract(
#[from]
#[source]
@ -30,23 +32,47 @@ pub enum Error {
ExtractError,
),
#[error("Failed to load config file: {0}")]
#[error("The config file could not be loaded")]
Config(
#[from]
#[source]
#[diagnostic_source]
ConfigError,
),
#[error("Mapper failed: {0}")]
#[error("Mapping failed")]
Mapper(
#[from]
#[source]
#[diagnostic_source]
MapperError,
),
#[error("Failed to work with json: {0}")]
#[error("The passed is invalid")]
Version(
#[from]
#[diagnostic_source]
VersionError,
),
#[error("Failed to work with json")]
Json(#[from] serde_json::Error),
#[error("IO Error: {0}")]
#[error("Error during IO operation")]
Io(#[from] io::Error),
}
#[derive(Debug, Error, Diagnostic)]
pub enum VersionError {
#[error("Invalid version string `{0}`")]
ParseVersion(#[source_code] String),
#[error("Unknown Version `{0}`")]
UnkownVersion(#[source_code] String),
#[error("The version `{0}` is not installed")]
NotInstalled(#[source_code] String),
#[error("The version requirement `{0}` cannot be fulfilled")]
Unfulfillable(VersionReq),
}

@ -16,10 +16,12 @@ use error::Result;
use tokio::fs;
pub async fn install_version(version: NodeVersion) -> Result<()> {
fs::remove_file(&*VERSION_FILE_PATH).await?;
if VERSION_FILE_PATH.exists() {
fs::remove_file(&*VERSION_FILE_PATH).await?;
}
let repo = get_repository().await?;
if repo.is_installed(&version) {
if repo.is_installed(&version)? {
if !Confirm::new()
.with_prompt("The version {version} is already installed. Reinstall?")
.default(false)
@ -38,7 +40,7 @@ pub async fn install_version(version: NodeVersion) -> Result<()> {
pub async fn set_default_version(version: NodeVersion) -> Result<()> {
let mut mapper = get_mapper().await?;
if !mapper.repository().is_installed(&version)
if !mapper.repository().is_installed(&version)?
&& Confirm::new()
.with_prompt(format!(
"The version {version} is not installed. Do you want to install it?"
@ -61,7 +63,7 @@ pub async fn exec(command: String, args: Vec<OsString>) -> Result<i32> {
let mapper = get_mapper().await?;
let active_version = mapper.active_version();
if !mapper.repository().is_installed(active_version) {
if !mapper.repository().is_installed(active_version)? {
mapper.repository().install_version(&active_version).await?;
}
let exit_status = mapper.exec(command, args).await?;

@ -6,7 +6,7 @@ use clap::Parser;
mod args;
#[tokio::main(flavor = "current_thread")]
async fn main() -> nenv::error::Result<()> {
async fn main() -> color_eyre::eyre::Result<()> {
color_eyre::install().unwrap();
let args: Args = Args::parse();
@ -20,7 +20,9 @@ async fn main() -> nenv::error::Result<()> {
process::exit(exit_code);
}
args::Command::Refresh => nenv::refresh().await,
}
}?;
Ok(())
}
fn print_version() {

@ -1,7 +1,5 @@
use std::{
ffi::OsString,
io::{stderr, stdin, stdout},
os::fd::{AsRawFd, FromRawFd},
path::PathBuf,
process::{ExitStatus, Stdio},
};
@ -35,18 +33,11 @@ impl MappedCommand {
if !self.path.exists() {
return Err(CommandError::NotFound(self.path));
}
let (stdin, stdout, stderr) = unsafe {
(
Stdio::from_raw_fd(stdin().as_raw_fd()),
Stdio::from_raw_fd(stdout().as_raw_fd()),
Stdio::from_raw_fd(stderr().as_raw_fd()),
)
};
let exit_status = Command::new(self.path)
.args(self.args)
.stdin(stdin)
.stdout(stdout)
.stderr(stderr)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
.wait()
.await?;

@ -4,7 +4,7 @@ use tokio::fs;
use crate::{
consts::BIN_DIR,
error::LibResult,
error::{LibResult, VersionError},
repository::{NodeVersion, Repository},
};
@ -60,7 +60,7 @@ impl Mapper {
pub async fn exec(&self, command: String, args: Vec<OsString>) -> LibResult<ExitStatus> {
let node_path = self
.repo
.get_version_path(&self.active_version)
.get_version_path(&self.active_version)?
.expect("version not installed");
let executable = node_path.bin().join(command);
let exit_status = MappedCommand::new(executable, args)
@ -101,8 +101,8 @@ impl Mapper {
async fn map_active_version(&self) -> LibResult<()> {
let dir = self
.repo
.get_version_path(&self.active_version)
.expect("missing version");
.get_version_path(&self.active_version)?
.ok_or_else(|| VersionError::NotInstalled(self.active_version.to_string()))?;
map_node_bin(dir).await?;
Ok(())

@ -15,7 +15,7 @@ use crate::{
consts::{
ARCH, BIN_DIR, CACHE_DIR, CFG_DIR, DATA_DIR, NODE_ARCHIVE_SUFFIX, NODE_VERSIONS_DIR, OS,
},
error::LibResult,
error::{LibResult, VersionError},
web_api::{VersionInfo, WebApi},
};
@ -117,15 +117,15 @@ impl Repository {
}
/// Returns the path for the given node version
pub fn get_version_path(&self, version: &NodeVersion) -> Option<NodePath> {
let info = self.parse_req(&version);
pub fn get_version_path(&self, version: &NodeVersion) -> LibResult<Option<NodePath>> {
let info = self.parse_req(&version)?;
let path = build_version_path(&info.version);
if path.exists() {
Ok(if path.exists() {
Some(NodePath::new(path))
} else {
None
}
})
}
/// Returns a list of installed versions
@ -143,15 +143,15 @@ impl Repository {
}
/// Returns if the given version is installed
pub fn is_installed(&self, version: &NodeVersion) -> bool {
let info = self.parse_req(version);
pub fn is_installed(&self, version: &NodeVersion) -> LibResult<bool> {
let info = self.parse_req(version)?;
build_version_path(&info.version).exists()
Ok(build_version_path(&info.version).exists())
}
/// Installs a specified node version
pub async fn install_version(&self, version_req: &NodeVersion) -> LibResult<()> {
let info = self.parse_req(&version_req);
let info = self.parse_req(&version_req)?;
let archive_path = self.download_version(&info.version).await?;
self.extract_archive(info, &archive_path)?;
@ -179,16 +179,21 @@ impl Repository {
Ok(())
}
fn parse_req(&self, version_req: &NodeVersion) -> &VersionInfo {
match version_req {
fn parse_req(&self, version_req: &NodeVersion) -> Result<&VersionInfo, VersionError> {
let version = match version_req {
NodeVersion::Latest => self.versions.latest(),
NodeVersion::LatestLts => self.versions.latest_lts(),
NodeVersion::Lts(lts) => self.versions.get_lts(&lts).expect("Version not found"),
NodeVersion::Lts(lts) => self
.versions
.get_lts(&lts)
.ok_or_else(|| VersionError::UnkownVersion(lts.to_owned()))?,
NodeVersion::Req(req) => self
.versions
.get_fulfilling(&req)
.expect("Version not found"),
}
.ok_or_else(|| VersionError::Unfulfillable(req.to_owned()))?,
};
Ok(version)
}
}

Loading…
Cancel
Save