Skip to content
New issue

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

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

Already on GitHub? # to your account

Add alias command #1115

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Here are some of the things you can do with `juliaup`:
- `juliaup add 1.6.1~x86` installs the 32 bit version of Julia 1.6.1 on your system.
- `juliaup default 1.6~x86` configures the `julia` command to start the latest 1.6.x 32 bit version of Julia you have installed on your system.
- `juliaup link dev ~/juliasrc/julia` configures the `dev` channel to use a binary that you provide that is located at `~/juliasrc/julia`. You can then use `dev` as if it was a system provided channel, i.e. make it the default or use it with the `+` version selector. You can use other names than `dev` and link as many versions into `juliaup` as you want.
- `juliaup alias r release` configures the `r` channel to act as if you had requested the `release` channel.
- `juliaup self update` installs the latest version, which is necessary if new releases reach the beta channel, etc.
- `juliaup self uninstall` uninstalls Juliaup. Note that on some platforms this command is not available, in those situations one should use platform specific methods to uninstall Juliaup.
- `juliaup override status` shows all configured directory overrides.
Expand Down
11 changes: 11 additions & 0 deletions src/bin/julialauncher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,17 @@ fn get_julia_path_from_channel(
args.as_ref().map_or_else(Vec::new, |v| v.clone()),
))
}
JuliaupConfigChannel::AliasedChannel {
channel: newchannel,
} => {
return get_julia_path_from_channel(
versions_db,
config_data,
newchannel,
juliaupconfig_path,
juliaup_channel_source,
)
}
JuliaupConfigChannel::SystemChannel { version } => {
let path = &config_data
.installed_versions.get(version)
Expand Down
2 changes: 2 additions & 0 deletions src/bin/juliaup.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::{Context, Result};
use clap::Parser;
use juliaup::cli::{ConfigSubCmd, Juliaup, OverrideSubCmd, SelfSubCmd};
use juliaup::command_alias::run_command_alias;
use juliaup::command_api::run_command_api;
use juliaup::command_completions::run_command_completions;
#[cfg(not(windows))]
Expand Down Expand Up @@ -102,6 +103,7 @@ fn main() -> Result<()> {
file,
args,
} => run_command_link(&channel, &file, &args, &paths),
Juliaup::Alias { alias, channel } => run_command_alias(&alias, &channel, &paths),
Juliaup::List {} => run_command_list(&paths),
Juliaup::Config(subcmd) => match subcmd {
#[cfg(not(windows))]
Expand Down
2 changes: 2 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum Juliaup {
file: String,
args: Vec<String>,
},
/// Link an existing juliaup channel to a custom channel name
Alias { alias: String, channel: String },
/// List all available channels
#[clap(alias = "ls")]
List {},
Expand Down
54 changes: 54 additions & 0 deletions src/command_alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::config_file::JuliaupConfigChannel;
use crate::config_file::{load_mut_config_db, save_config_db};
use crate::global_paths::GlobalPaths;
#[cfg(not(windows))]
use crate::operations::create_symlink;
use crate::operations::is_valid_channel;
use crate::versions_file::load_versions_db;
use anyhow::{bail, Context, Result};

pub fn run_command_alias(alias: &str, channel: &str, paths: &GlobalPaths) -> Result<()> {
let mut config_file = load_mut_config_db(paths)
.with_context(|| "`alias` command failed to load configuration data.")?;

let versiondb_data =
load_versions_db(paths).with_context(|| "`alias` command failed to load versions db.")?;

if config_file.data.installed_channels.contains_key(alias) {
bail!("Channel name `{}` is already used.", alias)
}

if !config_file.data.installed_channels.contains_key(channel) {
eprintln!("WARNING: The channel `{}` does not currently exist. If this was a mistake, run `juliaup remove {}` and try again.", channel, alias);
}

if is_valid_channel(&versiondb_data, &alias.to_string())? {
eprintln!("WARNING: The channel name `{}` is also a system channel. By creating an alias to this channel you are hiding this system channel.", alias);
}

config_file.data.installed_channels.insert(
alias.to_string(),
JuliaupConfigChannel::AliasedChannel {
channel: channel.to_string(),
},
);

#[cfg(not(windows))]
let create_symlinks = config_file.data.settings.create_channel_symlinks;

save_config_db(&mut config_file)
.with_context(|| "`alias` command failed to save configuration db.")?;

#[cfg(not(windows))]
if create_symlinks {
create_symlink(
&JuliaupConfigChannel::AliasedChannel {
channel: channel.to_string(),
},
&format!("julia-{}", channel),
paths,
)?;
}

Ok(())
}
196 changes: 114 additions & 82 deletions src/command_api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::config_file::load_config_db;
use crate::config_file::JuliaupConfigChannel;
use crate::config_file::JuliaupReadonlyConfigFile;
use crate::global_paths::GlobalPaths;
use crate::utils::parse_versionstring;
use anyhow::{bail, Context, Result};
Expand Down Expand Up @@ -29,6 +30,114 @@ pub struct JuliaupApiGetinfoReturn {
pub other_versions: Vec<JuliaupChannelInfo>,
}

fn get_channel_info(
name: &String,
channel: &JuliaupConfigChannel,
config_file: &JuliaupReadonlyConfigFile,
paths: &GlobalPaths,
) -> Result<Option<JuliaupChannelInfo>> {
match channel {
JuliaupConfigChannel::SystemChannel {
version: fullversion,
} => {
let (platform, mut version) = parse_versionstring(&fullversion)
.with_context(|| "Encountered invalid version string in the configuration file while running the getconfig1 API command.")?;

version.build = semver::BuildMetadata::EMPTY;

match config_file.data.installed_versions.get(&fullversion.clone()) {
Some(channel) => return Ok(Some(JuliaupChannelInfo {
name: name.clone(),
file: paths.juliauphome
.join(&channel.path)
.join("bin")
.join(format!("julia{}", std::env::consts::EXE_SUFFIX))
.normalize()
.with_context(|| "Normalizing the path for an entry from the config file failed while running the getconfig1 API command.")?
.into_path_buf()
.to_string_lossy()
.to_string(),
args: Vec::new(),
version: version.to_string(),
arch: platform
})),
None => bail!("The channel '{}' is configured as a system channel, but no such channel exists in the versions database.", name)
}
}
JuliaupConfigChannel::LinkedChannel { command, args } => {
let mut new_args: Vec<String> = Vec::new();

for i in args.as_ref().unwrap() {
new_args.push(i.to_string());
}

new_args.push("--version".to_string());

let res = std::process::Command::new(&command)
.args(&new_args)
.output();

match res {
Ok(output) => {
let expected_version_prefix = "julia version ";

let trimmed_string = std::str::from_utf8(&output.stdout).unwrap().trim();

if !trimmed_string.starts_with(expected_version_prefix) {
return Ok(None);
}

let version =
Version::parse(&trimmed_string[expected_version_prefix.len()..])?;

Ok(Some(JuliaupChannelInfo {
name: name.clone(),
file: command.clone(),
args: args.clone().unwrap_or_default(),
version: version.to_string(),
arch: "".to_string(),
}))
}
Err(_) => return Ok(None),
}
}
// TODO: fix
JuliaupConfigChannel::AliasedChannel { channel } => {
let real_channel_info = get_channel_info(name, config_file.data.installed_channels.get(channel).unwrap(), config_file, paths)?;

match real_channel_info {
Some(info) => {
return Ok(Some(JuliaupChannelInfo {
name: name.clone(),
file: info.file,
args: info.args,
version: info.version,
arch: info.arch,
}))
}
None => return Ok(None),
}
}
JuliaupConfigChannel::DirectDownloadChannel { path, url: _, local_etag: _, server_etag: _, version } => {
return Ok(Some(JuliaupChannelInfo {
name: name.clone(),
file: paths.juliauphome
.join(path)
.join("bin")
.join(format!("julia{}", std::env::consts::EXE_SUFFIX))
.normalize()
.with_context(|| "Normalizing the path for an entry from the config file failed while running the getconfig1 API command.")?
.into_path_buf()
.to_string_lossy()
.to_string(),
args: Vec::new(),
version: version.clone(),
arch: "".to_string(),
}))
}
}
}

pub fn run_command_api(command: &str, paths: &GlobalPaths) -> Result<()> {
if command != "getconfig1" {
bail!("Wrong API command.");
Expand All @@ -43,89 +152,12 @@ pub fn run_command_api(command: &str, paths: &GlobalPaths) -> Result<()> {
"Failed to load configuration file while running the getconfig1 API command."
})?;

for (key, value) in config_file.data.installed_channels {
let curr = match value {
JuliaupConfigChannel::SystemChannel {
version: fullversion,
} => {
let (platform, mut version) = parse_versionstring(&fullversion)
.with_context(|| "Encountered invalid version string in the configuration file while running the getconfig1 API command.")?;

version.build = semver::BuildMetadata::EMPTY;

match config_file.data.installed_versions.get(&fullversion) {
Some(channel) => JuliaupChannelInfo {
name: key.clone(),
file: paths.juliauphome
.join(&channel.path)
.join("bin")
.join(format!("julia{}", std::env::consts::EXE_SUFFIX))
.normalize()
.with_context(|| "Normalizing the path for an entry from the config file failed while running the getconfig1 API command.")?
.into_path_buf()
.to_string_lossy()
.to_string(),
args: Vec::new(),
version: version.to_string(),
arch: platform
},
None => bail!("The channel '{}' is configured as a system channel, but no such channel exists in the versions database.", key)
}
}
JuliaupConfigChannel::LinkedChannel { command, args } => {
let mut new_args: Vec<String> = Vec::new();

for i in args.as_ref().unwrap() {
new_args.push(i.to_string());
}
let other_conf = config_file.clone();

new_args.push("--version".to_string());

let res = std::process::Command::new(&command)
.args(&new_args)
.output();

match res {
Ok(output) => {
let expected_version_prefix = "julia version ";

let trimmed_string = std::str::from_utf8(&output.stdout).unwrap().trim();

if !trimmed_string.starts_with(expected_version_prefix) {
continue;
}

let version =
Version::parse(&trimmed_string[expected_version_prefix.len()..])?;

JuliaupChannelInfo {
name: key.clone(),
file: command.clone(),
args: args.unwrap_or_default(),
version: version.to_string(),
arch: "".to_string(),
}
}
Err(_) => continue,
}
}
JuliaupConfigChannel::DirectDownloadChannel { path, url: _, local_etag: _, server_etag: _, version } => {
JuliaupChannelInfo {
name: key.clone(),
file: paths.juliauphome
.join(path)
.join("bin")
.join(format!("julia{}", std::env::consts::EXE_SUFFIX))
.normalize()
.with_context(|| "Normalizing the path for an entry from the config file failed while running the getconfig1 API command.")?
.into_path_buf()
.to_string_lossy()
.to_string(),
args: Vec::new(),
version: version.clone(),
arch: "".to_string(),
}
}
for (key, value) in config_file.data.installed_channels {
let curr = match get_channel_info(&key, &value, &other_conf, paths)? {
Some(channel_info) => channel_info,
None => continue,
};

match config_file.data.default {
Expand Down
4 changes: 4 additions & 0 deletions src/command_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ pub fn run_command_status(paths: &GlobalPaths) -> Result<()> {
}
format!("Linked to `{}`", combined_command)
}
JuliaupConfigChannel::AliasedChannel { channel } => {
format!("Aliased to channel `{}`", channel)
}
},
update: match i.1 {
JuliaupConfigChannel::SystemChannel { version } => {
Expand All @@ -104,6 +107,7 @@ pub fn run_command_status(paths: &GlobalPaths) -> Result<()> {
command: _,
args: _,
} => "".to_string(),
JuliaupConfigChannel::AliasedChannel { channel: _ } => "".to_string(),
JuliaupConfigChannel::DirectDownloadChannel {
path: _,
url: _,
Expand Down
11 changes: 11 additions & 0 deletions src/command_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ fn update_channel(
);
}
}
JuliaupConfigChannel::AliasedChannel {
channel: realchannel,
} => {
return update_channel(
config_db,
realchannel,
version_db,
ignore_non_updatable_channel,
paths,
)
}
JuliaupConfigChannel::DirectDownloadChannel {
path,
url,
Expand Down
5 changes: 5 additions & 0 deletions src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ pub enum JuliaupConfigChannel {
#[serde(rename = "Args")]
args: Option<Vec<String>>,
},
AliasedChannel {
#[serde(rename = "Channel")]
channel: String,
},
}

#[derive(Serialize, Deserialize, Clone, PartialEq)]
Expand Down Expand Up @@ -136,6 +140,7 @@ pub struct JuliaupConfigFile {
pub self_data: JuliaupSelfConfig,
}

#[derive(Clone)]
pub struct JuliaupReadonlyConfigFile {
pub data: JuliaupConfig,
#[cfg(feature = "selfupdate")]
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::Context;

pub mod cli;
pub mod command_add;
pub mod command_alias;
pub mod command_api;
pub mod command_completions;
pub mod command_config_backgroundselfupdate;
Expand Down
Loading