Skip to content

Commit

Permalink
WIP on case-insensitive matching
Browse files Browse the repository at this point in the history
This ended up being a lot of work and a bit confusing, so I'm not sure it's worth pursuing. But I
want to save this work in case I change my mind in the future.
  • Loading branch information
autarch committed Dec 25, 2024
1 parent 8adc59e commit 429cb86
Show file tree
Hide file tree
Showing 31 changed files with 197 additions and 89 deletions.
3 changes: 3 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
with a new `UbiBuilder::forge` method.
- When looking for macOS assets, `ubi` will now match against `macosx` in asset names, not just
`macos` and `osx`. Implemented by @kattouf (Vasiliy Kattouf). GH #80.
- Matching an executable name in the downloaded release is now done case-insensitively. For example,
the project named `bscan/PerlNavigator` releases contain a file named `perlnavigator`. This is now
handled correctly without requiring you to pass `--exe perlnavigator`.

## 0.2.4 - 2024-11-24

Expand Down
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ Options:
--self-upgrade Use ubi to upgrade to the latest version of ubi. The --exe, --in,
--project, --tag, and --url args will be ignored.
-i, --in <in> The directory in which the binary should be placed. Defaults to ./bin.
-e, --exe <exe> The name of this project's executable. By default this is the same as
the project name, so for houseabsolute/precious we look for precious or
precious.exe. When running on Windows the ".exe" suffix will be added
as needed.
-e, --exe <exe> The name of this project's executable. By default, this is the same as
the project name, but case-insensitive. For example, with a project
named `houseabsolute/precious` it looks for `precious`, `precious.exe`,
`Precious`, `PRECIOUS.exe`, etc. When running on Windows the ".exe"
suffix will be added as needed, so you should never include this in the
value passed to `exe`.
-m, --matching <matching> A string that will be matched against the release filename when there
are multiple matching files for your OS/arch. For example, there may be
multiple releases for an OS/arch that differ by compiler (MSVC vs. gcc)
Expand Down Expand Up @@ -122,6 +124,25 @@ for anonymous API requests.
However, you can also use the `--url` option to bypass the forge site API by providing the download
link directly.

## Installed Executable Naming

If the release is in the form of a tarball or zip file, `ubi` will look in that archive file for a
file that matches the value given for the `exe` field, if any. Otherwise it looks for a file with
the same name as the project. In either case, does case-insensitive matching/

The file it matches will be installed with whatever casing it has in the archive file. So if a
project is named "SomeProject" and it releases a tarball that contains a "someproject" executable,
`ubi` will find it and install it with that name.

If the release is in the form of a bare executable or a compressed executable, then the installed
executable will use the name of the project instead.

This is a bit inconsistent, but it's how `ubi` has behaved since it was created, and I find this to
be the sanest behavior. Some projects, for example `rust-analyzer`, provide releases as compressed
executables with names like `rust-analyzer-x86_64-apple-darwin.gz` and
`rust-analyzer-x86_64-unknown-linux-musl.gz`, so installing these as `rust-analyzer` seems like
better behavior.

## Upgrading `ubi`

You can run `ubi --self-upgrade` to upgrade `ubi` using `ubi`. Note that you must have write
Expand Down
9 changes: 5 additions & 4 deletions ubi-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ fn cmd() -> Command {
.help("The directory in which the binary should be placed. Defaults to ./bin."),
)
.arg(Arg::new("exe").long("exe").short('e').help(concat!(
"The name of this project's executable. By default this is the same as the",
" project name, so for houseabsolute/precious we look for precious or",
r#" precious.exe. When running on Windows the ".exe" suffix will be added"#,
" as needed.",
"The name of this project's executable. By default, this is the same as the project",
" name, but matched case-insensitively. For example, with a project named",
" `houseabsolute/precious` it looks for `precious`, `precious.exe`, `Precious`,",
r#" `PRECIOUS.exe`, etc. When running on Windows the ".exe" suffix will be added as"#,
" needed, so you should never include this in the value passed to `exe`.",
)))
.arg(
Arg::new("matching")
Expand Down
9 changes: 9 additions & 0 deletions ubi-cli/tests/ubi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,15 @@ fn integration_tests() -> Result<()> {
make_exe_pathbuf(&["bin", "wren_cli"]),
)?;

// The executable in the release files is named "perlnavigator", but the project is
// "PerlNavigator". This tests that exe extraction is case-insensitive.
run_test(
td.path(),
ubi.as_ref(),
&["--project", "bscan/PerlNavigator", "--tag", "v0.8.15"],
make_exe_pathbuf(&["bin", "perlnavigator"]),
)?;

run_test(
td.path(),
ubi.as_ref(),
Expand Down
22 changes: 11 additions & 11 deletions ubi/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use reqwest::{
header::{HeaderMap, HeaderValue, USER_AGENT},
Client,
};
use std::{env, fs::create_dir_all, path::PathBuf, str::FromStr};
use std::{env, path::PathBuf, str::FromStr};
use url::Url;
use which::which;

Expand Down Expand Up @@ -93,9 +93,11 @@ impl<'a> UbiBuilder<'a> {
self
}

/// Set the name of the executable to look for in archive files. By default this is the same as
/// the project name, so for `houseabsolute/precious` we look for `precious` or
/// `precious.exe`. When running on Windows the ".exe" suffix will be added as needed.
/// Set the name of the executable to look for in archive files. By default, this is the same as
/// the project name, but matched case-insensitively. For example, with a project named
/// `houseabsolute/precious` it looks for `precious`, `precious.exe`, `Precious`, `PRECIOUS.exe`,
/// etc. When running on Windows the ".exe" suffix will be added as needed, so you should never
/// include this in the value passed to `exe`.
#[must_use]
pub fn exe(mut self, exe: &'a str) -> Self {
self.exe = Some(exe);
Expand Down Expand Up @@ -180,14 +182,14 @@ impl<'a> UbiBuilder<'a> {
parse_project_name(self.project, asset_url.as_ref(), self.forge.clone())?;
let exe = exe_name(self.exe, &project_name, &platform);
let forge = self.new_forge(project_name, &forge_type)?;
let install_path = install_path(self.install_dir, &exe)?;
let install_dir = install_dir(self.install_dir)?;
let is_musl = self.is_musl.unwrap_or_else(|| platform_is_musl(&platform));

Ok(Ubi::new(
forge,
asset_url,
AssetPicker::new(self.matching, platform, is_musl),
Installer::new(install_path, exe),
Installer::new(install_dir, exe),
reqwest_client()?,
))
}
Expand Down Expand Up @@ -274,17 +276,15 @@ fn parse_project_name(
))
}

fn install_path(install_dir: Option<PathBuf>, exe: &str) -> Result<PathBuf> {
let mut path = if let Some(i) = install_dir {
fn install_dir(install_dir: Option<PathBuf>) -> Result<PathBuf> {
let path = if let Some(i) = install_dir {
i
} else {
let mut i = env::current_dir()?;
i.push("bin");
i
};
create_dir_all(&path)?;
path.push(exe);
debug!("install path = {}", path.to_string_lossy());
debug!("install dir = {}", path.to_string_lossy());
Ok(path)
}

Expand Down
Loading

0 comments on commit 429cb86

Please sign in to comment.